Define and use entity description in Axis entity base class (#114007)

Define and use entity description in entity base class
This commit is contained in:
Robert Svensson 2024-03-22 18:59:07 +01:00 committed by GitHub
parent 68e170284f
commit 817d931df0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 51 additions and 47 deletions

View File

@ -23,21 +23,14 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later from homeassistant.helpers.event import async_call_later
from .entity import AxisEventEntity from .entity import AxisEventDescription, AxisEventEntity
from .hub import AxisHub from .hub import AxisHub
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
class AxisBinarySensorDescription(BinarySensorEntityDescription): class AxisBinarySensorDescription(AxisEventDescription, BinarySensorEntityDescription):
"""Axis binary sensor entity description.""" """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 @callback
def event_id_is_int(event_id: str) -> bool: def event_id_is_int(event_id: str) -> bool:
@ -216,15 +209,15 @@ async def async_setup_entry(
class AxisBinarySensor(AxisEventEntity, BinarySensorEntity): class AxisBinarySensor(AxisEventEntity, BinarySensorEntity):
"""Representation of a binary Axis event.""" """Representation of a binary Axis event."""
entity_description: AxisBinarySensorDescription
def __init__( def __init__(
self, hub: AxisHub, description: AxisBinarySensorDescription, event: Event self, hub: AxisHub, description: AxisBinarySensorDescription, event: Event
) -> None: ) -> None:
"""Initialize the Axis binary sensor.""" """Initialize the Axis binary sensor."""
super().__init__(event, hub) super().__init__(hub, description, event)
self.entity_description = description
self._attr_name = description.name_fn(hub, event) or self._attr_name
self._attr_is_on = event.is_tripped self._attr_is_on = event.is_tripped
self._attr_device_class = description.device_class # temporary
self.cancel_scheduled_update: Callable[[], None] | None = None self.cancel_scheduled_update: Callable[[], None] | None = None
@callback @callback

View File

@ -1,16 +1,23 @@
"""Base classes for Axis entities.""" """Base classes for Axis entities."""
from __future__ import annotations
from abc import abstractmethod 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 axis.models.event import Event, EventTopic
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect 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 .const import DOMAIN as AXIS_DOMAIN
from .hub import AxisHub
if TYPE_CHECKING:
from .hub import AxisHub
TOPIC_TO_EVENT_TYPE = { TOPIC_TO_EVENT_TYPE = {
EventTopic.DAY_NIGHT_VISION: "DayNight", 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): class AxisEntity(Entity):
"""Base common to all Axis entities.""" """Base common to all Axis entities."""
@ -66,21 +85,26 @@ class AxisEntity(Entity):
class AxisEventEntity(AxisEntity): class AxisEventEntity(AxisEntity):
"""Base common to all Axis entities from event stream.""" """Base common to all Axis entities from event stream."""
entity_description: AxisEventDescription
_attr_should_poll = False _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.""" """Initialize the Axis event."""
super().__init__(hub) super().__init__(hub)
self.entity_description = description
self._event_id = event.id self._event_id = event.id
self._event_topic = event.topic_base 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_unique_id = f"{hub.unique_id}-{event.topic}-{event.id}"
self._attr_device_class = event.group.value
@callback @callback
@abstractmethod @abstractmethod
def async_event_callback(self, event: Event) -> None: def async_event_callback(self, event: Event) -> None:

View File

@ -1,6 +1,6 @@
"""Support for Axis lights.""" """Support for Axis lights."""
from collections.abc import Callable, Iterable from collections.abc import Iterable
from dataclasses import dataclass from dataclasses import dataclass
from functools import partial from functools import partial
from typing import Any from typing import Any
@ -17,7 +17,7 @@ from homeassistant.config_entries import ConfigEntry
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 .entity import TOPIC_TO_EVENT_TYPE, AxisEventEntity from .entity import TOPIC_TO_EVENT_TYPE, AxisEventDescription, AxisEventEntity
from .hub import AxisHub from .hub import AxisHub
@ -31,16 +31,9 @@ def light_name_fn(hub: AxisHub, event: Event) -> str:
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
class AxisLightDescription(LightEntityDescription): class AxisLightDescription(AxisEventDescription, LightEntityDescription):
"""Axis light entity description.""" """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 = ( ENTITY_DESCRIPTIONS = (
AxisLightDescription( AxisLightDescription(
@ -83,6 +76,8 @@ async def async_setup_entry(
class AxisLight(AxisEventEntity, LightEntity): class AxisLight(AxisEventEntity, LightEntity):
"""Representation of an Axis light.""" """Representation of an Axis light."""
entity_description: AxisLightDescription
_attr_should_poll = True _attr_should_poll = True
_attr_color_mode = ColorMode.BRIGHTNESS _attr_color_mode = ColorMode.BRIGHTNESS
_attr_supported_color_modes = {ColorMode.BRIGHTNESS} _attr_supported_color_modes = {ColorMode.BRIGHTNESS}
@ -91,11 +86,9 @@ class AxisLight(AxisEventEntity, LightEntity):
self, hub: AxisHub, description: AxisLightDescription, event: Event self, hub: AxisHub, description: AxisLightDescription, event: Event
) -> None: ) -> None:
"""Initialize the Axis light.""" """Initialize the Axis light."""
super().__init__(event, hub) super().__init__(hub, description, event)
self.entity_description = description
self._attr_name = description.name_fn(hub, event)
self._attr_is_on = event.is_tripped
self._attr_is_on = event.is_tripped
self._light_id = f"led{event.id}" self._light_id = f"led{event.id}"
self.current_intensity = 0 self.current_intensity = 0
self.max_intensity = 0 self.max_intensity = 0

View File

@ -1,6 +1,6 @@
"""Support for Axis switches.""" """Support for Axis switches."""
from collections.abc import Callable, Iterable from collections.abc import Iterable
from dataclasses import dataclass from dataclasses import dataclass
from functools import partial from functools import partial
from typing import Any from typing import Any
@ -17,21 +17,14 @@ 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 .entity import AxisEventEntity from .entity import AxisEventDescription, AxisEventEntity
from .hub import AxisHub from .hub import AxisHub
@dataclass(frozen=True, kw_only=True) @dataclass(frozen=True, kw_only=True)
class AxisSwitchDescription(SwitchEntityDescription): class AxisSwitchDescription(AxisEventDescription, SwitchEntityDescription):
"""Axis switch entity description.""" """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 = ( ENTITY_DESCRIPTIONS = (
AxisSwitchDescription( AxisSwitchDescription(
@ -76,13 +69,14 @@ async def async_setup_entry(
class AxisSwitch(AxisEventEntity, SwitchEntity): class AxisSwitch(AxisEventEntity, SwitchEntity):
"""Representation of a Axis switch.""" """Representation of a Axis switch."""
entity_description: AxisSwitchDescription
def __init__( def __init__(
self, hub: AxisHub, description: AxisSwitchDescription, event: Event self, hub: AxisHub, description: AxisSwitchDescription, event: Event
) -> None: ) -> None:
"""Initialize the Axis switch.""" """Initialize the Axis switch."""
super().__init__(event, hub) super().__init__(hub, description, event)
self.entity_description = description
self._attr_name = description.name_fn(hub, event) or self._attr_name
self._attr_is_on = event.is_tripped self._attr_is_on = event.is_tripped
@callback @callback