Use ConfigEntry runtime_data in devolo Home Network (#116694)

This commit is contained in:
Guido Schmitz 2024-05-06 22:31:39 +02:00 committed by GitHub
parent b3008b074e
commit e65f2f1984
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 97 additions and 112 deletions

View File

@ -2,6 +2,7 @@
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any
@ -48,10 +49,21 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
DevoloHomeNetworkConfigEntry = ConfigEntry["DevoloHomeNetworkData"]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@dataclass
class DevoloHomeNetworkData:
"""The devolo Home Network data."""
device: Device
coordinators: dict[str, DataUpdateCoordinator[Any]]
async def async_setup_entry(
hass: HomeAssistant, entry: DevoloHomeNetworkConfigEntry
) -> bool:
"""Set up devolo Home Network from a config entry."""
hass.data.setdefault(DOMAIN, {})
zeroconf_instance = await zeroconf.async_get_async_instance(hass)
async_client = get_async_client(hass)
device_registry = dr.async_get(hass)
@ -73,7 +85,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
translation_placeholders={"ip_address": entry.data[CONF_IP_ADDRESS]},
) from err
hass.data[DOMAIN][entry.entry_id] = {"device": device}
entry.runtime_data = DevoloHomeNetworkData(device=device, coordinators={})
async def async_update_firmware_available() -> UpdateFirmwareCheck:
"""Fetch data from API endpoint."""
@ -188,7 +200,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
for coordinator in coordinators.values():
await coordinator.async_config_entry_first_refresh()
hass.data[DOMAIN][entry.entry_id]["coordinators"] = coordinators
entry.runtime_data.coordinators = coordinators
await hass.config_entries.async_forward_entry_setups(entry, platforms(device))
@ -199,15 +211,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(
hass: HomeAssistant, entry: DevoloHomeNetworkConfigEntry
) -> bool:
"""Unload a config entry."""
device: Device = hass.data[DOMAIN][entry.entry_id]["device"]
device = entry.runtime_data.device
unload_ok = await hass.config_entries.async_unload_platforms(
entry, platforms(device)
)
if unload_ok:
await device.async_disconnect()
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -4,9 +4,7 @@ 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 (
@ -14,13 +12,13 @@ from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from .const import CONNECTED_PLC_DEVICES, CONNECTED_TO_ROUTER, DOMAIN
from . import DevoloHomeNetworkConfigEntry
from .const import CONNECTED_PLC_DEVICES, CONNECTED_TO_ROUTER
from .entity import DevoloCoordinatorEntity
@ -52,13 +50,12 @@ SENSOR_TYPES: dict[str, DevoloBinarySensorEntityDescription] = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> 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[Any]] = hass.data[DOMAIN][
entry.entry_id
]["coordinators"]
coordinators = entry.runtime_data.coordinators
entities: list[BinarySensorEntity] = []
entities.append(
@ -66,7 +63,6 @@ async def async_setup_entry(
entry,
coordinators[CONNECTED_PLC_DEVICES],
SENSOR_TYPES[CONNECTED_TO_ROUTER],
device,
)
)
async_add_entities(entities)
@ -79,14 +75,13 @@ class DevoloBinarySensorEntity(
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[LogicalNetwork],
description: DevoloBinarySensorEntityDescription,
device: Device,
) -> None:
"""Initialize entity."""
self.entity_description: DevoloBinarySensorEntityDescription = description
super().__init__(entry, coordinator, device)
super().__init__(entry, coordinator)
@property
def is_on(self) -> bool:

View File

@ -13,12 +13,12 @@ from homeassistant.components.button import (
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN, IDENTIFY, PAIRING, RESTART, START_WPS
from .entity import DevoloEntity
@ -55,10 +55,12 @@ BUTTON_TYPES: dict[str, DevoloButtonEntityDescription] = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Get all devices and buttons and setup them via config entry."""
device: Device = hass.data[DOMAIN][entry.entry_id]["device"]
device = entry.runtime_data.device
entities: list[DevoloButtonEntity] = []
if device.plcnet:
@ -66,14 +68,12 @@ async def async_setup_entry(
DevoloButtonEntity(
entry,
BUTTON_TYPES[IDENTIFY],
device,
)
)
entities.append(
DevoloButtonEntity(
entry,
BUTTON_TYPES[PAIRING],
device,
)
)
if device.device and "restart" in device.device.features:
@ -81,7 +81,6 @@ async def async_setup_entry(
DevoloButtonEntity(
entry,
BUTTON_TYPES[RESTART],
device,
)
)
if device.device and "wifi1" in device.device.features:
@ -89,7 +88,6 @@ async def async_setup_entry(
DevoloButtonEntity(
entry,
BUTTON_TYPES[START_WPS],
device,
)
)
async_add_entities(entities)
@ -102,13 +100,12 @@ class DevoloButtonEntity(DevoloEntity, ButtonEntity):
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
description: DevoloButtonEntityDescription,
device: Device,
) -> None:
"""Initialize entity."""
self.entity_description = description
super().__init__(entry, device)
super().__init__(entry)
async def async_press(self) -> None:
"""Handle the button press."""

View File

@ -114,10 +114,11 @@ class DevoloHomeNetworkConfigFlow(ConfigFlow, domain=DOMAIN):
async def async_step_reauth(self, data: Mapping[str, Any]) -> ConfigFlowResult:
"""Handle reauthentication."""
self.context[CONF_HOST] = data[CONF_IP_ADDRESS]
self.context["title_placeholders"][PRODUCT] = self.hass.data[DOMAIN][
self.context["entry_id"]
]["device"].product
if entry := self.hass.config_entries.async_get_entry(self.context["entry_id"]):
self.context[CONF_HOST] = data[CONF_IP_ADDRESS]
self.context["title_placeholders"][PRODUCT] = (
entry.runtime_data.device.product
)
return await self.async_step_reauth_confirm()
async def async_step_reauth_confirm(

View File

@ -10,7 +10,6 @@ from homeassistant.components.device_tracker import (
ScannerEntity,
SourceType,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_UNKNOWN, UnitOfFrequency
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_registry as er
@ -20,16 +19,19 @@ from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator,
)
from . import DevoloHomeNetworkConfigEntry
from .const import CONNECTED_WIFI_CLIENTS, DOMAIN, WIFI_APTYPE, WIFI_BANDS
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Get all devices and sensors and setup them via config entry."""
device: Device = hass.data[DOMAIN][entry.entry_id]["device"]
device = entry.runtime_data.device
coordinators: dict[str, DataUpdateCoordinator[list[ConnectedStationInfo]]] = (
hass.data[DOMAIN][entry.entry_id]["coordinators"]
entry.runtime_data.coordinators
)
registry = er.async_get(hass)
tracked = set()

View File

@ -4,23 +4,20 @@ from __future__ import annotations
from typing import Any
from devolo_plc_api import Device
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD
from homeassistant.core import HomeAssistant
from .const import DOMAIN
from . import DevoloHomeNetworkConfigEntry
TO_REDACT = {CONF_PASSWORD}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry
hass: HomeAssistant, entry: DevoloHomeNetworkConfigEntry
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""
device: Device = hass.data[DOMAIN][entry.entry_id]["device"]
device = entry.runtime_data.device
diag_data = {
"entry": async_redact_data(entry.as_dict(), TO_REDACT),

View File

@ -4,7 +4,6 @@ from __future__ import annotations
from typing import TypeVar
from devolo_plc_api.device import Device
from devolo_plc_api.device_api import (
ConnectedStationInfo,
NeighborAPInfo,
@ -12,7 +11,6 @@ from devolo_plc_api.device_api import (
)
from devolo_plc_api.plcnet_api import DataRate, LogicalNetwork
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import (
@ -20,6 +18,7 @@ from homeassistant.helpers.update_coordinator import (
DataUpdateCoordinator,
)
from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN
_DataT = TypeVar(
@ -42,24 +41,25 @@ class DevoloEntity(Entity):
def __init__(
self,
entry: ConfigEntry,
device: Device,
entry: DevoloHomeNetworkConfigEntry,
) -> None:
"""Initialize a devolo home network device."""
self.device = device
self.device = entry.runtime_data.device
self.entry = entry
self._attr_device_info = DeviceInfo(
configuration_url=f"http://{device.ip}",
connections={(CONNECTION_NETWORK_MAC, device.mac)},
identifiers={(DOMAIN, str(device.serial_number))},
configuration_url=f"http://{self.device.ip}",
connections={(CONNECTION_NETWORK_MAC, self.device.mac)},
identifiers={(DOMAIN, str(self.device.serial_number))},
manufacturer="devolo",
model=device.product,
serial_number=device.serial_number,
sw_version=device.firmware_version,
model=self.device.product,
serial_number=self.device.serial_number,
sw_version=self.device.firmware_version,
)
self._attr_translation_key = self.entity_description.key
self._attr_unique_id = f"{device.serial_number}_{self.entity_description.key}"
self._attr_unique_id = (
f"{self.device.serial_number}_{self.entity_description.key}"
)
class DevoloCoordinatorEntity(
@ -69,10 +69,9 @@ class DevoloCoordinatorEntity(
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[_DataT],
device: Device,
) -> None:
"""Initialize a devolo home network device."""
super().__init__(coordinator)
DevoloEntity.__init__(self, entry, device)
DevoloEntity.__init__(self, entry)

View File

@ -5,20 +5,19 @@ from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from functools import partial
from typing import Any
from devolo_plc_api import Device, wifi_qr_code
from devolo_plc_api import wifi_qr_code
from devolo_plc_api.device_api import WifiGuestAccessGet
from homeassistant.components.image import ImageEntity, ImageEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
import homeassistant.util.dt as dt_util
from .const import DOMAIN, IMAGE_GUEST_WIFI, SWITCH_GUEST_WIFI
from . import DevoloHomeNetworkConfigEntry
from .const import IMAGE_GUEST_WIFI, SWITCH_GUEST_WIFI
from .entity import DevoloCoordinatorEntity
@ -39,13 +38,12 @@ IMAGE_TYPES: dict[str, DevoloImageEntityDescription] = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> 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[Any]] = hass.data[DOMAIN][
entry.entry_id
]["coordinators"]
coordinators = entry.runtime_data.coordinators
entities: list[ImageEntity] = []
entities.append(
@ -53,7 +51,6 @@ async def async_setup_entry(
entry,
coordinators[SWITCH_GUEST_WIFI],
IMAGE_TYPES[IMAGE_GUEST_WIFI],
device,
)
)
async_add_entities(entities)
@ -66,14 +63,13 @@ class DevoloImageEntity(DevoloCoordinatorEntity[WifiGuestAccessGet], ImageEntity
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[WifiGuestAccessGet],
description: DevoloImageEntityDescription,
device: Device,
) -> None:
"""Initialize entity."""
self.entity_description: DevoloImageEntityDescription = description
super().__init__(entry, coordinator, device)
super().__init__(entry, coordinator)
ImageEntity.__init__(self, coordinator.hass)
self._attr_image_last_updated = dt_util.utcnow()
self._data = self.coordinator.data

View File

@ -7,7 +7,6 @@ from dataclasses import dataclass
from enum import StrEnum
from typing import Any, Generic, TypeVar
from devolo_plc_api.device import Device
from devolo_plc_api.device_api import ConnectedStationInfo, NeighborAPInfo
from devolo_plc_api.plcnet_api import REMOTE, DataRate, LogicalNetwork
@ -17,16 +16,15 @@ from homeassistant.components.sensor import (
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory, UnitOfDataRate
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import DevoloHomeNetworkConfigEntry
from .const import (
CONNECTED_PLC_DEVICES,
CONNECTED_WIFI_CLIENTS,
DOMAIN,
NEIGHBORING_WIFI_NETWORKS,
PLC_RX_RATE,
PLC_TX_RATE,
@ -101,13 +99,13 @@ SENSOR_TYPES: dict[str, DevoloSensorEntityDescription[Any]] = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> 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[Any]] = hass.data[DOMAIN][
entry.entry_id
]["coordinators"]
device = entry.runtime_data.device
coordinators = entry.runtime_data.coordinators
entities: list[BaseDevoloSensorEntity[Any, Any]] = []
if device.plcnet:
@ -116,7 +114,6 @@ async def async_setup_entry(
entry,
coordinators[CONNECTED_PLC_DEVICES],
SENSOR_TYPES[CONNECTED_PLC_DEVICES],
device,
)
)
network = await device.plcnet.async_get_network_overview()
@ -129,7 +126,6 @@ async def async_setup_entry(
entry,
coordinators[CONNECTED_PLC_DEVICES],
SENSOR_TYPES[PLC_TX_RATE],
device,
peer,
)
)
@ -138,7 +134,6 @@ async def async_setup_entry(
entry,
coordinators[CONNECTED_PLC_DEVICES],
SENSOR_TYPES[PLC_RX_RATE],
device,
peer,
)
)
@ -148,7 +143,6 @@ async def async_setup_entry(
entry,
coordinators[CONNECTED_WIFI_CLIENTS],
SENSOR_TYPES[CONNECTED_WIFI_CLIENTS],
device,
)
)
entities.append(
@ -156,7 +150,6 @@ async def async_setup_entry(
entry,
coordinators[NEIGHBORING_WIFI_NETWORKS],
SENSOR_TYPES[NEIGHBORING_WIFI_NETWORKS],
device,
)
)
async_add_entities(entities)
@ -171,14 +164,13 @@ class BaseDevoloSensorEntity(
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[_CoordinatorDataT],
description: DevoloSensorEntityDescription[_ValueDataT],
device: Device,
) -> None:
"""Initialize entity."""
self.entity_description = description
super().__init__(entry, coordinator, device)
super().__init__(entry, coordinator)
class DevoloSensorEntity(BaseDevoloSensorEntity[_CoordinatorDataT, _CoordinatorDataT]):
@ -199,14 +191,13 @@ class DevoloPlcDataRateSensorEntity(BaseDevoloSensorEntity[LogicalNetwork, DataR
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[LogicalNetwork],
description: DevoloSensorEntityDescription[DataRate],
device: Device,
peer: str,
) -> None:
"""Initialize entity."""
super().__init__(entry, coordinator, description, device)
super().__init__(entry, coordinator, description)
self._peer = peer
peer_device = next(
device

View File

@ -11,13 +11,13 @@ from devolo_plc_api.device_api import WifiGuestAccessGet
from devolo_plc_api.exceptions.device import DevicePasswordProtected, DeviceUnavailable
from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN, SWITCH_GUEST_WIFI, SWITCH_LEDS
from .entity import DevoloCoordinatorEntity
@ -51,13 +51,13 @@ SWITCH_TYPES: dict[str, DevoloSwitchEntityDescription[Any]] = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> 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[Any]] = hass.data[DOMAIN][
entry.entry_id
]["coordinators"]
device = entry.runtime_data.device
coordinators = entry.runtime_data.coordinators
entities: list[DevoloSwitchEntity[Any]] = []
if device.device and "led" in device.device.features:
@ -66,7 +66,6 @@ async def async_setup_entry(
entry,
coordinators[SWITCH_LEDS],
SWITCH_TYPES[SWITCH_LEDS],
device,
)
)
if device.device and "wifi1" in device.device.features:
@ -75,7 +74,6 @@ async def async_setup_entry(
entry,
coordinators[SWITCH_GUEST_WIFI],
SWITCH_TYPES[SWITCH_GUEST_WIFI],
device,
)
)
async_add_entities(entities)
@ -88,14 +86,13 @@ class DevoloSwitchEntity(DevoloCoordinatorEntity[_DataT], SwitchEntity):
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator[_DataT],
description: DevoloSwitchEntityDescription[_DataT],
device: Device,
) -> None:
"""Initialize entity."""
self.entity_description = description
super().__init__(entry, coordinator, device)
super().__init__(entry, coordinator)
@property
def is_on(self) -> bool:

View File

@ -16,13 +16,13 @@ from homeassistant.components.update import (
UpdateEntityDescription,
UpdateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import DevoloHomeNetworkConfigEntry
from .const import DOMAIN, REGULAR_FIRMWARE
from .entity import DevoloCoordinatorEntity
@ -47,13 +47,12 @@ UPDATE_TYPES: dict[str, DevoloUpdateEntityDescription] = {
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
hass: HomeAssistant,
entry: DevoloHomeNetworkConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> 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[Any]] = hass.data[DOMAIN][
entry.entry_id
]["coordinators"]
coordinators = entry.runtime_data.coordinators
async_add_entities(
[
@ -61,7 +60,6 @@ async def async_setup_entry(
entry,
coordinators[REGULAR_FIRMWARE],
UPDATE_TYPES[REGULAR_FIRMWARE],
device,
)
]
)
@ -78,14 +76,13 @@ class DevoloUpdateEntity(DevoloCoordinatorEntity, UpdateEntity):
def __init__(
self,
entry: ConfigEntry,
entry: DevoloHomeNetworkConfigEntry,
coordinator: DataUpdateCoordinator,
description: DevoloUpdateEntityDescription,
device: Device,
) -> None:
"""Initialize entity."""
self.entity_description = description
super().__init__(entry, coordinator, device)
super().__init__(entry, coordinator)
self._in_progress_old_version: str | None = None
@property