Home Assistant Unofficial Reference 2024.12.1
storage.py
Go to the documentation of this file.
1 """Helpers for HomeKit data stored in HA storage."""
2 
3 from __future__ import annotations
4 
5 import logging
6 from typing import Any
7 
8 from aiohomekit.characteristic_cache import Pairing, StorageLayout
9 
10 from homeassistant.core import HomeAssistant, callback
11 from homeassistant.helpers.storage import Store
12 
13 from .const import DOMAIN, ENTITY_MAP
14 
15 ENTITY_MAP_STORAGE_KEY = f"{DOMAIN}-entity-map"
16 ENTITY_MAP_STORAGE_VERSION = 1
17 ENTITY_MAP_SAVE_DELAY = 10
18 _LOGGER = logging.getLogger(__name__)
19 
20 
22  """Holds a cache of entity structure data from a paired HomeKit device.
23 
24  HomeKit has a cacheable entity map that describes how an IP or BLE
25  endpoint is structured. This object holds the latest copy of that data.
26 
27  An endpoint is made of accessories, services and characteristics. It is
28  safe to cache this data until the c# discovery data changes.
29 
30  Caching this data means we can add HomeKit devices to HA immediately at
31  start even if discovery hasn't seen them yet or they are out of range. It
32  is also important for BLE devices - accessing the entity structure is
33  very slow for these devices.
34  """
35 
36  def __init__(self, hass: HomeAssistant) -> None:
37  """Create a new entity map store."""
38  self.hasshass = hass
39  self.storestore = Store[StorageLayout](
40  hass, ENTITY_MAP_STORAGE_VERSION, ENTITY_MAP_STORAGE_KEY
41  )
42  self.storage_datastorage_data: dict[str, Pairing] = {}
43 
44  async def async_initialize(self) -> None:
45  """Get the pairing cache data."""
46  if not (raw_storage := await self.storestore.async_load()):
47  # There is no cached data about HomeKit devices yet
48  return
49 
50  self.storage_datastorage_data = raw_storage.get("pairings", {})
51 
52  def get_map(self, homekit_id: str) -> Pairing | None:
53  """Get a pairing cache item."""
54  return self.storage_datastorage_data.get(homekit_id)
55 
56  @callback
58  self,
59  homekit_id: str,
60  config_num: int,
61  accessories: list[Any],
62  broadcast_key: str | None = None,
63  state_num: int | None = None,
64  ) -> Pairing:
65  """Create a new pairing cache."""
66  _LOGGER.debug("Creating or updating entity map for %s", homekit_id)
67  data = Pairing(
68  config_num=config_num,
69  accessories=accessories,
70  broadcast_key=broadcast_key,
71  state_num=state_num,
72  )
73  self.storage_datastorage_data[homekit_id] = data
74  self._async_schedule_save_async_schedule_save()
75  return data
76 
77  @callback
78  def async_delete_map(self, homekit_id: str) -> None:
79  """Delete pairing cache."""
80  removed_one = False
81  # Previously there was a bug where a lowercase homekit_id was stored
82  # in the storage. We need to account for that.
83  for hkid in (homekit_id, homekit_id.lower()):
84  if hkid not in self.storage_datastorage_data:
85  continue
86  _LOGGER.debug("Deleting entity map for %s", hkid)
87  self.storage_datastorage_data.pop(hkid)
88  removed_one = True
89  if removed_one:
90  self._async_schedule_save_async_schedule_save()
91 
92  @callback
93  def _async_schedule_save(self) -> None:
94  """Schedule saving the entity map cache."""
95  self.storestore.async_delay_save(self._data_to_save_data_to_save, ENTITY_MAP_SAVE_DELAY)
96 
97  @callback
98  def _data_to_save(self) -> StorageLayout:
99  """Return data of entity map to store in a file."""
100  return StorageLayout(pairings=self.storage_datastorage_data)
101 
102 
103 async def async_get_entity_storage(hass: HomeAssistant) -> EntityMapStorage:
104  """Get entity storage."""
105  if ENTITY_MAP in hass.data:
106  map_storage: EntityMapStorage = hass.data[ENTITY_MAP]
107  return map_storage
108  map_storage = hass.data[ENTITY_MAP] = EntityMapStorage(hass)
109  await map_storage.async_initialize()
110  return map_storage
Pairing async_create_or_update_map(self, str homekit_id, int config_num, list[Any] accessories, str|None broadcast_key=None, int|None state_num=None)
Definition: storage.py:64
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
EntityMapStorage async_get_entity_storage(HomeAssistant hass)
Definition: storage.py:103
_T|None async_load(self)
Definition: storage.py:275
None async_delay_save(self, Callable[[], _T] data_func, float delay=0)
Definition: storage.py:444