Use HassKey in lovelace (#136313)

* Use HassKey in lovelace

* Improve type hints

* docstring

* Rename constant
This commit is contained in:
epenet 2025-01-23 11:26:18 +01:00 committed by GitHub
parent ae65a81188
commit 73bd21e0ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 57 additions and 31 deletions

View File

@ -1,5 +1,6 @@
"""Support for the Lovelace UI.""" """Support for the Lovelace UI."""
from dataclasses import dataclass
import logging import logging
import voluptuous as vol import voluptuous as vol
@ -29,6 +30,7 @@ from .const import ( # noqa: F401
DEFAULT_ICON, DEFAULT_ICON,
DOMAIN, DOMAIN,
EVENT_LOVELACE_UPDATED, EVENT_LOVELACE_UPDATED,
LOVELACE_DATA,
MODE_STORAGE, MODE_STORAGE,
MODE_YAML, MODE_YAML,
RESOURCE_CREATE_FIELDS, RESOURCE_CREATE_FIELDS,
@ -73,6 +75,16 @@ CONFIG_SCHEMA = vol.Schema(
) )
@dataclass
class LovelaceData:
"""Dataclass to store information in hass.data."""
mode: str
dashboards: dict[str | None, dashboard.LovelaceConfig]
resources: resources.ResourceStorageCollection
yaml_dashboards: dict[str | None, ConfigType]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Lovelace commands.""" """Set up the Lovelace commands."""
mode = config[DOMAIN][CONF_MODE] mode = config[DOMAIN][CONF_MODE]
@ -100,7 +112,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
resource_collection = await create_yaml_resource_col( resource_collection = await create_yaml_resource_col(
hass, config[DOMAIN].get(CONF_RESOURCES) hass, config[DOMAIN].get(CONF_RESOURCES)
) )
hass.data[DOMAIN]["resources"] = resource_collection hass.data[LOVELACE_DATA].resources = resource_collection
default_config: dashboard.LovelaceConfig default_config: dashboard.LovelaceConfig
if mode == MODE_YAML: if mode == MODE_YAML:
@ -151,13 +163,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass, websocket.websocket_lovelace_delete_config hass, websocket.websocket_lovelace_delete_config
) )
hass.data[DOMAIN] = { hass.data[LOVELACE_DATA] = LovelaceData(
mode=mode,
# We store a dictionary mapping url_path: config. None is the default. # We store a dictionary mapping url_path: config. None is the default.
"mode": mode, dashboards={None: default_config},
"dashboards": {None: default_config}, resources=resource_collection,
"resources": resource_collection, yaml_dashboards=config[DOMAIN].get(CONF_DASHBOARDS, {}),
"yaml_dashboards": config[DOMAIN].get(CONF_DASHBOARDS, {}), )
}
if hass.config.recovery_mode: if hass.config.recovery_mode:
return True return True
@ -168,11 +180,11 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
if change_type == collection.CHANGE_REMOVED: if change_type == collection.CHANGE_REMOVED:
frontend.async_remove_panel(hass, url_path) frontend.async_remove_panel(hass, url_path)
await hass.data[DOMAIN]["dashboards"].pop(url_path).async_delete() await hass.data[LOVELACE_DATA].dashboards.pop(url_path).async_delete()
return return
if change_type == collection.CHANGE_ADDED: if change_type == collection.CHANGE_ADDED:
existing = hass.data[DOMAIN]["dashboards"].get(url_path) existing = hass.data[LOVELACE_DATA].dashboards.get(url_path)
if existing: if existing:
_LOGGER.warning( _LOGGER.warning(
@ -182,13 +194,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
) )
return return
hass.data[DOMAIN]["dashboards"][url_path] = dashboard.LovelaceStorage( hass.data[LOVELACE_DATA].dashboards[url_path] = dashboard.LovelaceStorage(
hass, item hass, item
) )
update = False update = False
else: else:
hass.data[DOMAIN]["dashboards"][url_path].config = item hass.data[LOVELACE_DATA].dashboards[url_path].config = item
update = True update = True
try: try:
@ -197,10 +209,10 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
_LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path) _LOGGER.warning("Failed to %s panel %s from storage", change_type, url_path)
# Process YAML dashboards # Process YAML dashboards
for url_path, dashboard_conf in hass.data[DOMAIN]["yaml_dashboards"].items(): for url_path, dashboard_conf in hass.data[LOVELACE_DATA].yaml_dashboards.items():
# For now always mode=yaml # For now always mode=yaml
lovelace_config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf) lovelace_config = dashboard.LovelaceYAML(hass, url_path, dashboard_conf)
hass.data[DOMAIN]["dashboards"][url_path] = lovelace_config hass.data[LOVELACE_DATA].dashboards[url_path] = lovelace_config
try: try:
_register_panel(hass, url_path, MODE_YAML, dashboard_conf, False) _register_panel(hass, url_path, MODE_YAML, dashboard_conf, False)

View File

@ -2,6 +2,8 @@
from __future__ import annotations from __future__ import annotations
from typing import Any
from pychromecast import Chromecast from pychromecast import Chromecast
from pychromecast.const import CAST_TYPE_CHROMECAST from pychromecast.const import CAST_TYPE_CHROMECAST
@ -23,8 +25,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.network import NoURLAvailableError, get_url
from .const import DOMAIN, ConfigNotFound from .const import DOMAIN, LOVELACE_DATA, ConfigNotFound
from .dashboard import LovelaceConfig
DEFAULT_DASHBOARD = "_default_" DEFAULT_DASHBOARD = "_default_"
@ -76,7 +77,7 @@ async def async_browse_media(
can_expand=False, can_expand=False,
) )
] ]
for url_path in hass.data[DOMAIN]["dashboards"]: for url_path in hass.data[LOVELACE_DATA].dashboards:
if url_path is None: if url_path is None:
continue continue
@ -151,11 +152,13 @@ async def async_play_media(
return True return True
async def _get_dashboard_info(hass, url_path): async def _get_dashboard_info(
hass: HomeAssistant, url_path: str | None
) -> dict[str, Any]:
"""Load a dashboard and return info on views.""" """Load a dashboard and return info on views."""
if url_path == DEFAULT_DASHBOARD: if url_path == DEFAULT_DASHBOARD:
url_path = None url_path = None
dashboard: LovelaceConfig | None = hass.data[DOMAIN]["dashboards"].get(url_path) dashboard = hass.data[LOVELACE_DATA].dashboards.get(url_path)
if dashboard is None: if dashboard is None:
raise ValueError("Invalid dashboard specified") raise ValueError("Invalid dashboard specified")
@ -172,7 +175,7 @@ async def _get_dashboard_info(hass, url_path):
url_path = dashboard.url_path url_path = dashboard.url_path
title = config.get("title", url_path) if config else url_path title = config.get("title", url_path) if config else url_path
views = [] views: list[dict[str, Any]] = []
data = { data = {
"title": title, "title": title,
"url_path": url_path, "url_path": url_path,

View File

@ -1,6 +1,8 @@
"""Constants for Lovelace.""" """Constants for Lovelace."""
from typing import Any from __future__ import annotations
from typing import TYPE_CHECKING, Any
import voluptuous as vol import voluptuous as vol
@ -15,8 +17,13 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import VolDictType from homeassistant.helpers.typing import VolDictType
from homeassistant.util import slugify from homeassistant.util import slugify
from homeassistant.util.hass_dict import HassKey
if TYPE_CHECKING:
from . import LovelaceData
DOMAIN = "lovelace" DOMAIN = "lovelace"
LOVELACE_DATA: HassKey[LovelaceData] = HassKey(DOMAIN)
DEFAULT_ICON = "hass:view-dashboard" DEFAULT_ICON = "hass:view-dashboard"

View File

@ -27,6 +27,7 @@ from .const import (
DOMAIN, DOMAIN,
EVENT_LOVELACE_UPDATED, EVENT_LOVELACE_UPDATED,
LOVELACE_CONFIG_FILE, LOVELACE_CONFIG_FILE,
LOVELACE_DATA,
MODE_STORAGE, MODE_STORAGE,
MODE_YAML, MODE_YAML,
STORAGE_DASHBOARD_CREATE_FIELDS, STORAGE_DASHBOARD_CREATE_FIELDS,
@ -315,7 +316,7 @@ class DashboardsCollectionWebSocket(collection.DictStorageCollectionWebsocket):
msg["id"], msg["id"],
[ [
dashboard.config dashboard.config
for dashboard in hass.data[DOMAIN]["dashboards"].values() for dashboard in hass.data[LOVELACE_DATA].dashboards.values()
if dashboard.config if dashboard.config
], ],
) )

View File

@ -1,12 +1,13 @@
"""Provide info to system health.""" """Provide info to system health."""
import asyncio import asyncio
from typing import Any
from homeassistant.components import system_health from homeassistant.components import system_health
from homeassistant.const import CONF_MODE from homeassistant.const import CONF_MODE
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from .const import DOMAIN, MODE_AUTO, MODE_STORAGE, MODE_YAML from .const import LOVELACE_DATA, MODE_AUTO, MODE_STORAGE, MODE_YAML
@callback @callback
@ -17,15 +18,17 @@ def async_register(
register.async_register_info(system_health_info, "/config/lovelace") register.async_register_info(system_health_info, "/config/lovelace")
async def system_health_info(hass): async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:
"""Get info for the info page.""" """Get info for the info page."""
health_info = {"dashboards": len(hass.data[DOMAIN]["dashboards"])} health_info: dict[str, Any] = {
health_info.update(await hass.data[DOMAIN]["resources"].async_get_info()) "dashboards": len(hass.data[LOVELACE_DATA].dashboards)
}
health_info.update(await hass.data[LOVELACE_DATA].resources.async_get_info())
dashboards_info = await asyncio.gather( dashboards_info = await asyncio.gather(
*( *(
hass.data[DOMAIN]["dashboards"][dashboard].async_get_info() hass.data[LOVELACE_DATA].dashboards[dashboard].async_get_info()
for dashboard in hass.data[DOMAIN]["dashboards"] for dashboard in hass.data[LOVELACE_DATA].dashboards
) )
) )
@ -39,7 +42,7 @@ async def system_health_info(hass):
else: else:
health_info[key] = dashboard[key] health_info[key] = dashboard[key]
if hass.data[DOMAIN][CONF_MODE] == MODE_YAML: if hass.data[LOVELACE_DATA].mode == MODE_YAML:
health_info[CONF_MODE] = MODE_YAML health_info[CONF_MODE] = MODE_YAML
elif MODE_STORAGE in modes: elif MODE_STORAGE in modes:
health_info[CONF_MODE] = MODE_STORAGE health_info[CONF_MODE] = MODE_STORAGE

View File

@ -13,7 +13,7 @@ from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.json import json_fragment from homeassistant.helpers.json import json_fragment
from .const import CONF_URL_PATH, DOMAIN, ConfigNotFound from .const import CONF_URL_PATH, LOVELACE_DATA, ConfigNotFound
from .dashboard import LovelaceStorage from .dashboard import LovelaceStorage
@ -27,7 +27,7 @@ def _handle_errors(func):
msg: dict[str, Any], msg: dict[str, Any],
) -> None: ) -> None:
url_path = msg.get(CONF_URL_PATH) url_path = msg.get(CONF_URL_PATH)
config: LovelaceStorage | None = hass.data[DOMAIN]["dashboards"].get(url_path) config = hass.data[LOVELACE_DATA].dashboards.get(url_path)
if config is None: if config is None:
connection.send_error( connection.send_error(
@ -74,7 +74,7 @@ async def websocket_lovelace_resources_impl(
This function is called by both Storage and YAML mode WS handlers. This function is called by both Storage and YAML mode WS handlers.
""" """
resources = hass.data[DOMAIN]["resources"] resources = hass.data[LOVELACE_DATA].resources
if hass.config.safe_mode: if hass.config.safe_mode:
connection.send_result(msg["id"], []) connection.send_result(msg["id"], [])