Add own coordinator to devolo_home_network (#128159)

This commit is contained in:
Guido Schmitz 2024-10-29 13:23:28 +01:00 committed by GitHub
parent 478bf643bf
commit 5ae2f3d081
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 86 additions and 41 deletions

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
from asyncio import Semaphore
from dataclasses import dataclass from dataclasses import dataclass
import logging import logging
from typing import Any from typing import Any
@ -32,7 +33,7 @@ from homeassistant.core import Event, HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.httpx_client import get_async_client from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.update_coordinator import UpdateFailed
from .const import ( from .const import (
CONNECTED_PLC_DEVICES, CONNECTED_PLC_DEVICES,
@ -47,6 +48,7 @@ from .const import (
SWITCH_GUEST_WIFI, SWITCH_GUEST_WIFI,
SWITCH_LEDS, SWITCH_LEDS,
) )
from .coordinator import DevoloDataUpdateCoordinator
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -58,7 +60,7 @@ class DevoloHomeNetworkData:
"""The devolo Home Network data.""" """The devolo Home Network data."""
device: Device device: Device
coordinators: dict[str, DataUpdateCoordinator[Any]] coordinators: dict[str, DevoloDataUpdateCoordinator[Any]]
async def async_setup_entry( async def async_setup_entry(
@ -68,6 +70,7 @@ async def async_setup_entry(
zeroconf_instance = await zeroconf.async_get_async_instance(hass) zeroconf_instance = await zeroconf.async_get_async_instance(hass)
async_client = get_async_client(hass) async_client = get_async_client(hass)
device_registry = dr.async_get(hass) device_registry = dr.async_get(hass)
semaphore = Semaphore(1)
try: try:
device = Device( device = Device(
@ -163,58 +166,65 @@ async def async_setup_entry(
"""Disconnect from device.""" """Disconnect from device."""
await device.async_disconnect() await device.async_disconnect()
coordinators: dict[str, DataUpdateCoordinator[Any]] = {} coordinators: dict[str, DevoloDataUpdateCoordinator[Any]] = {}
if device.plcnet: if device.plcnet:
coordinators[CONNECTED_PLC_DEVICES] = DataUpdateCoordinator( coordinators[CONNECTED_PLC_DEVICES] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=CONNECTED_PLC_DEVICES, name=CONNECTED_PLC_DEVICES,
semaphore=semaphore,
update_method=async_update_connected_plc_devices, update_method=async_update_connected_plc_devices,
update_interval=LONG_UPDATE_INTERVAL, update_interval=LONG_UPDATE_INTERVAL,
) )
if device.device and "led" in device.device.features: if device.device and "led" in device.device.features:
coordinators[SWITCH_LEDS] = DataUpdateCoordinator( coordinators[SWITCH_LEDS] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=SWITCH_LEDS, name=SWITCH_LEDS,
semaphore=semaphore,
update_method=async_update_led_status, update_method=async_update_led_status,
update_interval=SHORT_UPDATE_INTERVAL, update_interval=SHORT_UPDATE_INTERVAL,
) )
if device.device and "restart" in device.device.features: if device.device and "restart" in device.device.features:
coordinators[LAST_RESTART] = DataUpdateCoordinator( coordinators[LAST_RESTART] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=LAST_RESTART, name=LAST_RESTART,
semaphore=semaphore,
update_method=async_update_last_restart, update_method=async_update_last_restart,
update_interval=SHORT_UPDATE_INTERVAL, update_interval=SHORT_UPDATE_INTERVAL,
) )
if device.device and "update" in device.device.features: if device.device and "update" in device.device.features:
coordinators[REGULAR_FIRMWARE] = DataUpdateCoordinator( coordinators[REGULAR_FIRMWARE] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=REGULAR_FIRMWARE, name=REGULAR_FIRMWARE,
semaphore=semaphore,
update_method=async_update_firmware_available, update_method=async_update_firmware_available,
update_interval=FIRMWARE_UPDATE_INTERVAL, update_interval=FIRMWARE_UPDATE_INTERVAL,
) )
if device.device and "wifi1" in device.device.features: if device.device and "wifi1" in device.device.features:
coordinators[CONNECTED_WIFI_CLIENTS] = DataUpdateCoordinator( coordinators[CONNECTED_WIFI_CLIENTS] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=CONNECTED_WIFI_CLIENTS, name=CONNECTED_WIFI_CLIENTS,
semaphore=semaphore,
update_method=async_update_wifi_connected_station, update_method=async_update_wifi_connected_station,
update_interval=SHORT_UPDATE_INTERVAL, update_interval=SHORT_UPDATE_INTERVAL,
) )
coordinators[NEIGHBORING_WIFI_NETWORKS] = DataUpdateCoordinator( coordinators[NEIGHBORING_WIFI_NETWORKS] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=NEIGHBORING_WIFI_NETWORKS, name=NEIGHBORING_WIFI_NETWORKS,
semaphore=semaphore,
update_method=async_update_wifi_neighbor_access_points, update_method=async_update_wifi_neighbor_access_points,
update_interval=LONG_UPDATE_INTERVAL, update_interval=LONG_UPDATE_INTERVAL,
) )
coordinators[SWITCH_GUEST_WIFI] = DataUpdateCoordinator( coordinators[SWITCH_GUEST_WIFI] = DevoloDataUpdateCoordinator(
hass, hass,
_LOGGER, _LOGGER,
name=SWITCH_GUEST_WIFI, name=SWITCH_GUEST_WIFI,
semaphore=semaphore,
update_method=async_update_guest_wifi_status, update_method=async_update_guest_wifi_status,
update_interval=SHORT_UPDATE_INTERVAL, update_interval=SHORT_UPDATE_INTERVAL,
) )

View File

@ -15,13 +15,13 @@ from homeassistant.components.binary_sensor import (
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
from .const import CONNECTED_PLC_DEVICES, CONNECTED_TO_ROUTER from .const import CONNECTED_PLC_DEVICES, CONNECTED_TO_ROUTER
from .coordinator import DevoloDataUpdateCoordinator
from .entity import DevoloCoordinatorEntity from .entity import DevoloCoordinatorEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
def _is_connected_to_router(entity: DevoloBinarySensorEntity) -> bool: def _is_connected_to_router(entity: DevoloBinarySensorEntity) -> bool:
@ -78,7 +78,7 @@ class DevoloBinarySensorEntity(
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[LogicalNetwork], coordinator: DevoloDataUpdateCoordinator[LogicalNetwork],
description: DevoloBinarySensorEntityDescription, description: DevoloBinarySensorEntityDescription,
) -> None: ) -> None:
"""Initialize entity.""" """Initialize entity."""

View File

@ -22,7 +22,7 @@ from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN, IDENTIFY, PAIRING, RESTART, START_WPS from .const import DOMAIN, IDENTIFY, PAIRING, RESTART, START_WPS
from .entity import DevoloEntity from .entity import DevoloEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)

View File

@ -0,0 +1,38 @@
"""Base coordinator."""
from asyncio import Semaphore
from collections.abc import Awaitable, Callable
from datetime import timedelta
from logging import Logger
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
class DevoloDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
"""Class to manage fetching data from devolo Home Network devices."""
def __init__(
self,
hass: HomeAssistant,
logger: Logger,
*,
name: str,
semaphore: Semaphore,
update_interval: timedelta,
update_method: Callable[[], Awaitable[_DataT]],
) -> None:
"""Initialize global data updater."""
super().__init__(
hass,
logger,
name=name,
update_interval=update_interval,
update_method=update_method,
)
self._semaphore = semaphore
async def _async_update_data(self) -> _DataT:
"""Fetch the latest data from the source."""
async with self._semaphore:
return await super()._async_update_data()

View File

@ -13,15 +13,13 @@ from homeassistant.const import STATE_UNKNOWN, UnitOfFrequency
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import CoordinatorEntity
CoordinatorEntity,
DataUpdateCoordinator,
)
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
from .const import CONNECTED_WIFI_CLIENTS, DOMAIN, WIFI_APTYPE, WIFI_BANDS from .const import CONNECTED_WIFI_CLIENTS, DOMAIN, WIFI_APTYPE, WIFI_BANDS
from .coordinator import DevoloDataUpdateCoordinator
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
async def async_setup_entry( async def async_setup_entry(
@ -31,7 +29,7 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Get all devices and sensors and setup them via config entry.""" """Get all devices and sensors and setup them via config entry."""
device = entry.runtime_data.device device = entry.runtime_data.device
coordinators: dict[str, DataUpdateCoordinator[list[ConnectedStationInfo]]] = ( coordinators: dict[str, DevoloDataUpdateCoordinator[list[ConnectedStationInfo]]] = (
entry.runtime_data.coordinators entry.runtime_data.coordinators
) )
registry = er.async_get(hass) registry = er.async_get(hass)
@ -84,13 +82,14 @@ async def async_setup_entry(
class DevoloScannerEntity( class DevoloScannerEntity(
CoordinatorEntity[DataUpdateCoordinator[list[ConnectedStationInfo]]], ScannerEntity CoordinatorEntity[DevoloDataUpdateCoordinator[list[ConnectedStationInfo]]],
ScannerEntity,
): ):
"""Representation of a devolo device tracker.""" """Representation of a devolo device tracker."""
def __init__( def __init__(
self, self,
coordinator: DataUpdateCoordinator[list[ConnectedStationInfo]], coordinator: DevoloDataUpdateCoordinator[list[ConnectedStationInfo]],
device: Device, device: Device,
mac: str, mac: str,
) -> None: ) -> None:

View File

@ -12,13 +12,11 @@ from devolo_plc_api.plcnet_api import DataRate, LogicalNetwork
from homeassistant.const import ATTR_CONNECTIONS from homeassistant.const import ATTR_CONNECTIONS
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import ( from homeassistant.helpers.update_coordinator import CoordinatorEntity
CoordinatorEntity,
DataUpdateCoordinator,
)
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN from .const import DOMAIN
from .coordinator import DevoloDataUpdateCoordinator
type _DataType = ( type _DataType = (
LogicalNetwork LogicalNetwork
@ -64,14 +62,14 @@ class DevoloEntity(Entity):
class DevoloCoordinatorEntity[_DataT: _DataType]( class DevoloCoordinatorEntity[_DataT: _DataType](
CoordinatorEntity[DataUpdateCoordinator[_DataT]], DevoloEntity CoordinatorEntity[DevoloDataUpdateCoordinator[_DataT]], DevoloEntity
): ):
"""Representation of a coordinated devolo home network device.""" """Representation of a coordinated devolo home network device."""
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[_DataT], coordinator: DevoloDataUpdateCoordinator[_DataT],
) -> None: ) -> None:
"""Initialize a devolo home network device.""" """Initialize a devolo home network device."""
super().__init__(coordinator) super().__init__(coordinator)

View File

@ -13,14 +13,14 @@ from homeassistant.components.image import ImageEntity, ImageEntityDescription
from homeassistant.const import EntityCategory from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
from .const import IMAGE_GUEST_WIFI, SWITCH_GUEST_WIFI from .const import IMAGE_GUEST_WIFI, SWITCH_GUEST_WIFI
from .coordinator import DevoloDataUpdateCoordinator
from .entity import DevoloCoordinatorEntity from .entity import DevoloCoordinatorEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -66,7 +66,7 @@ class DevoloImageEntity(DevoloCoordinatorEntity[WifiGuestAccessGet], ImageEntity
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[WifiGuestAccessGet], coordinator: DevoloDataUpdateCoordinator[WifiGuestAccessGet],
description: DevoloImageEntityDescription, description: DevoloImageEntityDescription,
) -> None: ) -> None:
"""Initialize entity.""" """Initialize entity."""

View File

@ -20,7 +20,6 @@ from homeassistant.components.sensor import (
from homeassistant.const import EntityCategory, UnitOfDataRate from homeassistant.const import EntityCategory, UnitOfDataRate
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.dt import utcnow from homeassistant.util.dt import utcnow
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
@ -32,9 +31,10 @@ from .const import (
PLC_RX_RATE, PLC_RX_RATE,
PLC_TX_RATE, PLC_TX_RATE,
) )
from .coordinator import DevoloDataUpdateCoordinator
from .entity import DevoloCoordinatorEntity from .entity import DevoloCoordinatorEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
def _last_restart(runtime: int) -> datetime: def _last_restart(runtime: int) -> datetime:
@ -198,7 +198,7 @@ class BaseDevoloSensorEntity[
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[_CoordinatorDataT], coordinator: DevoloDataUpdateCoordinator[_CoordinatorDataT],
description: DevoloSensorEntityDescription[_ValueDataT, _SensorDataT], description: DevoloSensorEntityDescription[_ValueDataT, _SensorDataT],
) -> None: ) -> None:
"""Initialize entity.""" """Initialize entity."""
@ -231,7 +231,7 @@ class DevoloPlcDataRateSensorEntity(
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[LogicalNetwork], coordinator: DevoloDataUpdateCoordinator[LogicalNetwork],
description: DevoloSensorEntityDescription[DataRate, float], description: DevoloSensorEntityDescription[DataRate, float],
peer: str, peer: str,
) -> None: ) -> None:

View File

@ -15,13 +15,13 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN, SWITCH_GUEST_WIFI, SWITCH_LEDS from .const import DOMAIN, SWITCH_GUEST_WIFI, SWITCH_LEDS
from .coordinator import DevoloDataUpdateCoordinator
from .entity import DevoloCoordinatorEntity from .entity import DevoloCoordinatorEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
type _DataType = WifiGuestAccessGet | bool type _DataType = WifiGuestAccessGet | bool
@ -91,7 +91,7 @@ class DevoloSwitchEntity[_DataT: _DataType](
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[_DataT], coordinator: DevoloDataUpdateCoordinator[_DataT],
description: DevoloSwitchEntityDescription[_DataT], description: DevoloSwitchEntityDescription[_DataT],
) -> None: ) -> None:
"""Initialize entity.""" """Initialize entity."""

View File

@ -20,13 +20,13 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import DevoloHomeNetworkConfigEntry from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN, REGULAR_FIRMWARE from .const import DOMAIN, REGULAR_FIRMWARE
from .coordinator import DevoloDataUpdateCoordinator
from .entity import DevoloCoordinatorEntity from .entity import DevoloCoordinatorEntity
PARALLEL_UPDATES = 1 PARALLEL_UPDATES = 0
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
@ -79,7 +79,7 @@ class DevoloUpdateEntity(DevoloCoordinatorEntity, UpdateEntity):
def __init__( def __init__(
self, self,
entry: DevoloHomeNetworkConfigEntry, entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator, coordinator: DevoloDataUpdateCoordinator,
description: DevoloUpdateEntityDescription, description: DevoloUpdateEntityDescription,
) -> None: ) -> None:
"""Initialize entity.""" """Initialize entity."""