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.
114 lines
4.1 KiB
Python
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
|