1 """Provide a way to label and group anything."""
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[LabelRegistry] =
HassKey(
"label_registry")
26 EVENT_LABEL_REGISTRY_UPDATED: EventType[EventLabelRegistryUpdatedData] =
EventType(
27 "label_registry_updated"
29 STORAGE_KEY =
"core.label_registry"
30 STORAGE_VERSION_MAJOR = 1
31 STORAGE_VERSION_MINOR = 2
35 """Data type for individual label. Used in LabelRegistryStoreData."""
38 description: str |
None
47 """Store data type for LabelRegistry."""
49 labels: list[_LabelStoreData]
53 """Event data for when the label registry is updated."""
55 action: Literal[
"create",
"remove",
"update"]
59 type EventLabelRegistryUpdated = Event[EventLabelRegistryUpdatedData]
62 @dataclass(slots=True, frozen=True, kw_only=True)
64 """Label Registry Entry."""
67 description: str |
None =
None
68 color: str |
None =
None
69 icon: str |
None =
None
73 """Store label registry data."""
77 old_major_version: int,
78 old_minor_version: int,
79 old_data: dict[str, list[dict[str, Any]]],
80 ) -> LabelRegistryStoreData:
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 label
in old_data[
"labels"]:
90 label[
"created_at"] = label[
"modified_at"] = created_at
96 """Class to hold a registry of labels."""
98 labels: NormalizedNameBaseRegistryItems[LabelEntry]
99 _label_data: dict[str, LabelEntry]
102 """Initialize the label registry."""
106 STORAGE_VERSION_MAJOR,
109 minor_version=STORAGE_VERSION_MINOR,
116 We retrieve the LabelEntry from the underlying dict to avoid
117 the overhead of the UserDict __getitem__.
123 """Get label by name."""
128 """Get all labels."""
129 return self.
labelslabels.values()
132 """Generate label ID."""
140 color: str |
None =
None,
141 icon: str |
None =
None,
142 description: str |
None =
None,
144 """Create a new label."""
145 self.
hasshass.verify_event_loop_thread(
"label_registry.async_create")
149 f
"The name {name} ({label.normalized_name}) is already in use"
154 description=description,
159 label_id = label.label_id
160 self.
labelslabels[label_id] = label
161 self.async_schedule_save()
163 self.
hasshass.bus.async_fire_internal(
164 EVENT_LABEL_REGISTRY_UPDATED,
172 self.
hasshass.verify_event_loop_thread(
"label_registry.async_delete")
173 del self.
labelslabels[label_id]
174 self.
hasshass.bus.async_fire_internal(
175 EVENT_LABEL_REGISTRY_UPDATED,
181 self.async_schedule_save()
188 color: str |
None | UndefinedType = UNDEFINED,
189 description: str |
None | UndefinedType = UNDEFINED,
190 icon: str |
None | UndefinedType = UNDEFINED,
191 name: str | UndefinedType = UNDEFINED,
193 """Update name of label."""
194 old = self.
labelslabels[label_id]
195 changes: dict[str, Any] = {
197 for attr_name, value
in (
199 (
"description", description),
202 if value
is not UNDEFINED
and getattr(old, attr_name) != value
205 if name
is not UNDEFINED
and name != old.name:
206 changes[
"name"] = name
211 changes[
"modified_at"] =
utcnow()
213 self.
hasshass.verify_event_loop_thread(
"label_registry.async_update")
214 new = self.
labelslabels[label_id] = dataclasses.replace(old, **changes)
216 self.async_schedule_save()
217 self.
hasshass.bus.async_fire_internal(
218 EVENT_LABEL_REGISTRY_UPDATED,
228 """Load the label registry."""
230 labels = NormalizedNameBaseRegistryItems[LabelEntry]()
233 for label
in data[
"labels"]:
235 color=label[
"color"],
236 description=label[
"description"],
238 label_id=label[
"label_id"],
240 created_at=datetime.fromisoformat(label[
"created_at"]),
241 modified_at=datetime.fromisoformat(label[
"modified_at"]),
249 """Return data of label registry to store in a file."""
253 "color": entry.color,
254 "description": entry.description,
256 "label_id": entry.label_id,
258 "created_at": entry.created_at.isoformat(),
259 "modified_at": entry.modified_at.isoformat(),
261 for entry
in self.
labelslabels.values()
267 @singleton(DATA_REGISTRY)
269 """Get label registry."""
274 """Load label registry."""
275 assert DATA_REGISTRY
not in hass.data
LabelRegistryStoreData _async_migrate_func(self, int old_major_version, int old_minor_version, dict[str, list[dict[str, Any]]] old_data)
LabelEntry|None async_get_label_by_name(self, str name)
LabelEntry async_update(self, str label_id, *str|None|UndefinedType color=UNDEFINED, str|None|UndefinedType description=UNDEFINED, str|None|UndefinedType icon=UNDEFINED, str|UndefinedType name=UNDEFINED)
LabelRegistryStoreData _data_to_save(self)
Iterable[LabelEntry] async_list_labels(self)
None __init__(self, HomeAssistant hass)
LabelEntry async_create(self, str name, *str|None color=None, str|None icon=None, str|None description=None)
str _generate_id(self, str name)
None async_delete(self, str label_id)
LabelEntry|None async_get_label(self, str label_id)
web.Response get(self, web.Request request, str config_key)
None async_load(HomeAssistant hass)
LabelRegistry async_get(HomeAssistant hass)
_VT|None get_by_name(self, str name)
str generate_id_from_name(self, str name)