Done-condition: the integration registers the panel dashboard on first setup, so a fresh HA install with children configured shows the panel with no YAML and no manual dashboard creation. A test asserts the dashboard is created on setup and is NOT duplicated on a reload.
What this does
custom_components/nestquest/dashboard.py (new): async_register_dashboard(hass) registers ONE storage-mode Lovelace dashboard on setup — config {url_path: "nestquest", title: "NestQuest", icon: mdi:party-popper, show_in_sidebar: true, mode: "storage", strategy: {type: "custom:nestquest-party"}} — the same surface HA's dashboards collection exposes (async_items() / async_create_item()).
Idempotent: the stable url_path is the idempotency key. A reload or a second entry never duplicates the dashboard, and a dashboard already occupying the url_path (ours or a user's) is left untouched.
Graceful degradation: a missing Lovelace dashboards collection (YAML mode / lovelace not loaded) or a refused create logs a clear warning and setup continues — never a crash. The user's dashboard is never overwritten or deleted.
Constants (DASHBOARD_URL_PATH, DASHBOARD_TITLE, DASHBOARD_ICON, DASHBOARD_STRATEGY_TYPE) live in core/const.py per the no-hardcoded-domain rule; the bare domain string is referenced as DOMAIN.
Wired into async_setup_entry after the frontend bundle registration.
Tests (tests/test_lifecycle.py, +5)
dashboard created on setup with the expected url_path / mode / strategy / title / icon / sidebar visibility
reload and second entry do NOT create a second dashboard
an existing user dashboard under the url_path is left alone (no create, no mutation)
missing collection: warning logged, setup still succeeds
refusing collection (PermissionError): setup still succeeds
Checks
TZ=UTC uv run pytest -q → 1696 passed (baseline 1691 + 5 new; note: frontend/node_modules had to be installed for the Node render-harness tests, gitignored)
TZ=UTC uv run pytest -q tests/test_lifecycle.py → 26 passed
Closes the done-condition for task c2d4dd8e-96f1-4f67-ab61-4ff370517dee.
## Auto-register the dashboard on setup
Maestro task: `c2d4dd8e-96f1-4f67-ab61-4ff370517dee` (Feature 20 — Zero-Config Panel Dashboard)
**Done-condition:** the integration registers the panel dashboard on first setup, so a fresh HA install with children configured shows the panel with no YAML and no manual dashboard creation. A test asserts the dashboard is created on setup and is NOT duplicated on a reload.
### What this does
- `custom_components/nestquest/dashboard.py` (new): `async_register_dashboard(hass)` registers ONE storage-mode Lovelace dashboard on setup — config `{url_path: "nestquest", title: "NestQuest", icon: mdi:party-popper, show_in_sidebar: true, mode: "storage", strategy: {type: "custom:nestquest-party"}}` — the same surface HA's dashboards collection exposes (`async_items()` / `async_create_item()`).
- Idempotent: the stable `url_path` is the idempotency key. A reload or a second entry never duplicates the dashboard, and a dashboard already occupying the url_path (ours or a user's) is left untouched.
- Graceful degradation: a missing Lovelace dashboards collection (YAML mode / lovelace not loaded) or a refused create logs a clear warning and setup continues — never a crash. The user's dashboard is never overwritten or deleted.
- Constants (`DASHBOARD_URL_PATH`, `DASHBOARD_TITLE`, `DASHBOARD_ICON`, `DASHBOARD_STRATEGY_TYPE`) live in `core/const.py` per the no-hardcoded-domain rule; the bare domain string is referenced as `DOMAIN`.
- Wired into `async_setup_entry` after the frontend bundle registration.
### Tests (tests/test_lifecycle.py, +5)
- dashboard created on setup with the expected url_path / mode / strategy / title / icon / sidebar visibility
- reload and second entry do NOT create a second dashboard
- an existing user dashboard under the url_path is left alone (no create, no mutation)
- missing collection: warning logged, setup still succeeds
- refusing collection (PermissionError): setup still succeeds
### Checks
- `TZ=UTC uv run pytest -q` → `1696 passed` (baseline 1691 + 5 new; note: `frontend/node_modules` had to be installed for the Node render-harness tests, gitignored)
- `TZ=UTC uv run pytest -q tests/test_lifecycle.py` → `26 passed`
Closes the done-condition for task c2d4dd8e-96f1-4f67-ab61-4ff370517dee.
A fresh HA install with children configured should show the panel
with no YAML and no manual dashboard creation: setup now registers
ONE storage-mode Lovelace dashboard whose config is the bundle's
custom:nestquest-party strategy (frontend/src/strategy.ts), under the
stable url_path 'nestquest' with a title, icon and sidebar visibility.
Registration is idempotent — the url_path is the idempotency key, so
a reload or a second entry never duplicates the dashboard, and a
dashboard already occupying the url_path (ours or the user's) is left
completely alone (never overwritten, never deleted). An unavailable
Lovelace dashboards collection (YAML mode, lovelace not loaded) or a
refused create logs a clear warning and setup continues — never a
crash. Tests cover create-on-setup, no-duplicate-on-reload, the
untouched user dashboard, the missing collection and a refusing one.
P1: The registration was a silent no-op in production — it read
hass.data["lovelace"].dashboards as the DashboardsCollection and
awaited a sync async_items(), and passed "strategy"/"mode" keys the
real create schema rejects. Per the real lovelace source
(components/lovelace/__init__.py, _create_map_dashboard): the metadata
collection lives at hass.data["lovelace"]["dashboards_collection"],
async_items() is a SYNC @callback, the create payload carries metadata
only (url_path/title/icon/show_in_sidebar/allow_single_word), and the
dashboard CONFIG (the strategy reference) is saved as a SECOND step
through the content manager registered under
hass.data["lovelace"]["dashboards"][url_path]. The strategy is saved
only for a dashboard created in this run (or whose config already
carries ours); an existing dashboard skips both steps, so a user's
dashboard config is never overwritten. Collection unavailable / save
raising still warns and continues, never crashing setup.
P2: Declare "lovelace" in manifest.json dependencies (alongside
frontend/http) so hass.data["lovelace"] is populated before setup
instead of relying on boot order.
P3: Guard the check-then-create with a per-hass asyncio.Lock so two
concurrent entry setups cannot both pass the exists-check and race on
the url_path create. Per-hass (attribute on the hass object) rather
than a bare module-level lock, which binds to the first event loop
that awaits it.
The lifecycle tests' fake is rebuilt to mirror the real API shape
(sync async_items, async async_create_item, a dashboards content-
manager dict whose entries expose async async_save), with assertions
that the strategy config was saved with the custom:nestquest-party
type, that a user's existing config is not overwritten, that a failing
save still leaves setup green, and that concurrent registrations
create the dashboard exactly once (verified to fail without the
guard).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Auto-register the dashboard on setup
Maestro task:
c2d4dd8e-96f1-4f67-ab61-4ff370517dee(Feature 20 — Zero-Config Panel Dashboard)Done-condition: the integration registers the panel dashboard on first setup, so a fresh HA install with children configured shows the panel with no YAML and no manual dashboard creation. A test asserts the dashboard is created on setup and is NOT duplicated on a reload.
What this does
custom_components/nestquest/dashboard.py(new):async_register_dashboard(hass)registers ONE storage-mode Lovelace dashboard on setup — config{url_path: "nestquest", title: "NestQuest", icon: mdi:party-popper, show_in_sidebar: true, mode: "storage", strategy: {type: "custom:nestquest-party"}}— the same surface HA's dashboards collection exposes (async_items()/async_create_item()).url_pathis the idempotency key. A reload or a second entry never duplicates the dashboard, and a dashboard already occupying the url_path (ours or a user's) is left untouched.DASHBOARD_URL_PATH,DASHBOARD_TITLE,DASHBOARD_ICON,DASHBOARD_STRATEGY_TYPE) live incore/const.pyper the no-hardcoded-domain rule; the bare domain string is referenced asDOMAIN.async_setup_entryafter the frontend bundle registration.Tests (tests/test_lifecycle.py, +5)
Checks
TZ=UTC uv run pytest -q→1696 passed(baseline 1691 + 5 new; note:frontend/node_moduleshad to be installed for the Node render-harness tests, gitignored)TZ=UTC uv run pytest -q tests/test_lifecycle.py→26 passedCloses the done-condition for task c2d4dd8e-96f1-4f67-ab61-4ff370517dee.
Independent Claude Sonnet 5 review completed for this exact commit.