diff --git a/integration/custom_components/luma_home/__init__.py b/integration/custom_components/luma_home/__init__.py index c03e921..d627c4c 100644 --- a/integration/custom_components/luma_home/__init__.py +++ b/integration/custom_components/luma_home/__init__.py @@ -1,10 +1,12 @@ """Luma Home — config bridge between HA dashboards and Luma TV app.""" +import logging from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from .const import DOMAIN -from . import websocket_api + +_LOGGER = logging.getLogger(__name__) async def async_setup(hass: HomeAssistant, config: dict) -> bool: @@ -19,7 +21,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN][entry.entry_id] = { "dashboard_url_path": entry.data.get("dashboard_url_path", "luma-tv"), } - websocket_api.async_register_commands(hass) + try: + from . import websocket_api + websocket_api.async_register_commands(hass) + _LOGGER.info("Luma Home WS commands registered successfully") + except Exception: + _LOGGER.exception("Failed to register Luma Home WS commands") return True diff --git a/integration/custom_components/luma_home/websocket_api.py b/integration/custom_components/luma_home/websocket_api.py index 850a42b..04c8742 100644 --- a/integration/custom_components/luma_home/websocket_api.py +++ b/integration/custom_components/luma_home/websocket_api.py @@ -9,15 +9,12 @@ from __future__ import annotations import json import logging -import os -import secrets from pathlib import Path from typing import Any import voluptuous as vol from homeassistant.components import websocket_api -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import config_validation as cv +from homeassistant.core import HomeAssistant from .const import DOMAIN, DEFAULT_DASHBOARD_URL_PATH, PAIRING_DIR @@ -195,18 +192,11 @@ async def handle_get_config( """Return the Luma v2 config derived from a Lovelace dashboard.""" url_path = msg.get("dashboard_url_path") or _get_dashboard_path(hass) - try: - config = await hass.data["lovelace"]["dashboards"][url_path].async_load(False) - except KeyError: - # Try the default dashboard - try: - config = await hass.data["lovelace"]["dashboards"][None].async_load(False) - _LOGGER.warning( - "Dashboard '%s' not found, falling back to default", url_path - ) - except (KeyError, Exception) as err: - connection.send_error(msg["id"], "not_found", f"Dashboard not found: {err}") - return + config = await _load_lovelace_config(hass, url_path) + + if config is None: + connection.send_error(msg["id"], "not_found", f"Dashboard '{url_path}' not found") + return views = config.get("views", []) rooms = [_map_view(v) for v in views] @@ -219,6 +209,28 @@ async def handle_get_config( connection.send_result(msg["id"], result) +async def _load_lovelace_config(hass: HomeAssistant, url_path: str) -> dict | None: + """Load a Lovelace dashboard config by reading the .storage file.""" + storage_key = url_path.replace("-", "_") + storage_path = Path(hass.config.path(f".storage/lovelace.{storage_key}")) + _LOGGER.debug("Looking for dashboard at %s", storage_path) + + try: + if not await hass.async_add_executor_job(storage_path.exists): + _LOGGER.warning("Dashboard storage file not found: %s", storage_path) + return None + raw = await hass.async_add_executor_job(storage_path.read_text, "utf-8") + stored = json.loads(raw) + config = stored.get("data", {}).get("config") + if config is None: + config = stored.get("data", {}) + _LOGGER.debug("Loaded dashboard with %d views", len(config.get("views", []))) + return config + except Exception as err: + _LOGGER.error("Failed to load dashboard '%s': %s", url_path, err) + return None + + @websocket_api.websocket_command( { vol.Required("type"): "luma/pair",