96 lines
3.2 KiB
Python
96 lines
3.2 KiB
Python
"""Tradfri DataUpdateCoordinator."""
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from datetime import timedelta
|
|
from typing import Any
|
|
|
|
from pytradfri.command import Command
|
|
from pytradfri.device import Device
|
|
from pytradfri.error import RequestError
|
|
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
|
|
|
from .const import LOGGER
|
|
|
|
SCAN_INTERVAL = 60 # Interval for updating the coordinator
|
|
|
|
|
|
class TradfriDeviceDataUpdateCoordinator(DataUpdateCoordinator[Device]):
|
|
"""Coordinator to manage data for a specific Tradfri device."""
|
|
|
|
def __init__(
|
|
self,
|
|
hass: HomeAssistant,
|
|
*,
|
|
api: Callable[[Command | list[Command]], Any],
|
|
device: Device,
|
|
) -> None:
|
|
"""Initialize device coordinator."""
|
|
self.api = api
|
|
self.device = device
|
|
self._exception: Exception | None = None
|
|
|
|
super().__init__(
|
|
hass,
|
|
LOGGER,
|
|
name=f"Update coordinator for {device}",
|
|
update_interval=timedelta(seconds=SCAN_INTERVAL),
|
|
)
|
|
|
|
async def set_hub_available(self, available: bool) -> None:
|
|
"""Set status of hub."""
|
|
if available != self.last_update_success:
|
|
if not available:
|
|
self.last_update_success = False
|
|
await self.async_request_refresh()
|
|
|
|
@callback
|
|
def _observe_update(self, device: Device) -> None:
|
|
"""Update the coordinator for a device when a change is detected."""
|
|
self.async_set_updated_data(data=device)
|
|
|
|
@callback
|
|
def _exception_callback(self, exc: Exception) -> None:
|
|
"""Schedule handling exception.."""
|
|
self.hass.async_create_task(self._handle_exception(exc))
|
|
|
|
async def _handle_exception(self, exc: Exception) -> None:
|
|
"""Handle observe exceptions in a coroutine."""
|
|
# Store exception so that it gets raised in _async_update_data
|
|
self._exception = exc
|
|
|
|
LOGGER.debug(
|
|
"Observation failed for %s, trying again", self.device, exc_info=exc
|
|
)
|
|
# Change interval so we get a swift refresh
|
|
self.update_interval = timedelta(seconds=5)
|
|
await self.async_request_refresh()
|
|
|
|
async def _async_update_data(self) -> Device:
|
|
"""Fetch data from the gateway for a specific device."""
|
|
try:
|
|
if self._exception:
|
|
exc = self._exception
|
|
self._exception = None # Clear stored exception
|
|
raise exc
|
|
except RequestError as err:
|
|
raise UpdateFailed(f"Error communicating with API: {err}.") from err
|
|
|
|
if not self.data or not self.last_update_success: # Start subscription
|
|
try:
|
|
cmd = self.device.observe(
|
|
callback=self._observe_update,
|
|
err_callback=self._exception_callback,
|
|
duration=0,
|
|
)
|
|
await self.api(cmd)
|
|
except RequestError as err:
|
|
raise UpdateFailed(f"Error communicating with API: {err}.") from err
|
|
|
|
# Reset update interval
|
|
self.update_interval = timedelta(seconds=SCAN_INTERVAL)
|
|
|
|
return self.device
|