Compare commits
3 commits
dev
...
unifi_comb
Author | SHA1 | Date | |
---|---|---|---|
|
12fa24b756 | ||
|
cf7d63b5c5 | ||
|
6d600423cf |
7 changed files with 44 additions and 38 deletions
|
@ -149,7 +149,7 @@ class UnifiButtonEntity(UnifiEntity[HandlerT, ApiItemT], ButtonEntity):
|
||||||
|
|
||||||
async def async_press(self) -> None:
|
async def async_press(self) -> None:
|
||||||
"""Press the button."""
|
"""Press the button."""
|
||||||
await self.entity_description.control_fn(self.hub.api, self._obj_id)
|
await self.entity_description.control_fn(self.api, self._obj_id)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
||||||
|
|
|
@ -5,6 +5,7 @@ from __future__ import annotations
|
||||||
from collections.abc import Callable, Mapping
|
from collections.abc import Callable, Mapping
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
from functools import cached_property
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
@ -152,7 +153,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiTrackerEntityDescription, ...] = (
|
||||||
allowed_fn=async_client_allowed_fn,
|
allowed_fn=async_client_allowed_fn,
|
||||||
api_handler_fn=lambda api: api.clients,
|
api_handler_fn=lambda api: api.clients,
|
||||||
device_info_fn=lambda api, obj_id: None,
|
device_info_fn=lambda api, obj_id: None,
|
||||||
event_is_on=(WIRED_CONNECTION + WIRELESS_CONNECTION),
|
event_is_on=set(WIRED_CONNECTION + WIRELESS_CONNECTION),
|
||||||
event_to_subscribe=(
|
event_to_subscribe=(
|
||||||
WIRED_CONNECTION
|
WIRED_CONNECTION
|
||||||
+ WIRED_DISCONNECTION
|
+ WIRED_DISCONNECTION
|
||||||
|
@ -225,7 +226,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity):
|
||||||
|
|
||||||
entity_description: UnifiTrackerEntityDescription
|
entity_description: UnifiTrackerEntityDescription
|
||||||
|
|
||||||
_event_is_on: tuple[EventKey, ...]
|
_event_is_on: set[EventKey]
|
||||||
_ignore_events: bool
|
_ignore_events: bool
|
||||||
_is_connected: bool
|
_is_connected: bool
|
||||||
|
|
||||||
|
@ -236,7 +237,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity):
|
||||||
Initiate is_connected.
|
Initiate is_connected.
|
||||||
"""
|
"""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
self._event_is_on = description.event_is_on or ()
|
self._event_is_on = description.event_is_on or set()
|
||||||
self._ignore_events = False
|
self._ignore_events = False
|
||||||
self._is_connected = description.is_connected_fn(self.hub, self._obj_id)
|
self._is_connected = description.is_connected_fn(self.hub, self._obj_id)
|
||||||
if self.is_connected:
|
if self.is_connected:
|
||||||
|
@ -254,24 +255,24 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity):
|
||||||
@property
|
@property
|
||||||
def hostname(self) -> str | None:
|
def hostname(self) -> str | None:
|
||||||
"""Return hostname of the device."""
|
"""Return hostname of the device."""
|
||||||
return self.entity_description.hostname_fn(self.hub.api, self._obj_id)
|
return self.entity_description.hostname_fn(self.api, self._obj_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ip_address(self) -> str | None:
|
def ip_address(self) -> str | None:
|
||||||
"""Return the primary ip address of the device."""
|
"""Return the primary ip address of the device."""
|
||||||
return self.entity_description.ip_address_fn(self.hub.api, self._obj_id)
|
return self.entity_description.ip_address_fn(self.api, self._obj_id)
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def mac_address(self) -> str:
|
def mac_address(self) -> str:
|
||||||
"""Return the mac address of the device."""
|
"""Return the mac address of the device."""
|
||||||
return self._obj_id
|
return self._obj_id
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def source_type(self) -> SourceType:
|
def source_type(self) -> SourceType:
|
||||||
"""Return the source type, eg gps or router, of the device."""
|
"""Return the source type, eg gps or router, of the device."""
|
||||||
return SourceType.ROUTER
|
return SourceType.ROUTER
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def unique_id(self) -> str:
|
def unique_id(self) -> str:
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
return self._attr_unique_id
|
return self._attr_unique_id
|
||||||
|
@ -292,42 +293,45 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity):
|
||||||
Schedule new heartbeat check if connected.
|
Schedule new heartbeat check if connected.
|
||||||
"""
|
"""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
|
hub = self.hub
|
||||||
|
|
||||||
if event == ItemEvent.CHANGED:
|
if event is ItemEvent.CHANGED:
|
||||||
# Prioritize normal data updates over events
|
# Prioritize normal data updates over events
|
||||||
self._ignore_events = True
|
self._ignore_events = True
|
||||||
|
|
||||||
elif event == ItemEvent.ADDED and not self.available:
|
elif event is ItemEvent.ADDED and not self.available:
|
||||||
# From unifi.entity.async_signal_reachable_callback
|
# From unifi.entity.async_signal_reachable_callback
|
||||||
# Controller connection state has changed and entity is unavailable
|
# Controller connection state has changed and entity is unavailable
|
||||||
# Cancel heartbeat
|
# Cancel heartbeat
|
||||||
self.hub.remove_heartbeat(self.unique_id)
|
hub.remove_heartbeat(self.unique_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
if is_connected := description.is_connected_fn(self.hub, self._obj_id):
|
obj_id = self._obj_id
|
||||||
|
if is_connected := description.is_connected_fn(hub, obj_id):
|
||||||
self._is_connected = is_connected
|
self._is_connected = is_connected
|
||||||
self.hub.update_heartbeat(
|
self.hub.update_heartbeat(
|
||||||
self.unique_id,
|
self.unique_id,
|
||||||
dt_util.utcnow()
|
dt_util.utcnow() + description.heartbeat_timedelta_fn(hub, obj_id),
|
||||||
+ description.heartbeat_timedelta_fn(self.hub, self._obj_id),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_event_callback(self, event: Event) -> None:
|
def async_event_callback(self, event: Event) -> None:
|
||||||
"""Event subscription callback."""
|
"""Event subscription callback."""
|
||||||
if event.mac != self._obj_id or self._ignore_events:
|
obj_id = self._obj_id
|
||||||
|
if event.mac != obj_id or self._ignore_events:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
hub = self.hub
|
||||||
if event.key in self._event_is_on:
|
if event.key in self._event_is_on:
|
||||||
self.hub.remove_heartbeat(self.unique_id)
|
hub.remove_heartbeat(self.unique_id)
|
||||||
self._is_connected = True
|
self._is_connected = True
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.hub.update_heartbeat(
|
hub.update_heartbeat(
|
||||||
self.unique_id,
|
self.unique_id,
|
||||||
dt_util.utcnow()
|
dt_util.utcnow()
|
||||||
+ self.entity_description.heartbeat_timedelta_fn(self.hub, self._obj_id),
|
+ self.entity_description.heartbeat_timedelta_fn(hub, obj_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
|
@ -352,7 +356,7 @@ class UnifiScannerEntity(UnifiEntity[HandlerT, ApiItemT], ScannerEntity):
|
||||||
if self.entity_description.key != "Client device scanner":
|
if self.entity_description.key != "Client device scanner":
|
||||||
return None
|
return None
|
||||||
|
|
||||||
client = self.entity_description.object_fn(self.hub.api, self._obj_id)
|
client = self.entity_description.object_fn(self.api, self._obj_id)
|
||||||
raw = client.raw
|
raw = client.raw
|
||||||
|
|
||||||
attributes_to_check = CLIENT_STATIC_ATTRIBUTES
|
attributes_to_check = CLIENT_STATIC_ATTRIBUTES
|
||||||
|
|
|
@ -120,7 +120,7 @@ class UnifiEntityDescription(EntityDescription, Generic[HandlerT, ApiItemT]):
|
||||||
# Optional constants
|
# Optional constants
|
||||||
has_entity_name = True # Part of EntityDescription
|
has_entity_name = True # Part of EntityDescription
|
||||||
"""Has entity name defaults to true."""
|
"""Has entity name defaults to true."""
|
||||||
event_is_on: tuple[EventKey, ...] | None = None
|
event_is_on: set[EventKey] | None = None
|
||||||
"""Which UniFi events should be used to consider state 'on'."""
|
"""Which UniFi events should be used to consider state 'on'."""
|
||||||
event_to_subscribe: tuple[EventKey, ...] | None = None
|
event_to_subscribe: tuple[EventKey, ...] | None = None
|
||||||
"""Which UniFi events to listen on."""
|
"""Which UniFi events to listen on."""
|
||||||
|
@ -143,6 +143,7 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]):
|
||||||
"""Set up UniFi switch entity."""
|
"""Set up UniFi switch entity."""
|
||||||
self._obj_id = obj_id
|
self._obj_id = obj_id
|
||||||
self.hub = hub
|
self.hub = hub
|
||||||
|
self.api = hub.api
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
|
|
||||||
hub.entity_loader.known_objects.add((description.key, obj_id))
|
hub.entity_loader.known_objects.add((description.key, obj_id))
|
||||||
|
@ -154,14 +155,14 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]):
|
||||||
self._attr_should_poll = description.should_poll
|
self._attr_should_poll = description.should_poll
|
||||||
self._attr_unique_id = description.unique_id_fn(hub, obj_id)
|
self._attr_unique_id = description.unique_id_fn(hub, obj_id)
|
||||||
|
|
||||||
obj = description.object_fn(self.hub.api, obj_id)
|
obj = description.object_fn(self.api, obj_id)
|
||||||
self._attr_name = description.name_fn(obj)
|
self._attr_name = description.name_fn(obj)
|
||||||
self.async_initiate_state()
|
self.async_initiate_state()
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Register callbacks."""
|
"""Register callbacks."""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
handler = description.api_handler_fn(self.hub.api)
|
handler = description.api_handler_fn(self.api)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def unregister_object() -> None:
|
def unregister_object() -> None:
|
||||||
|
@ -201,7 +202,7 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]):
|
||||||
# Subscribe to events if defined
|
# Subscribe to events if defined
|
||||||
if description.event_to_subscribe is not None:
|
if description.event_to_subscribe is not None:
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
self.hub.api.events.subscribe(
|
self.api.events.subscribe(
|
||||||
self.async_event_callback,
|
self.async_event_callback,
|
||||||
description.event_to_subscribe,
|
description.event_to_subscribe,
|
||||||
)
|
)
|
||||||
|
@ -210,8 +211,8 @@ class UnifiEntity(Entity, Generic[HandlerT, ApiItemT]):
|
||||||
@callback
|
@callback
|
||||||
def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None:
|
def async_signalling_callback(self, event: ItemEvent, obj_id: str) -> None:
|
||||||
"""Update the entity state."""
|
"""Update the entity state."""
|
||||||
if event == ItemEvent.DELETED and obj_id == self._obj_id:
|
if event is ItemEvent.DELETED and obj_id == self._obj_id:
|
||||||
self.hass.async_create_task(self.remove_item({self._obj_id}))
|
self.hass.async_create_task(self.remove_item({obj_id}))
|
||||||
return
|
return
|
||||||
|
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
|
|
|
@ -97,7 +97,7 @@ class UnifiImageEntity(UnifiEntity[HandlerT, ApiItemT], ImageEntity):
|
||||||
"""Return bytes of image."""
|
"""Return bytes of image."""
|
||||||
if self.current_image is None:
|
if self.current_image is None:
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
obj = description.object_fn(self.hub.api, self._obj_id)
|
obj = description.object_fn(self.api, self._obj_id)
|
||||||
self.current_image = description.image_fn(self.hub, obj)
|
self.current_image = description.image_fn(self.hub, obj)
|
||||||
return self.current_image
|
return self.current_image
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ class UnifiImageEntity(UnifiEntity[HandlerT, ApiItemT], ImageEntity):
|
||||||
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
||||||
"""Update entity state."""
|
"""Update entity state."""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
obj = description.object_fn(self.hub.api, self._obj_id)
|
obj = description.object_fn(self.api, self._obj_id)
|
||||||
if (value := description.value_fn(obj)) != self.previous_value:
|
if (value := description.value_fn(obj)) != self.previous_value:
|
||||||
self.previous_value = value
|
self.previous_value = value
|
||||||
self.current_image = None
|
self.current_image = None
|
||||||
|
|
|
@ -490,7 +490,7 @@ class UnifiSensorEntity(UnifiEntity[HandlerT, ApiItemT], SensorEntity):
|
||||||
Update native_value.
|
Update native_value.
|
||||||
"""
|
"""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
obj = description.object_fn(self.hub.api, self._obj_id)
|
obj = description.object_fn(self.api, self._obj_id)
|
||||||
# Update the value only if value is considered to have changed relative to its previous state
|
# Update the value only if value is considered to have changed relative to its previous state
|
||||||
if description.value_changed_fn(
|
if description.value_changed_fn(
|
||||||
self.native_value, (value := description.value_fn(self.hub, obj))
|
self.native_value, (value := description.value_fn(self.hub, obj))
|
||||||
|
|
|
@ -11,7 +11,7 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
import aiounifi
|
import aiounifi
|
||||||
from aiounifi.interfaces.api_handlers import ItemEvent
|
from aiounifi.interfaces.api_handlers import ItemEvent
|
||||||
|
@ -189,7 +189,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSwitchEntityDescription, ...] = (
|
||||||
api_handler_fn=lambda api: api.clients,
|
api_handler_fn=lambda api: api.clients,
|
||||||
control_fn=async_block_client_control_fn,
|
control_fn=async_block_client_control_fn,
|
||||||
device_info_fn=async_client_device_info_fn,
|
device_info_fn=async_client_device_info_fn,
|
||||||
event_is_on=CLIENT_UNBLOCKED,
|
event_is_on=set(CLIENT_UNBLOCKED),
|
||||||
event_to_subscribe=CLIENT_BLOCKED + CLIENT_UNBLOCKED,
|
event_to_subscribe=CLIENT_BLOCKED + CLIENT_UNBLOCKED,
|
||||||
is_on_fn=lambda hub, client: not client.blocked,
|
is_on_fn=lambda hub, client: not client.blocked,
|
||||||
object_fn=lambda api, obj_id: api.clients[obj_id],
|
object_fn=lambda api, obj_id: api.clients[obj_id],
|
||||||
|
@ -342,7 +342,7 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity):
|
||||||
return
|
return
|
||||||
|
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
obj = description.object_fn(self.hub.api, self._obj_id)
|
obj = description.object_fn(self.api, self._obj_id)
|
||||||
if (is_on := description.is_on_fn(self.hub, obj)) != self.is_on:
|
if (is_on := description.is_on_fn(self.hub, obj)) != self.is_on:
|
||||||
self._attr_is_on = is_on
|
self._attr_is_on = is_on
|
||||||
|
|
||||||
|
@ -353,8 +353,9 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity):
|
||||||
return
|
return
|
||||||
|
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
assert isinstance(description.event_to_subscribe, tuple)
|
if TYPE_CHECKING:
|
||||||
assert isinstance(description.event_is_on, tuple)
|
assert description.event_to_subscribe is not None
|
||||||
|
assert description.event_is_on is not None
|
||||||
|
|
||||||
if event.key in description.event_to_subscribe:
|
if event.key in description.event_to_subscribe:
|
||||||
self._attr_is_on = event.key in description.event_is_on
|
self._attr_is_on = event.key in description.event_is_on
|
||||||
|
@ -367,7 +368,7 @@ class UnifiSwitchEntity(UnifiEntity[HandlerT, ApiItemT], SwitchEntity):
|
||||||
|
|
||||||
if self.entity_description.custom_subscribe is not None:
|
if self.entity_description.custom_subscribe is not None:
|
||||||
self.async_on_remove(
|
self.async_on_remove(
|
||||||
self.entity_description.custom_subscribe(self.hub.api)(
|
self.entity_description.custom_subscribe(self.api)(
|
||||||
self.async_signalling_callback, ItemEvent.CHANGED
|
self.async_signalling_callback, ItemEvent.CHANGED
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
@ -96,7 +96,7 @@ class UnifiDeviceUpdateEntity(UnifiEntity[_HandlerT, _DataT], UpdateEntity):
|
||||||
self, version: str | None, backup: bool, **kwargs: Any
|
self, version: str | None, backup: bool, **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Install an update."""
|
"""Install an update."""
|
||||||
await self.entity_description.control_fn(self.hub.api, self._obj_id)
|
await self.entity_description.control_fn(self.api, self._obj_id)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
def async_update_state(self, event: ItemEvent, obj_id: str) -> None:
|
||||||
|
@ -106,7 +106,7 @@ class UnifiDeviceUpdateEntity(UnifiEntity[_HandlerT, _DataT], UpdateEntity):
|
||||||
"""
|
"""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
|
|
||||||
obj = description.object_fn(self.hub.api, self._obj_id)
|
obj = description.object_fn(self.api, self._obj_id)
|
||||||
self._attr_in_progress = description.state_fn(self.hub.api, obj)
|
self._attr_in_progress = description.state_fn(self.api, obj)
|
||||||
self._attr_installed_version = obj.version
|
self._attr_installed_version = obj.version
|
||||||
self._attr_latest_version = obj.upgrade_to_firmware or obj.version
|
self._attr_latest_version = obj.upgrade_to_firmware or obj.version
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue