From 817d931df0fa0c9264a7c8abbd9cb887b1f3ebc6 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Fri, 22 Mar 2024 18:59:07 +0100 Subject: [PATCH] Define and use entity description in Axis entity base class (#114007) Define and use entity description in entity base class --- .../components/axis/binary_sensor.py | 19 +++------- homeassistant/components/axis/entity.py | 38 +++++++++++++++---- homeassistant/components/axis/light.py | 21 ++++------ homeassistant/components/axis/switch.py | 20 ++++------ 4 files changed, 51 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/axis/binary_sensor.py b/homeassistant/components/axis/binary_sensor.py index e68487a6bb1..b6df07ce4ef 100644 --- a/homeassistant/components/axis/binary_sensor.py +++ b/homeassistant/components/axis/binary_sensor.py @@ -23,21 +23,14 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.event import async_call_later -from .entity import AxisEventEntity +from .entity import AxisEventDescription, AxisEventEntity from .hub import AxisHub @dataclass(frozen=True, kw_only=True) -class AxisBinarySensorDescription(BinarySensorEntityDescription): +class AxisBinarySensorDescription(AxisEventDescription, BinarySensorEntityDescription): """Axis binary sensor entity description.""" - event_topic: tuple[EventTopic, ...] | EventTopic - """Event topic that provides state updates.""" - name_fn: Callable[[AxisHub, Event], str] = lambda hub, event: "" - """Function providing the corresponding name to the event ID.""" - supported_fn: Callable[[AxisHub, Event], bool] = lambda hub, event: True - """Function validating if event is supported.""" - @callback def event_id_is_int(event_id: str) -> bool: @@ -216,15 +209,15 @@ async def async_setup_entry( class AxisBinarySensor(AxisEventEntity, BinarySensorEntity): """Representation of a binary Axis event.""" + entity_description: AxisBinarySensorDescription + def __init__( self, hub: AxisHub, description: AxisBinarySensorDescription, event: Event ) -> None: """Initialize the Axis binary sensor.""" - super().__init__(event, hub) - self.entity_description = description - self._attr_name = description.name_fn(hub, event) or self._attr_name + super().__init__(hub, description, event) + self._attr_is_on = event.is_tripped - self._attr_device_class = description.device_class # temporary self.cancel_scheduled_update: Callable[[], None] | None = None @callback diff --git a/homeassistant/components/axis/entity.py b/homeassistant/components/axis/entity.py index 6542b8c55f5..7980b7217e8 100644 --- a/homeassistant/components/axis/entity.py +++ b/homeassistant/components/axis/entity.py @@ -1,16 +1,23 @@ """Base classes for Axis entities.""" +from __future__ import annotations + from abc import abstractmethod +from collections.abc import Callable +from dataclasses import dataclass +from typing import TYPE_CHECKING from axis.models.event import Event, EventTopic from homeassistant.core import callback from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import Entity +from homeassistant.helpers.entity import Entity, EntityDescription from .const import DOMAIN as AXIS_DOMAIN -from .hub import AxisHub + +if TYPE_CHECKING: + from .hub import AxisHub TOPIC_TO_EVENT_TYPE = { EventTopic.DAY_NIGHT_VISION: "DayNight", @@ -32,6 +39,18 @@ TOPIC_TO_EVENT_TYPE = { } +@dataclass(frozen=True, kw_only=True) +class AxisEventDescription(EntityDescription): + """Axis event based entity description.""" + + event_topic: tuple[EventTopic, ...] | EventTopic + """Event topic that provides state updates.""" + name_fn: Callable[[AxisHub, Event], str] = lambda hub, event: "" + """Function providing the corresponding name to the event ID.""" + supported_fn: Callable[[AxisHub, Event], bool] = lambda hub, event: True + """Function validating if event is supported.""" + + class AxisEntity(Entity): """Base common to all Axis entities.""" @@ -66,21 +85,26 @@ class AxisEntity(Entity): class AxisEventEntity(AxisEntity): """Base common to all Axis entities from event stream.""" + entity_description: AxisEventDescription + _attr_should_poll = False - def __init__(self, event: Event, hub: AxisHub) -> None: + def __init__( + self, hub: AxisHub, description: AxisEventDescription, event: Event + ) -> None: """Initialize the Axis event.""" super().__init__(hub) + self.entity_description = description + self._event_id = event.id self._event_topic = event.topic_base - self._event_type = TOPIC_TO_EVENT_TYPE[event.topic_base] + event_type = TOPIC_TO_EVENT_TYPE[event.topic_base] + + self._attr_name = description.name_fn(hub, event) or f"{event_type} {event.id}" - self._attr_name = f"{self._event_type} {event.id}" self._attr_unique_id = f"{hub.unique_id}-{event.topic}-{event.id}" - self._attr_device_class = event.group.value - @callback @abstractmethod def async_event_callback(self, event: Event) -> None: diff --git a/homeassistant/components/axis/light.py b/homeassistant/components/axis/light.py index ae7200398bd..1caeac3a247 100644 --- a/homeassistant/components/axis/light.py +++ b/homeassistant/components/axis/light.py @@ -1,6 +1,6 @@ """Support for Axis lights.""" -from collections.abc import Callable, Iterable +from collections.abc import Iterable from dataclasses import dataclass from functools import partial from typing import Any @@ -17,7 +17,7 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .entity import TOPIC_TO_EVENT_TYPE, AxisEventEntity +from .entity import TOPIC_TO_EVENT_TYPE, AxisEventDescription, AxisEventEntity from .hub import AxisHub @@ -31,16 +31,9 @@ def light_name_fn(hub: AxisHub, event: Event) -> str: @dataclass(frozen=True, kw_only=True) -class AxisLightDescription(LightEntityDescription): +class AxisLightDescription(AxisEventDescription, LightEntityDescription): """Axis light entity description.""" - event_topic: EventTopic - """Event topic that provides state updates.""" - name_fn: Callable[[AxisHub, Event], str] - """Function providing the corresponding name to the event ID.""" - supported_fn: Callable[[AxisHub, Event], bool] - """Function validating if event is supported.""" - ENTITY_DESCRIPTIONS = ( AxisLightDescription( @@ -83,6 +76,8 @@ async def async_setup_entry( class AxisLight(AxisEventEntity, LightEntity): """Representation of an Axis light.""" + entity_description: AxisLightDescription + _attr_should_poll = True _attr_color_mode = ColorMode.BRIGHTNESS _attr_supported_color_modes = {ColorMode.BRIGHTNESS} @@ -91,11 +86,9 @@ class AxisLight(AxisEventEntity, LightEntity): self, hub: AxisHub, description: AxisLightDescription, event: Event ) -> None: """Initialize the Axis light.""" - super().__init__(event, hub) - self.entity_description = description - self._attr_name = description.name_fn(hub, event) - self._attr_is_on = event.is_tripped + super().__init__(hub, description, event) + self._attr_is_on = event.is_tripped self._light_id = f"led{event.id}" self.current_intensity = 0 self.max_intensity = 0 diff --git a/homeassistant/components/axis/switch.py b/homeassistant/components/axis/switch.py index 8364d1317f9..34d2e746c5a 100644 --- a/homeassistant/components/axis/switch.py +++ b/homeassistant/components/axis/switch.py @@ -1,6 +1,6 @@ """Support for Axis switches.""" -from collections.abc import Callable, Iterable +from collections.abc import Iterable from dataclasses import dataclass from functools import partial from typing import Any @@ -17,21 +17,14 @@ from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback -from .entity import AxisEventEntity +from .entity import AxisEventDescription, AxisEventEntity from .hub import AxisHub @dataclass(frozen=True, kw_only=True) -class AxisSwitchDescription(SwitchEntityDescription): +class AxisSwitchDescription(AxisEventDescription, SwitchEntityDescription): """Axis switch entity description.""" - event_topic: EventTopic - """Event topic that provides state updates.""" - name_fn: Callable[[AxisHub, Event], str] - """Function providing the corresponding name to the event ID.""" - supported_fn: Callable[[AxisHub, Event], bool] - """Function validating if event is supported.""" - ENTITY_DESCRIPTIONS = ( AxisSwitchDescription( @@ -76,13 +69,14 @@ async def async_setup_entry( class AxisSwitch(AxisEventEntity, SwitchEntity): """Representation of a Axis switch.""" + entity_description: AxisSwitchDescription + def __init__( self, hub: AxisHub, description: AxisSwitchDescription, event: Event ) -> None: """Initialize the Axis switch.""" - super().__init__(event, hub) - self.entity_description = description - self._attr_name = description.name_fn(hub, event) or self._attr_name + super().__init__(hub, description, event) + self._attr_is_on = event.is_tripped @callback