Move entity definitions into own module in AVM Fritz!Tools (#117701)

* move entity definitions into own module

* merge entity description mixin

* add entity.py to .coveragerc
This commit is contained in:
Michael 2024-05-18 22:25:25 +02:00 committed by GitHub
parent a983a8c6d8
commit 6d3cafb43b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 154 additions and 164 deletions

View File

@ -461,6 +461,7 @@ omit =
homeassistant/components/freebox/home_base.py
homeassistant/components/freebox/switch.py
homeassistant/components/fritz/coordinator.py
homeassistant/components/fritz/entity.py
homeassistant/components/fritz/services.py
homeassistant/components/fritz/switch.py
homeassistant/components/fritzbox_callmonitor/__init__.py

View File

@ -17,17 +17,13 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .coordinator import (
AvmWrapper,
ConnectionInfo,
FritzBoxBaseCoordinatorEntity,
FritzEntityDescription,
)
from .coordinator import AvmWrapper, ConnectionInfo
from .entity import FritzBoxBaseCoordinatorEntity, FritzEntityDescription
_LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True)
@dataclass(frozen=True, kw_only=True)
class FritzBinarySensorEntityDescription(
BinarySensorEntityDescription, FritzEntityDescription
):

View File

@ -20,13 +20,8 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import BUTTON_TYPE_WOL, CONNECTION_TYPE_LAN, DATA_FRITZ, DOMAIN, MeshRoles
from .coordinator import (
AvmWrapper,
FritzData,
FritzDevice,
FritzDeviceBase,
_is_tracked,
)
from .coordinator import AvmWrapper, FritzData, FritzDevice, _is_tracked
from .entity import FritzDeviceBase
_LOGGER = logging.getLogger(__name__)

View File

@ -33,21 +33,14 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.typing import StateType
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
UpdateFailed,
)
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from .const import (
CONF_OLD_DISCOVERY,
DEFAULT_CONF_OLD_DISCOVERY,
DEFAULT_DEVICE_NAME,
DEFAULT_HOST,
DEFAULT_SSL,
DEFAULT_USERNAME,
@ -960,50 +953,6 @@ class FritzData:
wol_buttons: dict = field(default_factory=dict)
class FritzDeviceBase(CoordinatorEntity[AvmWrapper]):
"""Entity base class for a device connected to a FRITZ!Box device."""
def __init__(self, avm_wrapper: AvmWrapper, device: FritzDevice) -> None:
"""Initialize a FRITZ!Box device."""
super().__init__(avm_wrapper)
self._avm_wrapper = avm_wrapper
self._mac: str = device.mac_address
self._name: str = device.hostname or DEFAULT_DEVICE_NAME
@property
def name(self) -> str:
"""Return device name."""
return self._name
@property
def ip_address(self) -> str | None:
"""Return the primary ip address of the device."""
if self._mac:
return self._avm_wrapper.devices[self._mac].ip_address
return None
@property
def mac_address(self) -> str:
"""Return the mac address of the device."""
return self._mac
@property
def hostname(self) -> str | None:
"""Return hostname of the device."""
if self._mac:
return self._avm_wrapper.devices[self._mac].hostname
return None
async def async_process_update(self) -> None:
"""Update device."""
raise NotImplementedError
async def async_on_demand_update(self) -> None:
"""Update state."""
await self.async_process_update()
self.async_write_ha_state()
class FritzDevice:
"""Representation of a device connected to the FRITZ!Box."""
@ -1102,87 +1051,6 @@ class SwitchInfo(TypedDict):
init_state: bool
class FritzBoxBaseEntity:
"""Fritz host entity base class."""
def __init__(self, avm_wrapper: AvmWrapper, device_name: str) -> None:
"""Init device info class."""
self._avm_wrapper = avm_wrapper
self._device_name = device_name
@property
def mac_address(self) -> str:
"""Return the mac address of the main device."""
return self._avm_wrapper.mac
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
return DeviceInfo(
configuration_url=f"http://{self._avm_wrapper.host}",
connections={(dr.CONNECTION_NETWORK_MAC, self.mac_address)},
identifiers={(DOMAIN, self._avm_wrapper.unique_id)},
manufacturer="AVM",
model=self._avm_wrapper.model,
name=self._device_name,
sw_version=self._avm_wrapper.current_firmware,
)
@dataclass(frozen=True)
class FritzRequireKeysMixin:
"""Fritz entity description mix in."""
value_fn: Callable[[FritzStatus, Any], Any] | None
@dataclass(frozen=True)
class FritzEntityDescription(EntityDescription, FritzRequireKeysMixin):
"""Fritz entity base description."""
class FritzBoxBaseCoordinatorEntity(CoordinatorEntity[AvmWrapper]):
"""Fritz host coordinator entity base class."""
entity_description: FritzEntityDescription
_attr_has_entity_name = True
def __init__(
self,
avm_wrapper: AvmWrapper,
device_name: str,
description: FritzEntityDescription,
) -> None:
"""Init device info class."""
super().__init__(avm_wrapper)
self.entity_description = description
self._device_name = device_name
self._attr_unique_id = f"{avm_wrapper.unique_id}-{description.key}"
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
if self.entity_description.value_fn is not None:
self.async_on_remove(
await self.coordinator.async_register_entity_updates(
self.entity_description.key, self.entity_description.value_fn
)
)
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
return DeviceInfo(
configuration_url=f"http://{self.coordinator.host}",
connections={(dr.CONNECTION_NETWORK_MAC, self.coordinator.mac)},
identifiers={(DOMAIN, self.coordinator.unique_id)},
manufacturer="AVM",
model=self.coordinator.model,
name=self._device_name,
sw_version=self.coordinator.current_firmware,
)
@dataclass
class ConnectionInfo:
"""Fritz sensor connection information class."""

View File

@ -16,9 +16,9 @@ from .coordinator import (
AvmWrapper,
FritzData,
FritzDevice,
FritzDeviceBase,
device_filter_out_from_trackers,
)
from .entity import FritzDeviceBase
_LOGGER = logging.getLogger(__name__)

View File

@ -0,0 +1,137 @@
"""AVM FRITZ!Tools entities."""
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from typing import Any
from fritzconnection.lib.fritzstatus import FritzStatus
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DEFAULT_DEVICE_NAME, DOMAIN
from .coordinator import AvmWrapper, FritzDevice
class FritzDeviceBase(CoordinatorEntity[AvmWrapper]):
"""Entity base class for a device connected to a FRITZ!Box device."""
def __init__(self, avm_wrapper: AvmWrapper, device: FritzDevice) -> None:
"""Initialize a FRITZ!Box device."""
super().__init__(avm_wrapper)
self._avm_wrapper = avm_wrapper
self._mac: str = device.mac_address
self._name: str = device.hostname or DEFAULT_DEVICE_NAME
@property
def name(self) -> str:
"""Return device name."""
return self._name
@property
def ip_address(self) -> str | None:
"""Return the primary ip address of the device."""
if self._mac:
return self._avm_wrapper.devices[self._mac].ip_address
return None
@property
def mac_address(self) -> str:
"""Return the mac address of the device."""
return self._mac
@property
def hostname(self) -> str | None:
"""Return hostname of the device."""
if self._mac:
return self._avm_wrapper.devices[self._mac].hostname
return None
async def async_process_update(self) -> None:
"""Update device."""
raise NotImplementedError
async def async_on_demand_update(self) -> None:
"""Update state."""
await self.async_process_update()
self.async_write_ha_state()
class FritzBoxBaseEntity:
"""Fritz host entity base class."""
def __init__(self, avm_wrapper: AvmWrapper, device_name: str) -> None:
"""Init device info class."""
self._avm_wrapper = avm_wrapper
self._device_name = device_name
@property
def mac_address(self) -> str:
"""Return the mac address of the main device."""
return self._avm_wrapper.mac
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
return DeviceInfo(
configuration_url=f"http://{self._avm_wrapper.host}",
connections={(dr.CONNECTION_NETWORK_MAC, self.mac_address)},
identifiers={(DOMAIN, self._avm_wrapper.unique_id)},
manufacturer="AVM",
model=self._avm_wrapper.model,
name=self._device_name,
sw_version=self._avm_wrapper.current_firmware,
)
@dataclass(frozen=True, kw_only=True)
class FritzEntityDescription(EntityDescription):
"""Fritz entity base description."""
value_fn: Callable[[FritzStatus, Any], Any] | None
class FritzBoxBaseCoordinatorEntity(CoordinatorEntity[AvmWrapper]):
"""Fritz host coordinator entity base class."""
entity_description: FritzEntityDescription
_attr_has_entity_name = True
def __init__(
self,
avm_wrapper: AvmWrapper,
device_name: str,
description: FritzEntityDescription,
) -> None:
"""Init device info class."""
super().__init__(avm_wrapper)
self.entity_description = description
self._device_name = device_name
self._attr_unique_id = f"{avm_wrapper.unique_id}-{description.key}"
async def async_added_to_hass(self) -> None:
"""When entity is added to hass."""
await super().async_added_to_hass()
if self.entity_description.value_fn is not None:
self.async_on_remove(
await self.coordinator.async_register_entity_updates(
self.entity_description.key, self.entity_description.value_fn
)
)
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
return DeviceInfo(
configuration_url=f"http://{self.coordinator.host}",
connections={(dr.CONNECTION_NETWORK_MAC, self.coordinator.mac)},
identifiers={(DOMAIN, self.coordinator.unique_id)},
manufacturer="AVM",
model=self.coordinator.model,
name=self._device_name,
sw_version=self.coordinator.current_firmware,
)

View File

@ -15,7 +15,8 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import dt as dt_util, slugify
from .const import DOMAIN
from .coordinator import AvmWrapper, FritzBoxBaseEntity
from .coordinator import AvmWrapper
from .entity import FritzBoxBaseEntity
_LOGGER = logging.getLogger(__name__)

View File

@ -28,12 +28,8 @@ from homeassistant.helpers.typing import StateType
from homeassistant.util.dt import utcnow
from .const import DOMAIN, DSL_CONNECTION, UPTIME_DEVIATION
from .coordinator import (
AvmWrapper,
ConnectionInfo,
FritzBoxBaseCoordinatorEntity,
FritzEntityDescription,
)
from .coordinator import AvmWrapper, ConnectionInfo
from .entity import FritzBoxBaseCoordinatorEntity, FritzEntityDescription
_LOGGER = logging.getLogger(__name__)
@ -143,7 +139,7 @@ def _retrieve_link_attenuation_received_state(
return status.attenuation[1] / 10 # type: ignore[no-any-return]
@dataclass(frozen=True)
@dataclass(frozen=True, kw_only=True)
class FritzSensorEntityDescription(SensorEntityDescription, FritzEntityDescription):
"""Describes Fritz sensor entity."""

View File

@ -29,13 +29,12 @@ from .const import (
)
from .coordinator import (
AvmWrapper,
FritzBoxBaseEntity,
FritzData,
FritzDevice,
FritzDeviceBase,
SwitchInfo,
device_filter_out_from_trackers,
)
from .entity import FritzBoxBaseEntity, FritzDeviceBase
_LOGGER = logging.getLogger(__name__)

View File

@ -17,16 +17,13 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN
from .coordinator import (
AvmWrapper,
FritzBoxBaseCoordinatorEntity,
FritzEntityDescription,
)
from .coordinator import AvmWrapper
from .entity import FritzBoxBaseCoordinatorEntity, FritzEntityDescription
_LOGGER = logging.getLogger(__name__)
@dataclass(frozen=True)
@dataclass(frozen=True, kw_only=True)
class FritzUpdateEntityDescription(UpdateEntityDescription, FritzEntityDescription):
"""Describes Fritz update entity."""