1 """Provide a way to assign areas to floors in one's home."""
3 from __future__
import annotations
5 from collections.abc
import Iterable
7 from dataclasses
import dataclass
8 from datetime
import datetime
9 from typing
import Any, Literal, TypedDict
16 from .normalized_name_base_registry
import (
17 NormalizedNameBaseRegistryEntry,
18 NormalizedNameBaseRegistryItems,
20 from .registry
import BaseRegistry
21 from .singleton
import singleton
22 from .storage
import Store
23 from .typing
import UNDEFINED, UndefinedType
25 DATA_REGISTRY: HassKey[FloorRegistry] =
HassKey(
"floor_registry")
26 EVENT_FLOOR_REGISTRY_UPDATED: EventType[EventFloorRegistryUpdatedData] =
EventType(
27 "floor_registry_updated"
29 STORAGE_KEY =
"core.floor_registry"
30 STORAGE_VERSION_MAJOR = 1
31 STORAGE_VERSION_MINOR = 2
35 """Data type for individual floor. Used in FloorRegistryStoreData."""
47 """Store data type for FloorRegistry."""
49 floors: list[_FloorStoreData]
53 """Event data for when the floor registry is updated."""
55 action: Literal[
"create",
"remove",
"update"]
59 type EventFloorRegistryUpdated = Event[EventFloorRegistryUpdatedData]
62 @dataclass(slots=True, kw_only=True, frozen=True)
64 """Floor registry entry."""
68 icon: str |
None =
None
69 level: int |
None =
None
73 """Store floor registry data."""
77 old_major_version: int,
78 old_minor_version: int,
79 old_data: dict[str, list[dict[str, Any]]],
80 ) -> FloorRegistryStoreData:
81 """Migrate to the new version."""
82 if old_major_version > STORAGE_VERSION_MAJOR:
83 raise ValueError(
"Can't migrate to future version")
85 if old_major_version == 1:
86 if old_minor_version < 2:
89 for floor
in old_data[
"floors"]:
90 floor[
"created_at"] = floor[
"modified_at"] = created_at
96 """Class to hold a registry of floors."""
98 floors: NormalizedNameBaseRegistryItems[FloorEntry]
99 _floor_data: dict[str, FloorEntry]
102 """Initialize the floor registry."""
106 STORAGE_VERSION_MAJOR,
109 minor_version=STORAGE_VERSION_MINOR,
116 We retrieve the FloorEntry from the underlying dict to avoid
117 the overhead of the UserDict __getitem__.
123 """Get floor by name."""
128 """Get all floors."""
129 return self.
floorsfloors.values()
132 """Generate floor ID."""
140 aliases: set[str] |
None =
None,
141 icon: str |
None =
None,
142 level: int |
None =
None,
144 """Create a new floor."""
145 self.
hasshass.verify_event_loop_thread(
"floor_registry.async_create")
149 f
"The name {name} ({floor.normalized_name}) is already in use"
153 aliases=aliases
or set(),
159 floor_id = floor.floor_id
160 self.
floorsfloors[floor_id] = floor
161 self.async_schedule_save()
163 self.
hasshass.bus.async_fire_internal(
164 EVENT_FLOOR_REGISTRY_UPDATED,
172 self.
hasshass.verify_event_loop_thread(
"floor_registry.async_delete")
173 del self.
floorsfloors[floor_id]
174 self.
hasshass.bus.async_fire_internal(
175 EVENT_FLOOR_REGISTRY_UPDATED,
181 self.async_schedule_save()
188 aliases: set[str] | UndefinedType = UNDEFINED,
189 icon: str |
None | UndefinedType = UNDEFINED,
190 level: int | UndefinedType = UNDEFINED,
191 name: str | UndefinedType = UNDEFINED,
193 """Update name of the floor."""
194 old = self.
floorsfloors[floor_id]
195 changes: dict[str, Any] = {
197 for attr_name, value
in (
198 (
"aliases", aliases),
202 if value
is not UNDEFINED
and value != getattr(old, attr_name)
204 if name
is not UNDEFINED
and name != old.name:
205 changes[
"name"] = name
210 changes[
"modified_at"] =
utcnow()
212 self.
hasshass.verify_event_loop_thread(
"floor_registry.async_update")
213 new = self.
floorsfloors[floor_id] = dataclasses.replace(old, **changes)
215 self.async_schedule_save()
216 self.
hasshass.bus.async_fire_internal(
217 EVENT_FLOOR_REGISTRY_UPDATED,
227 """Load the floor registry."""
229 floors = NormalizedNameBaseRegistryItems[FloorEntry]()
232 for floor
in data[
"floors"]:
234 aliases=set(floor[
"aliases"]),
236 floor_id=floor[
"floor_id"],
238 level=floor[
"level"],
239 created_at=datetime.fromisoformat(floor[
"created_at"]),
240 modified_at=datetime.fromisoformat(floor[
"modified_at"]),
248 """Return data of floor registry to store in a file."""
252 "aliases":
list(entry.aliases),
253 "floor_id": entry.floor_id,
255 "level": entry.level,
257 "created_at": entry.created_at.isoformat(),
258 "modified_at": entry.modified_at.isoformat(),
260 for entry
in self.
floorsfloors.values()
266 @singleton(DATA_REGISTRY)
268 """Get floor registry."""
273 """Load floor registry."""
274 assert DATA_REGISTRY
not in hass.data
FloorRegistryStoreData _async_migrate_func(self, int old_major_version, int old_minor_version, dict[str, list[dict[str, Any]]] old_data)
None __init__(self, HomeAssistant hass)
Iterable[FloorEntry] async_list_floors(self)
FloorEntry async_update(self, str floor_id, *set[str]|UndefinedType aliases=UNDEFINED, str|None|UndefinedType icon=UNDEFINED, int|UndefinedType level=UNDEFINED, str|UndefinedType name=UNDEFINED)
FloorEntry async_create(self, str name, *set[str]|None aliases=None, str|None icon=None, int|None level=None)
FloorRegistryStoreData _data_to_save(self)
FloorEntry|None async_get_floor(self, str floor_id)
FloorEntry|None async_get_floor_by_name(self, str name)
str _generate_id(self, str name)
None async_delete(self, str floor_id)
web.Response get(self, web.Request request, str config_key)
FloorRegistry async_get(HomeAssistant hass)
None async_load(HomeAssistant hass)
_VT|None get_by_name(self, str name)
str generate_id_from_name(self, str name)