1 """Helpers to help coordinate updates."""
3 from __future__
import annotations
5 from collections.abc
import Callable, Coroutine
6 from datetime
import timedelta
10 from aiohttp
import ClientConnectorError, ServerDisconnectedError
11 from pyoverkiz.client
import OverkizClient
12 from pyoverkiz.enums
import EventName, ExecutionState, Protocol
13 from pyoverkiz.exceptions
import (
14 BadCredentialsException,
15 InvalidEventListenerIdException,
17 NotAuthenticatedException,
18 TooManyConcurrentRequestsException,
19 TooManyRequestsException,
21 from pyoverkiz.models
import Device, Event, Place
29 from .const
import DOMAIN, LOGGER, UPDATE_INTERVAL
31 EVENT_HANDLERS: Registry[
32 str, Callable[[OverkizDataUpdateCoordinator, Event], Coroutine[Any, Any,
None]]
37 """Class to manage fetching data from Overkiz platform."""
42 logger: logging.Logger,
45 client: OverkizClient,
46 devices: list[Device],
48 update_interval: timedelta |
None =
None,
51 """Initialize global data updater."""
56 update_interval=update_interval,
61 self.
devicesdevices: dict[str, Device] = {d.device_url: d
for d
in devices}
63 device.protocol
in (Protocol.RTS, Protocol.INTERNAL)
for device
in devices
65 self.
executionsexecutions: dict[str, dict[str, str]] = {}
70 """Fetch Overkiz data via event listener."""
72 events = await self.
clientclient.fetch_events()
73 except BadCredentialsException
as exception:
75 except TooManyConcurrentRequestsException
as exception:
76 raise UpdateFailed(
"Too many concurrent requests.")
from exception
77 except TooManyRequestsException
as exception:
78 raise UpdateFailed(
"Too many requests, try again later.")
from exception
79 except MaintenanceException
as exception:
80 raise UpdateFailed(
"Server is down for maintenance.")
from exception
81 except InvalidEventListenerIdException
as exception:
83 except (TimeoutError, ClientConnectorError)
as exception:
85 except (ServerDisconnectedError, NotAuthenticatedException):
90 await self.
clientclient.login()
92 except BadCredentialsException
as exception:
94 except TooManyRequestsException
as exception:
95 raise UpdateFailed(
"Too many requests, try again later.")
from exception
102 if event_handler := EVENT_HANDLERS.get(event.name):
103 await event_handler(self, event)
112 LOGGER.debug(
"Fetching all devices and state via /setup/devices")
113 return {d.device_url: d
for d
in await self.
clientclient.get_devices(refresh=
True)}
116 """Convert places with sub_places to a flat dictionary [placeoid, label])."""
118 if isinstance(place, Place):
119 areas[place.oid] = place.label
121 if isinstance(place.sub_places, list):
122 for sub_place
in place.sub_places:
128 @EVENT_HANDLERS.register(EventName.DEVICE_AVAILABLE)
130 coordinator: OverkizDataUpdateCoordinator, event: Event
132 """Handle device available event."""
134 coordinator.devices[event.device_url].available =
True
137 @EVENT_HANDLERS.register(EventName.DEVICE_UNAVAILABLE)
138 @EVENT_HANDLERS.register(EventName.DEVICE_DISABLED)
140 coordinator: OverkizDataUpdateCoordinator, event: Event
142 """Handle device unavailable / disabled event."""
144 coordinator.devices[event.device_url].available =
False
147 @EVENT_HANDLERS.register(EventName.DEVICE_CREATED)
148 @EVENT_HANDLERS.register(EventName.DEVICE_UPDATED)
150 coordinator: OverkizDataUpdateCoordinator, event: Event
152 """Handle device unavailable / disabled event."""
153 coordinator.hass.async_create_task(
154 coordinator.hass.config_entries.async_reload(coordinator.config_entry_id)
158 @EVENT_HANDLERS.register(EventName.DEVICE_STATE_CHANGED)
160 coordinator: OverkizDataUpdateCoordinator, event: Event
162 """Handle device state changed event."""
163 if not event.device_url:
166 for state
in event.device_states:
167 device = coordinator.devices[event.device_url]
168 device.states[state.name] = state
171 @EVENT_HANDLERS.register(EventName.DEVICE_REMOVED)
173 coordinator: OverkizDataUpdateCoordinator, event: Event
175 """Handle device removed event."""
176 if not event.device_url:
179 base_device_url = event.device_url.split(
"#")[0]
180 registry = dr.async_get(coordinator.hass)
182 if registered_device := registry.async_get_device(
183 identifiers={(DOMAIN, base_device_url)}
185 registry.async_remove_device(registered_device.id)
188 del coordinator.devices[event.device_url]
191 @EVENT_HANDLERS.register(EventName.EXECUTION_REGISTERED)
193 coordinator: OverkizDataUpdateCoordinator, event: Event
195 """Handle execution registered event."""
196 if event.exec_id
and event.exec_id
not in coordinator.executions:
197 coordinator.executions[event.exec_id] = {}
199 if not coordinator.is_stateless:
200 coordinator.update_interval =
timedelta(seconds=1)
203 @EVENT_HANDLERS.register(EventName.EXECUTION_STATE_CHANGED)
205 coordinator: OverkizDataUpdateCoordinator, event: Event
207 """Handle execution changed event."""
208 if event.exec_id
in coordinator.executions
and event.new_state
in [
209 ExecutionState.COMPLETED,
210 ExecutionState.FAILED,
212 del coordinator.executions[event.exec_id]
None __init__(self, HomeAssistant hass, logging.Logger logger, *str name, OverkizClient client, list[Device] devices, Place|None places, timedelta|None update_interval=None, str config_entry_id)
dict[str, Device] _get_devices(self)
dict[str, Device] _async_update_data(self)
dict[str, str] _places_to_area(self, Place place)
None update_interval(self, timedelta|None value)
timedelta|None update_interval(self)
None on_execution_registered(OverkizDataUpdateCoordinator coordinator, Event event)
None on_device_unavailable_disabled(OverkizDataUpdateCoordinator coordinator, Event event)
None on_device_created_updated(OverkizDataUpdateCoordinator coordinator, Event event)
None on_device_removed(OverkizDataUpdateCoordinator coordinator, Event event)
None on_device_state_changed(OverkizDataUpdateCoordinator coordinator, Event event)
None on_execution_state_changed(OverkizDataUpdateCoordinator coordinator, Event event)
None on_device_available(OverkizDataUpdateCoordinator coordinator, Event event)