Home Assistant Unofficial Reference 2024.12.1
coordinator.py
Go to the documentation of this file.
1 """Define a Notion data coordinator."""
2 
3 import asyncio
4 from dataclasses import dataclass, field
5 from datetime import timedelta
6 from typing import Any
7 
8 from aionotion.bridge.models import Bridge
9 from aionotion.client import Client
10 from aionotion.errors import InvalidCredentialsError, NotionError
11 from aionotion.listener.models import Listener
12 from aionotion.sensor.models import Sensor
13 from aionotion.user.models import UserPreferences
14 
15 from homeassistant.config_entries import ConfigEntry
16 from homeassistant.const import CONF_USERNAME
17 from homeassistant.core import HomeAssistant, callback
18 from homeassistant.exceptions import ConfigEntryAuthFailed
19 from homeassistant.helpers import device_registry as dr
20 from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
21 
22 from .const import DOMAIN, LOGGER
23 
24 DATA_BRIDGES = "bridges"
25 DATA_LISTENERS = "listeners"
26 DATA_SENSORS = "sensors"
27 DATA_USER_PREFERENCES = "user_preferences"
28 
29 DEFAULT_SCAN_INTERVAL = timedelta(minutes=1)
30 
31 
32 @callback
34  hass: HomeAssistant, entry: ConfigEntry, bridge: Bridge
35 ) -> None:
36  """Register a new bridge."""
37  if name := bridge.name:
38  bridge_name = name.capitalize()
39  else:
40  bridge_name = str(bridge.id)
41 
42  device_registry = dr.async_get(hass)
43  device_registry.async_get_or_create(
44  config_entry_id=entry.entry_id,
45  identifiers={(DOMAIN, bridge.hardware_id)},
46  manufacturer="Silicon Labs",
47  model=str(bridge.hardware_revision),
48  name=bridge_name,
49  sw_version=bridge.firmware_version.wifi,
50  )
51 
52 
53 @dataclass
54 class NotionData:
55  """Define a manager class for Notion data."""
56 
57  hass: HomeAssistant
58  entry: ConfigEntry
59 
60  # Define a dict of bridges, indexed by bridge ID (an integer):
61  bridges: dict[int, Bridge] = field(default_factory=dict)
62 
63  # Define a dict of listeners, indexed by listener UUID (a string):
64  listeners: dict[str, Listener] = field(default_factory=dict)
65 
66  # Define a dict of sensors, indexed by sensor UUID (a string):
67  sensors: dict[str, Sensor] = field(default_factory=dict)
68 
69  # Define a user preferences response object:
70  user_preferences: UserPreferences | None = field(default=None)
71 
72  def update_bridges(self, bridges: list[Bridge]) -> None:
73  """Update the bridges."""
74  for bridge in bridges:
75  # If a new bridge is discovered, register it:
76  if bridge.id not in self.bridges:
77  _async_register_new_bridge(self.hass, self.entry, bridge)
78  self.bridges[bridge.id] = bridge
79 
80  def update_listeners(self, listeners: list[Listener]) -> None:
81  """Update the listeners."""
82  self.listenerslisteners = {listener.id: listener for listener in listeners}
83 
84  def update_sensors(self, sensors: list[Sensor]) -> None:
85  """Update the sensors."""
86  self.sensorssensors = {sensor.uuid: sensor for sensor in sensors}
87 
88  def update_user_preferences(self, user_preferences: UserPreferences) -> None:
89  """Update the user preferences."""
90  self.user_preferencesuser_preferences = user_preferences
91 
92  def asdict(self) -> dict[str, Any]:
93  """Represent this dataclass (and its Pydantic contents) as a dict."""
94  data: dict[str, Any] = {
95  DATA_BRIDGES: [item.to_dict() for item in self.bridges.values()],
96  DATA_LISTENERS: [item.to_dict() for item in self.listenerslisteners.values()],
97  DATA_SENSORS: [item.to_dict() for item in self.sensorssensors.values()],
98  }
99  if self.user_preferencesuser_preferences:
100  data[DATA_USER_PREFERENCES] = self.user_preferencesuser_preferences.to_dict()
101  return data
102 
103 
105  """Define a Notion data coordinator."""
106 
107  config_entry: ConfigEntry
108 
109  def __init__(
110  self,
111  hass: HomeAssistant,
112  *,
113  entry: ConfigEntry,
114  client: Client,
115  ) -> None:
116  """Initialize."""
117  super().__init__(
118  hass,
119  LOGGER,
120  name=entry.data[CONF_USERNAME],
121  update_interval=DEFAULT_SCAN_INTERVAL,
122  )
123 
124  self._client_client = client
125  self._entry_entry = entry
126 
127  async def _async_update_data(self) -> NotionData:
128  """Fetch data from Notion."""
129  data = NotionData(hass=self.hasshass, entry=self._entry_entry)
130 
131  try:
132  async with asyncio.TaskGroup() as tg:
133  bridges = tg.create_task(self._client_client.bridge.async_all())
134  listeners = tg.create_task(self._client_client.listener.async_all())
135  sensors = tg.create_task(self._client_client.sensor.async_all())
136  user_preferences = tg.create_task(self._client_client.user.async_preferences())
137  except BaseExceptionGroup as err:
138  result = err.exceptions[0]
139  if isinstance(result, InvalidCredentialsError):
140  raise ConfigEntryAuthFailed(
141  "Invalid username and/or password"
142  ) from result
143  if isinstance(result, NotionError):
144  raise UpdateFailed(
145  f"There was a Notion error while updating: {result}"
146  ) from result
147  if isinstance(result, Exception):
148  LOGGER.debug(
149  "There was an unknown error while updating: %s",
150  result,
151  exc_info=result,
152  )
153  raise UpdateFailed(
154  f"There was an unknown error while updating: {result}"
155  ) from result
156  if isinstance(result, BaseException):
157  raise result from None
158 
159  data.update_bridges(bridges.result())
160  data.update_listeners(listeners.result())
161  data.update_sensors(sensors.result())
162  data.update_user_preferences(user_preferences.result())
163 
164  return data
None __init__(self, HomeAssistant hass, *ConfigEntry entry, Client client)
Definition: coordinator.py:115
None update_listeners(self, list[Listener] listeners)
Definition: coordinator.py:80
None update_bridges(self, list[Bridge] bridges)
Definition: coordinator.py:72
None update_sensors(self, list[Sensor] sensors)
Definition: coordinator.py:84
None update_user_preferences(self, UserPreferences user_preferences)
Definition: coordinator.py:88
None _async_register_new_bridge(HomeAssistant hass, ConfigEntry entry, Bridge bridge)
Definition: coordinator.py:35