Home Assistant Unofficial Reference 2024.12.1
config_store.py
Go to the documentation of this file.
1 """KNX entity configuration store."""
2 
3 from abc import ABC, abstractmethod
4 import logging
5 from typing import Any, Final, TypedDict
6 
7 from homeassistant.config_entries import ConfigEntry
8 from homeassistant.const import CONF_PLATFORM, Platform
9 from homeassistant.core import HomeAssistant, callback
10 from homeassistant.helpers import entity_registry as er
11 from homeassistant.helpers.storage import Store
12 from homeassistant.util.ulid import ulid_now
13 
14 from ..const import DOMAIN
15 from .const import CONF_DATA
16 
17 _LOGGER = logging.getLogger(__name__)
18 
19 STORAGE_VERSION: Final = 1
20 STORAGE_KEY: Final = f"{DOMAIN}/config_store.json"
21 
22 type KNXPlatformStoreModel = dict[str, dict[str, Any]] # unique_id: configuration
23 type KNXEntityStoreModel = dict[
24  str, KNXPlatformStoreModel
25 ] # platform: KNXPlatformStoreModel
26 
27 
28 class KNXConfigStoreModel(TypedDict):
29  """Represent KNX configuration store data."""
30 
31  entities: KNXEntityStoreModel
32 
33 
35  """Entity platform controller base class."""
36 
37  @abstractmethod
38  async def create_entity(self, unique_id: str, config: dict[str, Any]) -> None:
39  """Create a new entity."""
40 
41  @abstractmethod
42  async def update_entity(
43  self, entity_entry: er.RegistryEntry, config: dict[str, Any]
44  ) -> None:
45  """Update an existing entities configuration."""
46 
47 
49  """Manage KNX config store data."""
50 
51  def __init__(
52  self,
53  hass: HomeAssistant,
54  config_entry: ConfigEntry,
55  ) -> None:
56  """Initialize config store."""
57  self.hasshass = hass
58  self.config_entryconfig_entry = config_entry
59  self._store_store = Store[KNXConfigStoreModel](hass, STORAGE_VERSION, STORAGE_KEY)
60  self.datadata = KNXConfigStoreModel(entities={})
61  self._platform_controllers: dict[Platform, PlatformControllerBase] = {}
62 
63  async def load_data(self) -> None:
64  """Load config store data from storage."""
65  if data := await self._store_store.async_load():
66  self.datadata = KNXConfigStoreModel(**data)
67  _LOGGER.debug(
68  "Loaded KNX config data from storage. %s entity platforms",
69  len(self.datadata["entities"]),
70  )
71 
73  self, platform: Platform, controller: PlatformControllerBase
74  ) -> None:
75  """Add platform controller."""
76  self._platform_controllers[platform] = controller
77 
78  async def create_entity(
79  self, platform: Platform, data: dict[str, Any]
80  ) -> str | None:
81  """Create a new entity."""
82  platform_controller = self._platform_controllers[platform]
83  unique_id = f"knx_es_{ulid_now()}"
84  await platform_controller.create_entity(unique_id, data)
85  # store data after entity was added to be sure config didn't raise exceptions
86  self.datadata["entities"].setdefault(platform, {})[unique_id] = data
87  await self._store_store.async_save(self.datadata)
88 
89  entity_registry = er.async_get(self.hasshass)
90  return entity_registry.async_get_entity_id(platform, DOMAIN, unique_id)
91 
92  @callback
93  def get_entity_config(self, entity_id: str) -> dict[str, Any]:
94  """Return KNX entity configuration."""
95  entity_registry = er.async_get(self.hasshass)
96  if (entry := entity_registry.async_get(entity_id)) is None:
97  raise ConfigStoreException(f"Entity not found: {entity_id}")
98  try:
99  return {
100  CONF_PLATFORM: entry.domain,
101  CONF_DATA: self.datadata["entities"][entry.domain][entry.unique_id],
102  }
103  except KeyError as err:
104  raise ConfigStoreException(f"Entity data not found: {entity_id}") from err
105 
106  async def update_entity(
107  self, platform: Platform, entity_id: str, data: dict[str, Any]
108  ) -> None:
109  """Update an existing entity."""
110  platform_controller = self._platform_controllers[platform]
111  entity_registry = er.async_get(self.hasshass)
112  if (entry := entity_registry.async_get(entity_id)) is None:
113  raise ConfigStoreException(f"Entity not found: {entity_id}")
114  unique_id = entry.unique_id
115  if (
116  platform not in self.datadata["entities"]
117  or unique_id not in self.datadata["entities"][platform]
118  ):
119  raise ConfigStoreException(
120  f"Entity not found in storage: {entity_id} - {unique_id}"
121  )
122  await platform_controller.update_entity(entry, data)
123  # store data after entity is added to make sure config doesn't raise exceptions
124  self.datadata["entities"][platform][unique_id] = data
125  await self._store_store.async_save(self.datadata)
126 
127  async def delete_entity(self, entity_id: str) -> None:
128  """Delete an existing entity."""
129  entity_registry = er.async_get(self.hasshass)
130  if (entry := entity_registry.async_get(entity_id)) is None:
131  raise ConfigStoreException(f"Entity not found: {entity_id}")
132  try:
133  del self.datadata["entities"][entry.domain][entry.unique_id]
134  except KeyError as err:
135  raise ConfigStoreException(
136  f"Entity not found in {entry.domain}: {entry.unique_id}"
137  ) from err
138  entity_registry.async_remove(entity_id)
139  await self._store_store.async_save(self.datadata)
140 
141  def get_entity_entries(self) -> list[er.RegistryEntry]:
142  """Get entity_ids of all UI configured entities."""
143  entity_registry = er.async_get(self.hasshass)
144  unique_ids = {
145  uid for platform in self.datadata["entities"].values() for uid in platform
146  }
147  return [
148  registry_entry
149  for registry_entry in er.async_entries_for_config_entry(
150  entity_registry, self.config_entryconfig_entry.entry_id
151  )
152  if registry_entry.unique_id in unique_ids
153  ]
154 
155 
156 class ConfigStoreException(Exception):
157  """KNX config store exception."""
None add_platform(self, Platform platform, PlatformControllerBase controller)
Definition: config_store.py:74
None update_entity(self, Platform platform, str entity_id, dict[str, Any] data)
str|None create_entity(self, Platform platform, dict[str, Any] data)
Definition: config_store.py:80
None __init__(self, HomeAssistant hass, ConfigEntry config_entry)
Definition: config_store.py:55
None create_entity(self, str unique_id, dict[str, Any] config)
Definition: config_store.py:38
None async_load(HomeAssistant hass)
None async_save(self, _T data)
Definition: storage.py:424