From e99840f82c72d08bf5fa3c7c7db3df2bf412ed0f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 7 Jan 2023 14:12:03 +0100 Subject: [PATCH] Improve `devolo_home_network` generic typing (#85328) --- .../devolo_home_network/__init__.py | 3 +- .../devolo_home_network/binary_sensor.py | 12 ++--- .../devolo_home_network/device_tracker.py | 5 ++- .../components/devolo_home_network/entity.py | 20 ++++++++- .../components/devolo_home_network/sensor.py | 45 ++++++++++++------- 5 files changed, 60 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/devolo_home_network/__init__.py b/homeassistant/components/devolo_home_network/__init__.py index 9d154bc1005..1e750131cc2 100644 --- a/homeassistant/components/devolo_home_network/__init__.py +++ b/homeassistant/components/devolo_home_network/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations import logging +from typing import Any import async_timeout from devolo_plc_api import Device @@ -80,7 +81,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Disconnect from device.""" await device.async_disconnect() - coordinators: dict[str, DataUpdateCoordinator] = {} + coordinators: dict[str, DataUpdateCoordinator[Any]] = {} if device.plcnet: coordinators[CONNECTED_PLC_DEVICES] = DataUpdateCoordinator( hass, diff --git a/homeassistant/components/devolo_home_network/binary_sensor.py b/homeassistant/components/devolo_home_network/binary_sensor.py index 2340e8d4e03..94794e0403d 100644 --- a/homeassistant/components/devolo_home_network/binary_sensor.py +++ b/homeassistant/components/devolo_home_network/binary_sensor.py @@ -3,8 +3,10 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass +from typing import Any from devolo_plc_api import Device +from devolo_plc_api.plcnet_api import LogicalNetwork from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -62,9 +64,9 @@ async def async_setup_entry( ) -> None: """Get all devices and sensors and setup them via config entry.""" device: Device = hass.data[DOMAIN][entry.entry_id]["device"] - coordinators: dict[str, DataUpdateCoordinator] = hass.data[DOMAIN][entry.entry_id][ - "coordinators" - ] + coordinators: dict[str, DataUpdateCoordinator[Any]] = hass.data[DOMAIN][ + entry.entry_id + ]["coordinators"] entities: list[BinarySensorEntity] = [] if device.plcnet: @@ -79,12 +81,12 @@ async def async_setup_entry( async_add_entities(entities) -class DevoloBinarySensorEntity(DevoloEntity, BinarySensorEntity): +class DevoloBinarySensorEntity(DevoloEntity[LogicalNetwork], BinarySensorEntity): """Representation of a devolo binary sensor.""" def __init__( self, - coordinator: DataUpdateCoordinator, + coordinator: DataUpdateCoordinator[LogicalNetwork], description: DevoloBinarySensorEntityDescription, device: Device, device_name: str, diff --git a/homeassistant/components/devolo_home_network/device_tracker.py b/homeassistant/components/devolo_home_network/device_tracker.py index 7d5418e4fde..79f2eb1f495 100644 --- a/homeassistant/components/devolo_home_network/device_tracker.py +++ b/homeassistant/components/devolo_home_network/device_tracker.py @@ -88,7 +88,10 @@ class DevoloScannerEntity( """Representation of a devolo device tracker.""" def __init__( - self, coordinator: DataUpdateCoordinator, device: Device, mac: str + self, + coordinator: DataUpdateCoordinator[list[ConnectedStationInfo]], + device: Device, + mac: str, ) -> None: """Initialize entity.""" super().__init__(coordinator) diff --git a/homeassistant/components/devolo_home_network/entity.py b/homeassistant/components/devolo_home_network/entity.py index b7b2275109f..a7ded44884d 100644 --- a/homeassistant/components/devolo_home_network/entity.py +++ b/homeassistant/components/devolo_home_network/entity.py @@ -1,7 +1,11 @@ """Generic platform.""" from __future__ import annotations +from typing import TypeVar, Union + from devolo_plc_api.device import Device +from devolo_plc_api.device_api import ConnectedStationInfo, NeighborAPInfo +from devolo_plc_api.plcnet_api import LogicalNetwork from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import ( @@ -11,14 +15,26 @@ from homeassistant.helpers.update_coordinator import ( from .const import DOMAIN +_DataT = TypeVar( + "_DataT", + bound=Union[ + LogicalNetwork, + list[ConnectedStationInfo], + list[NeighborAPInfo], + ], +) -class DevoloEntity(CoordinatorEntity): + +class DevoloEntity(CoordinatorEntity[DataUpdateCoordinator[_DataT]]): """Representation of a devolo home network device.""" _attr_has_entity_name = True def __init__( - self, coordinator: DataUpdateCoordinator, device: Device, device_name: str + self, + coordinator: DataUpdateCoordinator[_DataT], + device: Device, + device_name: str, ) -> None: """Initialize a devolo home network device.""" super().__init__(coordinator) diff --git a/homeassistant/components/devolo_home_network/sensor.py b/homeassistant/components/devolo_home_network/sensor.py index 6c2257abbc7..b2382874aa0 100644 --- a/homeassistant/components/devolo_home_network/sensor.py +++ b/homeassistant/components/devolo_home_network/sensor.py @@ -3,9 +3,11 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from typing import Any +from typing import Any, Generic, TypeVar, Union from devolo_plc_api.device import Device +from devolo_plc_api.device_api import ConnectedStationInfo, NeighborAPInfo +from devolo_plc_api.plcnet_api import LogicalNetwork from homeassistant.components.sensor import ( SensorEntity, @@ -26,23 +28,32 @@ from .const import ( ) from .entity import DevoloEntity +_DataT = TypeVar( + "_DataT", + bound=Union[ + LogicalNetwork, + list[ConnectedStationInfo], + list[NeighborAPInfo], + ], +) + @dataclass -class DevoloSensorRequiredKeysMixin: +class DevoloSensorRequiredKeysMixin(Generic[_DataT]): """Mixin for required keys.""" - value_func: Callable[[Any], int] + value_func: Callable[[_DataT], int] @dataclass class DevoloSensorEntityDescription( - SensorEntityDescription, DevoloSensorRequiredKeysMixin + SensorEntityDescription, DevoloSensorRequiredKeysMixin[_DataT] ): """Describes devolo sensor entity.""" -SENSOR_TYPES: dict[str, DevoloSensorEntityDescription] = { - CONNECTED_PLC_DEVICES: DevoloSensorEntityDescription( +SENSOR_TYPES: dict[str, DevoloSensorEntityDescription[Any]] = { + CONNECTED_PLC_DEVICES: DevoloSensorEntityDescription[LogicalNetwork]( key=CONNECTED_PLC_DEVICES, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, @@ -52,7 +63,7 @@ SENSOR_TYPES: dict[str, DevoloSensorEntityDescription] = { {device.mac_address_from for device in data.data_rates} ), ), - CONNECTED_WIFI_CLIENTS: DevoloSensorEntityDescription( + CONNECTED_WIFI_CLIENTS: DevoloSensorEntityDescription[list[ConnectedStationInfo]]( key=CONNECTED_WIFI_CLIENTS, entity_registry_enabled_default=True, icon="mdi:wifi", @@ -60,7 +71,7 @@ SENSOR_TYPES: dict[str, DevoloSensorEntityDescription] = { state_class=SensorStateClass.MEASUREMENT, value_func=len, ), - NEIGHBORING_WIFI_NETWORKS: DevoloSensorEntityDescription( + NEIGHBORING_WIFI_NETWORKS: DevoloSensorEntityDescription[list[NeighborAPInfo]]( key=NEIGHBORING_WIFI_NETWORKS, entity_category=EntityCategory.DIAGNOSTIC, entity_registry_enabled_default=False, @@ -76,11 +87,11 @@ async def async_setup_entry( ) -> None: """Get all devices and sensors and setup them via config entry.""" device: Device = hass.data[DOMAIN][entry.entry_id]["device"] - coordinators: dict[str, DataUpdateCoordinator] = hass.data[DOMAIN][entry.entry_id][ - "coordinators" - ] + coordinators: dict[str, DataUpdateCoordinator[Any]] = hass.data[DOMAIN][ + entry.entry_id + ]["coordinators"] - entities: list[DevoloSensorEntity] = [] + entities: list[DevoloSensorEntity[Any]] = [] if device.plcnet: entities.append( DevoloSensorEntity( @@ -110,18 +121,20 @@ async def async_setup_entry( async_add_entities(entities) -class DevoloSensorEntity(DevoloEntity, SensorEntity): +class DevoloSensorEntity(DevoloEntity[_DataT], SensorEntity): """Representation of a devolo sensor.""" + entity_description: DevoloSensorEntityDescription[_DataT] + def __init__( self, - coordinator: DataUpdateCoordinator, - description: DevoloSensorEntityDescription, + coordinator: DataUpdateCoordinator[_DataT], + description: DevoloSensorEntityDescription[_DataT], device: Device, device_name: str, ) -> None: """Initialize entity.""" - self.entity_description: DevoloSensorEntityDescription = description + self.entity_description = description super().__init__(coordinator, device, device_name) @property