Files
NestQuest/custom_components/nestquest/dashboard.py
T
overseer f668cf95b0 Auto-register the zero-config panel dashboard on setup
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.
2026-09-23 10:17:20 -04:00

114 lines
4.1 KiB
Python

"""Zero-config panel dashboard registration for NestQuest (Feature 20).
On setup the integration registers ONE storage-mode Lovelace dashboard
whose config uses the bundle's ``custom:nestquest-party`` strategy (see
``frontend/src/strategy.ts``): the views themselves are GENERATED at
render time from the household roster, so the stored dashboard carries
nothing but the strategy reference. Registration is IDEMPOTENT — a
dashboard with the stable ``url_path`` (ours or a user's) is never
touched, and the Lovelace dashboards collection being unavailable
(YAML mode, lovelace not loaded) is a logged warning, never a setup
failure.
"""
from __future__ import annotations
from homeassistant.core import HomeAssistant
from .const import (
DASHBOARD_ICON,
DASHBOARD_STRATEGY_TYPE,
DASHBOARD_TITLE,
DASHBOARD_URL_PATH,
LOGGER,
)
_LOGGER = LOGGER
def _dashboards_collection(hass: HomeAssistant):
"""Return the Lovelace dashboards collection, or None when absent.
The lovelace integration stashes its collections on
``hass.data["lovelace"]``; a missing key (lovelace not loaded) and a
collection-less LovelaceData both mean "cannot create storage
dashboards here" — callers log and continue. The presence of the
surface is verified by capability (``async_create_item`` callable),
not by type, so no lovelace import is needed.
"""
lovelace_data = hass.data.get("lovelace")
collection = getattr(lovelace_data, "dashboards", None)
if collection is None or not callable(
getattr(collection, "async_create_item", None)
):
return None
return collection
def _item_url_path(item) -> str | None:
"""Read a dashboard item's ``url_path`` through either access shape.
The collection hands back storage items as dicts while builtin
dashboards come back as config objects; the url_path is reachable
through ``item["url_path"]`` or ``item.url_path`` respectively.
"""
url_path = getattr(item, "url_path", None)
if isinstance(url_path, str):
return url_path
get_url_path = getattr(item, "get", None)
if callable(get_url_path):
value = get_url_path("url_path")
if isinstance(value, str):
return value
return None
def _existing_dashboard(items) -> bool:
"""Whether a dashboard under the stable url_path already exists."""
return any(_item_url_path(item) == DASHBOARD_URL_PATH for item in items)
async def async_register_dashboard(hass: HomeAssistant) -> bool:
"""Register the NestQuest panel dashboard exactly once.
Returns True when a dashboard under the stable ``url_path`` exists
after the call (pre-existing or created here), False when the
Lovelace dashboards collection was unavailable or refused. A
dashboard already registered under the stable ``url_path`` — whether
ours from a previous setup or a user's own — is left COMPLETELY
alone: never overwritten, never deleted, never a duplicate.
"""
collection = _dashboards_collection(hass)
if collection is None:
_LOGGER.warning(
"Lovelace dashboards collection unavailable; the NestQuest "
"panel dashboard was not registered. Enable the Lovelace "
"integration in storage mode or create a dashboard with "
"strategy %s manually.",
DASHBOARD_STRATEGY_TYPE,
)
return False
try:
items = await collection.async_items()
if _existing_dashboard(items):
return True
await collection.async_create_item(
{
"url_path": DASHBOARD_URL_PATH,
"title": DASHBOARD_TITLE,
"icon": DASHBOARD_ICON,
"show_in_sidebar": True,
"mode": "storage",
"strategy": {"type": DASHBOARD_STRATEGY_TYPE},
}
)
except Exception:
_LOGGER.warning(
"Registering the NestQuest panel dashboard in the Lovelace "
"dashboards collection failed; set it up manually with "
"strategy %s.",
DASHBOARD_STRATEGY_TYPE,
exc_info=True,
)
return False
return True