1 """The Aprilaire coordinator."""
3 from __future__
import annotations
5 from collections.abc
import Awaitable, Callable
9 import pyaprilaire.client
10 from pyaprilaire.const
import MODELS, Attribute, FunctionalDomain
18 from .const
import DOMAIN
20 RECONNECT_INTERVAL = 60 * 60
21 RETRY_CONNECTION_INTERVAL = 10
24 _LOGGER = logging.getLogger(__name__)
26 type AprilaireConfigEntry = ConfigEntry[AprilaireCoordinator]
30 """Coordinator for interacting with the thermostat."""
35 unique_id: str |
None,
39 """Initialize the coordinator."""
43 self.
datadata: dict[str, Any] = {}
45 self._listeners: dict[CALLBACK_TYPE, tuple[CALLBACK_TYPE, object |
None]] = {}
47 self.
clientclient = pyaprilaire.client.AprilaireClient(
53 RETRY_CONNECTION_INTERVAL,
56 if hasattr(self.
clientclient,
"data")
and self.
clientclient.data:
61 self, update_callback: CALLBACK_TYPE, context: Any =
None
62 ) -> Callable[[],
None]:
63 """Listen for data updates."""
66 def remove_listener() -> None:
67 """Remove update listener."""
68 self._listeners.pop(remove_listener)
70 self._listeners[remove_listener] = (update_callback, context)
72 return remove_listener
76 """Update all registered listeners."""
77 for update_callback, _
in list(self._listeners.values()):
81 """Manually update data, notify listeners and reset refresh interval."""
92 old_device_info
is not None
93 and new_device_info
is not None
94 and old_device_info != new_device_info
96 device_registry = dr.async_get(self.
hasshass)
98 device = device_registry.async_get_device(old_device_info[
"identifiers"])
100 if device
is not None:
101 new_device_info.pop(
"identifiers",
None)
102 new_device_info.pop(
"connections",
None)
104 device_registry.async_update_device(
110 """Start listening for data."""
114 """Stop listening for data."""
118 self, ready_callback: Callable[[bool], Awaitable[
None]]
120 """Wait for the client to be ready."""
122 if not self.
datadata
or Attribute.MAC_ADDRESS
not in self.
datadata:
123 data = await self.
clientclient.wait_for_response(
124 FunctionalDomain.IDENTIFICATION, 2, WAIT_TIMEOUT
127 if not data
or Attribute.MAC_ADDRESS
not in data:
128 _LOGGER.error(
"Missing MAC address")
129 await ready_callback(
False)
133 if not self.
datadata
or Attribute.NAME
not in self.
datadata:
134 await self.
clientclient.wait_for_response(
135 FunctionalDomain.IDENTIFICATION, 4, WAIT_TIMEOUT
138 if not self.
datadata
or Attribute.THERMOSTAT_MODES
not in self.
datadata:
139 await self.
clientclient.wait_for_response(
140 FunctionalDomain.CONTROL, 7, WAIT_TIMEOUT
145 or Attribute.INDOOR_TEMPERATURE_CONTROLLING_SENSOR_STATUS
not in self.
datadata
147 await self.
clientclient.wait_for_response(
148 FunctionalDomain.SENSORS, 2, WAIT_TIMEOUT
151 await ready_callback(
True)
157 """Get the name of the thermostat."""
162 """Create the name of the thermostat."""
164 name = data.get(Attribute.NAME)
if data
else None
166 return name
if name
else "Aprilaire"
169 """Get the hardware version."""
171 if hardware_revision := data.get(Attribute.HARDWARE_REVISION):
173 f
"Rev. {chr(hardware_revision)}"
174 if hardware_revision > ord(
"A")
175 else str(hardware_revision)
182 """Get the device info for the thermostat."""
186 """Create the device info for the thermostat."""
188 if data
is None or Attribute.MAC_ADDRESS
not in data
or self.
unique_idunique_id
is None:
192 identifiers={(DOMAIN, self.
unique_idunique_id)},
194 manufacturer=
"Aprilaire",
197 model_number = data.get(Attribute.MODEL_NUMBER)
198 if model_number
is not None:
199 device_info[
"model"] = MODELS.get(model_number, f
"Unknown ({model_number})")
201 device_info[
"hw_version"] = self.
get_hw_versionget_hw_version(data)
203 firmware_major_revision = data.get(Attribute.FIRMWARE_MAJOR_REVISION)
204 firmware_minor_revision = data.get(Attribute.FIRMWARE_MINOR_REVISION)
205 if firmware_major_revision
is not None:
206 device_info[
"sw_version"] = (
207 str(firmware_major_revision)
208 if firmware_minor_revision
is None
209 else f
"{firmware_major_revision}.{firmware_minor_revision:02}"
DeviceInfo|None device_info(self)
str create_device_name(self, dict[str, Any]|None data)
str get_hw_version(self, dict[str, Any] data)
None __init__(self, HomeAssistant hass, str|None unique_id, str host, int port)
bool wait_for_ready(self, Callable[[bool], Awaitable[None]] ready_callback)
Callable[[], None] async_add_listener(self, CALLBACK_TYPE update_callback, Any context=None)
None async_set_updated_data(self, Any data)
DeviceInfo|None create_device_info(self, dict[str, Any] data)
None async_update_listeners(self)