diff --git a/homeassistant/components/unifi/button.py b/homeassistant/components/unifi/button.py index f03971267bb..85cd6c94c74 100644 --- a/homeassistant/components/unifi/button.py +++ b/homeassistant/components/unifi/button.py @@ -6,7 +6,7 @@ from __future__ import annotations from collections.abc import Callable, Coroutine from dataclasses import dataclass -from typing import Any, Generic +from typing import Any import aiounifi from aiounifi.interfaces.api_handlers import ItemEvent @@ -57,21 +57,14 @@ async def async_power_cycle_port_control_fn( await api.request(DevicePowerCyclePortRequest.create(mac, int(index))) -@dataclass(frozen=True) -class UnifiButtonEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): - """Validate and load entities from different UniFi handlers.""" - - control_fn: Callable[[aiounifi.Controller, str], Coroutine[Any, Any, None]] - - -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class UnifiButtonEntityDescription( - ButtonEntityDescription, - UnifiEntityDescription[HandlerT, ApiItemT], - UnifiButtonEntityDescriptionMixin[HandlerT, ApiItemT], + ButtonEntityDescription, UnifiEntityDescription[HandlerT, ApiItemT] ): """Class describing UniFi button entity.""" + control_fn: Callable[[aiounifi.Controller, str], Coroutine[Any, Any, None]] + ENTITY_DESCRIPTIONS: tuple[UnifiButtonEntityDescription, ...] = ( UnifiButtonEntityDescription[Devices, Device]( @@ -84,11 +77,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiButtonEntityDescription, ...] = ( available_fn=async_device_available_fn, control_fn=async_restart_device_control_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda _: "Restart", object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"device_restart-{obj_id}", ), @@ -106,7 +96,6 @@ ENTITY_DESCRIPTIONS: tuple[UnifiButtonEntityDescription, ...] = ( event_to_subscribe=None, name_fn=lambda port: f"{port.name} Power Cycle", object_fn=lambda api, obj_id: api.ports[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: hub.api.ports[obj_id].port_poe, unique_id_fn=lambda hub, obj_id: f"power_cycle-{obj_id}", ), diff --git a/homeassistant/components/unifi/device_tracker.py b/homeassistant/components/unifi/device_tracker.py index 87bc0b6c59b..81ba99c4ebe 100644 --- a/homeassistant/components/unifi/device_tracker.py +++ b/homeassistant/components/unifi/device_tracker.py @@ -6,7 +6,7 @@ from collections.abc import Callable, Mapping from dataclasses import dataclass from datetime import timedelta import logging -from typing import Any, Generic +from typing import Any import aiounifi from aiounifi.interfaces.api_handlers import ItemEvent @@ -136,9 +136,9 @@ def async_device_heartbeat_timedelta_fn(hub: UnifiHub, obj_id: str) -> timedelta return timedelta(seconds=device.next_interval + 60) -@dataclass(frozen=True) -class UnifiEntityTrackerDescriptionMixin(Generic[HandlerT, ApiItemT]): - """Device tracker local functions.""" +@dataclass(frozen=True, kw_only=True) +class UnifiTrackerEntityDescription(UnifiEntityDescription[HandlerT, ApiItemT]): + """Class describing UniFi device tracker entity.""" heartbeat_timedelta_fn: Callable[[UnifiHub, str], timedelta] ip_address_fn: Callable[[aiounifi.Controller, str], str | None] @@ -146,14 +146,6 @@ class UnifiEntityTrackerDescriptionMixin(Generic[HandlerT, ApiItemT]): hostname_fn: Callable[[aiounifi.Controller, str], str | None] -@dataclass(frozen=True) -class UnifiTrackerEntityDescription( - UnifiEntityDescription[HandlerT, ApiItemT], - UnifiEntityTrackerDescriptionMixin[HandlerT, ApiItemT], -): - """Class describing UniFi device tracker entity.""" - - ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = ( UnifiTrackerEntityDescription[Clients, Client]( key="Client device scanner", @@ -173,7 +165,6 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = ( is_connected_fn=async_client_is_connected_fn, name_fn=lambda client: client.name or client.hostname, object_fn=lambda api, obj_id: api.clients[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"{hub.site}-{obj_id}", ip_address_fn=lambda api, obj_id: api.clients[obj_id].ip, @@ -186,13 +177,10 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = ( api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=lambda api, obj_id: None, - event_is_on=None, - event_to_subscribe=None, heartbeat_timedelta_fn=async_device_heartbeat_timedelta_fn, is_connected_fn=lambda ctrlr, obj_id: ctrlr.api.devices[obj_id].state == 1, name_fn=lambda device: device.name or device.model, object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: obj_id, ip_address_fn=lambda api, obj_id: api.devices[obj_id].ip, diff --git a/homeassistant/components/unifi/entity.py b/homeassistant/components/unifi/entity.py index a88f4c9b657..d3dc1f51873 100644 --- a/homeassistant/components/unifi/entity.py +++ b/homeassistant/components/unifi/entity.py @@ -93,26 +93,26 @@ def async_client_device_info_fn(hub: UnifiHub, obj_id: str) -> DeviceInfo: ) -@dataclass(frozen=True) -class UnifiDescription(Generic[HandlerT, ApiItemT]): - """Validate and load entities from different UniFi handlers.""" +@dataclass(frozen=True, kw_only=True) +class UnifiEntityDescription(EntityDescription, Generic[HandlerT, ApiItemT]): + """UniFi Entity Description.""" allowed_fn: Callable[[UnifiHub, str], bool] api_handler_fn: Callable[[aiounifi.Controller], HandlerT] available_fn: Callable[[UnifiHub, str], bool] device_info_fn: Callable[[UnifiHub, str], DeviceInfo | None] - event_is_on: tuple[EventKey, ...] | None - event_to_subscribe: tuple[EventKey, ...] | None name_fn: Callable[[ApiItemT], str | None] object_fn: Callable[[aiounifi.Controller, str], ApiItemT] - should_poll: bool supported_fn: Callable[[UnifiHub, str], bool | None] unique_id_fn: Callable[[UnifiHub, str], str] - -@dataclass(frozen=True) -class UnifiEntityDescription(EntityDescription, UnifiDescription[HandlerT, ApiItemT]): - """UniFi Entity Description.""" + # Optional + event_is_on: tuple[EventKey, ...] | None = None + """Which UniFi events should be used to consider state 'on'.""" + event_to_subscribe: tuple[EventKey, ...] | None = None + """Which UniFi events to listen on.""" + should_poll: bool = False + """If entity needs to do regular checks on state.""" class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]): diff --git a/homeassistant/components/unifi/image.py b/homeassistant/components/unifi/image.py index a070c158772..fb3cdf0b39e 100644 --- a/homeassistant/components/unifi/image.py +++ b/homeassistant/components/unifi/image.py @@ -6,7 +6,6 @@ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass -from typing import Generic from aiounifi.interfaces.api_handlers import ItemEvent from aiounifi.interfaces.wlans import Wlans @@ -36,23 +35,16 @@ def async_wlan_qr_code_image_fn(hub: UnifiHub, wlan: Wlan) -> bytes: return hub.api.wlans.generate_wlan_qr_code(wlan) -@dataclass(frozen=True) -class UnifiImageEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): - """Validate and load entities from different UniFi handlers.""" +@dataclass(frozen=True, kw_only=True) +class UnifiImageEntityDescription( + ImageEntityDescription, UnifiEntityDescription[HandlerT, ApiItemT] +): + """Class describing UniFi image entity.""" image_fn: Callable[[UnifiHub, ApiItemT], bytes] value_fn: Callable[[ApiItemT], str | None] -@dataclass(frozen=True) -class UnifiImageEntityDescription( - ImageEntityDescription, - UnifiEntityDescription[HandlerT, ApiItemT], - UnifiImageEntityDescriptionMixin[HandlerT, ApiItemT], -): - """Class describing UniFi image entity.""" - - ENTITY_DESCRIPTIONS: tuple[UnifiImageEntityDescription, ...] = ( UnifiImageEntityDescription[Wlans, Wlan]( key="WLAN QR Code", @@ -63,11 +55,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiImageEntityDescription, ...] = ( api_handler_fn=lambda api: api.wlans, available_fn=async_wlan_available_fn, device_info_fn=async_wlan_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda wlan: "QR Code", object_fn=lambda api, obj_id: api.wlans[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"qr_code-{obj_id}", image_fn=async_wlan_qr_code_image_fn, diff --git a/homeassistant/components/unifi/sensor.py b/homeassistant/components/unifi/sensor.py index ab76e662859..bf803b2c0ab 100644 --- a/homeassistant/components/unifi/sensor.py +++ b/homeassistant/components/unifi/sensor.py @@ -9,7 +9,6 @@ from collections.abc import Callable from dataclasses import dataclass from datetime import date, datetime, timedelta from decimal import Decimal -from typing import Generic from aiounifi.interfaces.api_handlers import ItemEvent from aiounifi.interfaces.clients import Clients @@ -154,33 +153,28 @@ def async_client_is_connected_fn(hub: UnifiHub, obj_id: str) -> bool: return True -@dataclass(frozen=True) -class UnifiSensorEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): - """Validate and load entities from different UniFi handlers.""" - - value_fn: Callable[[UnifiHub, ApiItemT], datetime | float | str | None] - - @callback def async_device_state_value_fn(hub: UnifiHub, device: Device) -> str: """Retrieve the state of the device.""" return DEVICE_STATES[device.state] -@dataclass(frozen=True) +@dataclass(frozen=True, kw_only=True) class UnifiSensorEntityDescription( - SensorEntityDescription, - UnifiEntityDescription[HandlerT, ApiItemT], - UnifiSensorEntityDescriptionMixin[HandlerT, ApiItemT], + SensorEntityDescription, UnifiEntityDescription[HandlerT, ApiItemT] ): """Class describing UniFi sensor entity.""" + value_fn: Callable[[UnifiHub, ApiItemT], datetime | float | str | None] + + # Optional is_connected_fn: Callable[[UnifiHub, str], bool] | None = None - # Custom function to determine whether a state change should be recorded + """Calculate if source is connected.""" value_changed_fn: Callable[ [StateType | date | datetime | Decimal, datetime | float | str | None], bool, ] = lambda old, new: old != new + """Calculate whether a state change should be recorded.""" ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( @@ -196,12 +190,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.clients, available_fn=lambda hub, _: hub.available, device_info_fn=async_client_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_connected_fn=async_client_is_connected_fn, name_fn=lambda _: "RX", object_fn=lambda api, obj_id: api.clients[obj_id], - should_poll=False, supported_fn=lambda hub, _: hub.option_allow_bandwidth_sensors, unique_id_fn=lambda hub, obj_id: f"rx-{obj_id}", value_fn=async_client_rx_value_fn, @@ -218,12 +209,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.clients, available_fn=lambda hub, _: hub.available, device_info_fn=async_client_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_connected_fn=async_client_is_connected_fn, name_fn=lambda _: "TX", object_fn=lambda api, obj_id: api.clients[obj_id], - should_poll=False, supported_fn=lambda hub, _: hub.option_allow_bandwidth_sensors, unique_id_fn=lambda hub, obj_id: f"tx-{obj_id}", value_fn=async_client_tx_value_fn, @@ -239,11 +227,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.ports, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda port: f"{port.name} PoE Power", object_fn=lambda api, obj_id: api.ports[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: hub.api.ports[obj_id].port_poe, unique_id_fn=lambda hub, obj_id: f"poe_power-{obj_id}", value_fn=lambda _, obj: obj.poe_power if obj.poe_mode != "off" else "0", @@ -258,11 +243,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.clients, available_fn=lambda hub, obj_id: hub.available, device_info_fn=async_client_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda client: "Uptime", object_fn=lambda api, obj_id: api.clients[obj_id], - should_poll=False, supported_fn=lambda hub, _: hub.option_allow_uptime_sensors, unique_id_fn=lambda hub, obj_id: f"uptime-{obj_id}", value_fn=async_client_uptime_value_fn, @@ -276,8 +258,6 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.wlans, available_fn=async_wlan_available_fn, device_info_fn=async_wlan_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda wlan: None, object_fn=lambda api, obj_id: api.wlans[obj_id], should_poll=True, @@ -295,8 +275,6 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.outlets, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda outlet: f"{outlet.name} Outlet Power", object_fn=lambda api, obj_id: api.outlets[obj_id], should_poll=True, @@ -315,11 +293,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda device: "AC Power Budget", object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=async_device_outlet_supported_fn, unique_id_fn=lambda hub, obj_id: f"ac_power_budget-{obj_id}", value_fn=lambda hub, device: device.outlet_ac_power_budget, @@ -335,11 +310,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda device: "AC Power Consumption", object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=async_device_outlet_supported_fn, unique_id_fn=lambda hub, obj_id: f"ac_power_conumption-{obj_id}", value_fn=lambda hub, device: device.outlet_ac_power_consumption, @@ -353,11 +325,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda device: "Uptime", object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"device_uptime-{obj_id}", value_fn=async_device_uptime_value_fn, @@ -373,11 +342,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda device: "Temperature", object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=lambda ctrlr, obj_id: ctrlr.api.devices[obj_id].has_temperature, unique_id_fn=lambda hub, obj_id: f"device_temperature-{obj_id}", value_fn=lambda ctrlr, device: device.general_temperature, @@ -391,11 +357,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = ( api_handler_fn=lambda api: api.devices, available_fn=async_device_available_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda device: "State", object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"device_state-{obj_id}", value_fn=async_device_state_value_fn, diff --git a/homeassistant/components/unifi/switch.py b/homeassistant/components/unifi/switch.py index 4a2785f0c17..6b2ce3c8b34 100644 --- a/homeassistant/components/unifi/switch.py +++ b/homeassistant/components/unifi/switch.py @@ -10,7 +10,7 @@ from __future__ import annotations import asyncio from collections.abc import Callable, Coroutine from dataclasses import dataclass -from typing import Any, Generic +from typing import Any import aiounifi from aiounifi.interfaces.api_handlers import ItemEvent @@ -162,24 +162,20 @@ async def async_wlan_control_fn(hub: UnifiHub, obj_id: str, target: bool) -> Non await hub.api.request(WlanEnableRequest.create(obj_id, target)) -@dataclass(frozen=True) -class UnifiSwitchEntityDescriptionMixin(Generic[HandlerT, ApiItemT]): - """Validate and load entities from different UniFi handlers.""" +@dataclass(frozen=True, kw_only=True) +class UnifiSwitchEntityDescription( + SwitchEntityDescription, UnifiEntityDescription[HandlerT, ApiItemT] +): + """Class describing UniFi switch entity.""" control_fn: Callable[[UnifiHub, str, bool], Coroutine[Any, Any, None]] is_on_fn: Callable[[UnifiHub, ApiItemT], bool] - -@dataclass(frozen=True) -class UnifiSwitchEntityDescription( - SwitchEntityDescription, - UnifiEntityDescription[HandlerT, ApiItemT], - UnifiSwitchEntityDescriptionMixin[HandlerT, ApiItemT], -): - """Class describing UniFi switch entity.""" - + # Optional custom_subscribe: Callable[[aiounifi.Controller], SubscriptionT] | None = None + """Callback for additional subscriptions to any UniFi handler.""" only_event_for_state_change: bool = False + """Use only UniFi events to trigger state changes.""" ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( @@ -200,7 +196,6 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( name_fn=lambda client: None, object_fn=lambda api, obj_id: api.clients[obj_id], only_event_for_state_change=True, - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"block-{obj_id}", ), @@ -214,12 +209,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( control_fn=async_dpi_group_control_fn, custom_subscribe=lambda api: api.dpi_apps.subscribe, device_info_fn=async_dpi_group_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_on_fn=async_dpi_group_is_on_fn, name_fn=lambda group: group.name, object_fn=lambda api, obj_id: api.dpi_groups[obj_id], - should_poll=False, supported_fn=lambda c, obj_id: bool(c.api.dpi_groups[obj_id].dpiapp_ids), unique_id_fn=lambda hub, obj_id: obj_id, ), @@ -232,12 +224,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( available_fn=async_device_available_fn, control_fn=async_outlet_control_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_on_fn=lambda hub, outlet: outlet.relay_state, name_fn=lambda outlet: outlet.name, object_fn=lambda api, obj_id: api.outlets[obj_id], - should_poll=False, supported_fn=async_outlet_supports_switching_fn, unique_id_fn=lambda hub, obj_id: f"outlet-{obj_id}", ), @@ -252,12 +241,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( available_fn=lambda hub, obj_id: hub.available, control_fn=async_port_forward_control_fn, device_info_fn=async_port_forward_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_on_fn=lambda hub, port_forward: port_forward.enabled, name_fn=lambda port_forward: f"{port_forward.name}", object_fn=lambda api, obj_id: api.port_forwarding[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"port_forward-{obj_id}", ), @@ -273,12 +259,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( available_fn=async_device_available_fn, control_fn=async_poe_port_control_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_on_fn=lambda hub, port: port.poe_mode != "off", name_fn=lambda port: f"{port.name} PoE", object_fn=lambda api, obj_id: api.ports[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: hub.api.ports[obj_id].port_poe, unique_id_fn=lambda hub, obj_id: f"poe-{obj_id}", ), @@ -293,12 +276,9 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = ( available_fn=lambda hub, _: hub.available, control_fn=async_wlan_control_fn, device_info_fn=async_wlan_device_info_fn, - event_is_on=None, - event_to_subscribe=None, is_on_fn=lambda hub, wlan: wlan.enabled, name_fn=lambda wlan: None, object_fn=lambda api, obj_id: api.wlans[obj_id], - should_poll=False, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"wlan-{obj_id}", ), @@ -354,15 +334,11 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity): """Base representation of a UniFi switch.""" entity_description: UnifiSwitchEntityDescription[HandlerT, ApiItemT] - only_event_for_state_change = False @callback def async_initiate_state(self) -> None: """Initiate entity state.""" - self.async_update_state(ItemEvent.ADDED, self._obj_id) - self.only_event_for_state_change = ( - self.entity_description.only_event_for_state_change - ) + self.async_update_state(ItemEvent.ADDED, self._obj_id, first_update=True) async def async_turn_on(self, **kwargs: Any) -> None: """Turn on switch.""" @@ -373,12 +349,14 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity): await self.entity_description.control_fn(self.hub, self._obj_id, False) @callback - def async_update_state(self, event: ItemEvent, obj_id: str) -> None: + def async_update_state( + self, event: ItemEvent, obj_id: str, first_update: bool = False + ) -> None: """Update entity state. Update attr_is_on. """ - if self.only_event_for_state_change: + if not first_update and self.entity_description.only_event_for_state_change: return description = self.entity_description diff --git a/homeassistant/components/unifi/update.py b/homeassistant/components/unifi/update.py index b7f33b632b3..bb9771c170a 100644 --- a/homeassistant/components/unifi/update.py +++ b/homeassistant/components/unifi/update.py @@ -4,7 +4,7 @@ from __future__ import annotations from collections.abc import Callable, Coroutine from dataclasses import dataclass import logging -from typing import Any, Generic, TypeVar +from typing import Any, TypeVar import aiounifi from aiounifi.interfaces.api_handlers import ItemEvent @@ -40,23 +40,16 @@ async def async_device_control_fn(api: aiounifi.Controller, obj_id: str) -> None await api.request(DeviceUpgradeRequest.create(obj_id)) -@dataclass(frozen=True) -class UnifiUpdateEntityDescriptionMixin(Generic[_HandlerT, _DataT]): - """Validate and load entities from different UniFi handlers.""" +@dataclass(frozen=True, kw_only=True) +class UnifiUpdateEntityDescription( + UpdateEntityDescription, UnifiEntityDescription[_HandlerT, _DataT] +): + """Class describing UniFi update entity.""" control_fn: Callable[[aiounifi.Controller, str], Coroutine[Any, Any, None]] state_fn: Callable[[aiounifi.Controller, _DataT], bool] -@dataclass(frozen=True) -class UnifiUpdateEntityDescription( - UpdateEntityDescription, - UnifiEntityDescription[_HandlerT, _DataT], - UnifiUpdateEntityDescriptionMixin[_HandlerT, _DataT], -): - """Class describing UniFi update entity.""" - - ENTITY_DESCRIPTIONS: tuple[UnifiUpdateEntityDescription, ...] = ( UnifiUpdateEntityDescription[Devices, Device]( key="Upgrade device", @@ -67,11 +60,8 @@ ENTITY_DESCRIPTIONS: tuple[UnifiUpdateEntityDescription, ...] = ( available_fn=async_device_available_fn, control_fn=async_device_control_fn, device_info_fn=async_device_device_info_fn, - event_is_on=None, - event_to_subscribe=None, name_fn=lambda device: None, object_fn=lambda api, obj_id: api.devices[obj_id], - should_poll=False, state_fn=lambda api, device: device.state == 4, supported_fn=lambda hub, obj_id: True, unique_id_fn=lambda hub, obj_id: f"device_update-{obj_id}",