Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 69 additions & 55 deletions homeassistant/components/overkiz/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@
NotAuthenticatedException,
TooManyRequestsException,
)
from pyoverkiz.models import Device, Place
from pyoverkiz.models import Device, Event, Place

from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util.decorator import Registry

from .const import DOMAIN, UPDATE_INTERVAL

_LOGGER = logging.getLogger(__name__)

EVENT_HANDLERS = Registry()


class OverkizDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Device]]):
"""Class to manage fetching data from Overkiz platform."""
Expand Down Expand Up @@ -56,7 +59,7 @@ def __init__(
for device in devices
)
self.executions: dict[str, dict[str, str]] = {}
self.areas = self.places_to_area(places)
self.areas = self._places_to_area(places)
self._config_entry_id = config_entry_id

async def _async_update_data(self) -> dict[str, Device]:
Expand Down Expand Up @@ -88,57 +91,8 @@ async def _async_update_data(self) -> dict[str, Device]:
for event in events:
_LOGGER.debug(event)

if event.name == EventName.DEVICE_AVAILABLE:
self.devices[event.device_url].available = True

elif event.name in [
EventName.DEVICE_UNAVAILABLE,
EventName.DEVICE_DISABLED,
]:
self.devices[event.device_url].available = False

elif event.name in [
EventName.DEVICE_CREATED,
EventName.DEVICE_UPDATED,
Comment thread
iMicknl marked this conversation as resolved.
]:
self.hass.async_create_task(
self.hass.config_entries.async_reload(self._config_entry_id)
)

elif event.name == EventName.DEVICE_REMOVED:
base_device_url, *_ = event.device_url.split("#")
registry = dr.async_get(self.hass)

if registered_device := registry.async_get_device(
{(DOMAIN, base_device_url)}
):
registry.async_remove_device(registered_device.id)

del self.devices[event.device_url]

elif event.name == EventName.DEVICE_STATE_CHANGED:
for state in event.device_states:
device = self.devices[event.device_url]

if (device_state := device.states[state.name]) is None:
device_state = state
device.states[state.name] = device_state

device_state.value = state.value

elif event.name == EventName.EXECUTION_REGISTERED:
if event.exec_id not in self.executions:
self.executions[event.exec_id] = {}

if not self.is_stateless:
self.update_interval = timedelta(seconds=1)

elif (
event.name == EventName.EXECUTION_STATE_CHANGED
and event.exec_id in self.executions
and event.new_state in [ExecutionState.COMPLETED, ExecutionState.FAILED]
):
del self.executions[event.exec_id]
if event_handler := EVENT_HANDLERS.get(event.name):
await event_handler(self, event)

if not self.executions:
self.update_interval = UPDATE_INTERVAL
Expand All @@ -150,14 +104,74 @@ async def _get_devices(self) -> dict[str, Device]:
_LOGGER.debug("Fetching all devices and state via /setup/devices")
return {d.device_url: d for d in await self.client.get_devices(refresh=True)}

def places_to_area(self, place: Place) -> dict[str, str]:
def _places_to_area(self, place: Place) -> dict[str, str]:
"""Convert places with sub_places to a flat dictionary."""
areas = {}
if isinstance(place, Place):
areas[place.oid] = place.label

if isinstance(place.sub_places, list):
for sub_place in place.sub_places:
areas.update(self.places_to_area(sub_place))
areas.update(self._places_to_area(sub_place))

return areas

@EVENT_HANDLERS.register(EventName.DEVICE_AVAILABLE)
async def on_device_available(self, event: Event) -> None:
Comment thread
iMicknl marked this conversation as resolved.
Outdated
"""Handle device available event."""
if event.device_url:
self.devices[event.device_url].available = True

@EVENT_HANDLERS.register((EventName.DEVICE_UNAVAILABLE, EventName.DEVICE_DISABLED))
async def on_device_unavailable_disabled(self, event: Event) -> None:
"""Handle device unavailable / dispabled event."""
if event.device_url:
self.devices[event.device_url].available = False

@EVENT_HANDLERS.register(EventName.DEVICE_STATE_CHANGED)
async def on_device_state_changed(self, event: Event) -> None:
"""Handle device state changed event."""
if not event.device_url:
return

for state in event.device_states:
device = self.devices[event.device_url]

if (device_state := device.states[state.name]) is None:
device_state = state
device.states[state.name] = device_state

device_state.value = state.value

@EVENT_HANDLERS.register(EventName.DEVICE_REMOVED)
async def on_device_removed(self, event: Event) -> None:
"""Handle device removed event."""
if not event.device_url:
return

base_device_url = event.device_url.split("#")[0]
registry = dr.async_get(self.hass)

if registered_device := registry.async_get_device({(DOMAIN, base_device_url)}):
registry.async_remove_device(registered_device.id)

if event.device_url:
del self.devices[event.device_url]

@EVENT_HANDLERS.register(EventName.EXECUTION_REGISTERED)
async def on_execution_registered(self, event: Event) -> None:
"""Handle execution registered event."""
if event.exec_id and event.exec_id not in self.executions:
self.executions[event.exec_id] = {}

if not self.is_stateless:
self.update_interval = timedelta(seconds=1)

@EVENT_HANDLERS.register(EventName.EXECUTION_STATE_CHANGED)
async def on_execution_state_changed(self, event: Event) -> None:
"""Handle execution changed event."""
if event.exec_id in self.executions and event.new_state in [
ExecutionState.COMPLETED,
ExecutionState.FAILED,
]:
del self.executions[event.exec_id]