Reject unifi uptime sensor updates if time delta is small (#108464)
* Reject unifi uptime sensor updates if time delta is small * Revise uptime change threshold tuning * Use StateType helper * Treat missing or zero uptime as None (unknown)
This commit is contained in:
parent
80bfd4cef7
commit
a9fe63ed90
1 changed files with 29 additions and 7 deletions
|
@ -7,7 +7,8 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timedelta
|
from datetime import date, datetime, timedelta
|
||||||
|
from decimal import Decimal
|
||||||
from typing import Generic
|
from typing import Generic
|
||||||
|
|
||||||
from aiounifi.interfaces.api_handlers import ItemEvent
|
from aiounifi.interfaces.api_handlers import ItemEvent
|
||||||
|
@ -35,6 +36,7 @@ from homeassistant.const import EntityCategory, UnitOfDataRate, UnitOfPower
|
||||||
from homeassistant.core import Event as core_Event, HomeAssistant, callback
|
from homeassistant.core import Event as core_Event, HomeAssistant, callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
from homeassistant.helpers.typing import StateType
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from .const import DEVICE_STATES
|
from .const import DEVICE_STATES
|
||||||
|
@ -110,11 +112,22 @@ def async_wlan_client_value_fn(controller: UniFiController, wlan: Wlan) -> int:
|
||||||
@callback
|
@callback
|
||||||
def async_device_uptime_value_fn(
|
def async_device_uptime_value_fn(
|
||||||
controller: UniFiController, device: Device
|
controller: UniFiController, device: Device
|
||||||
) -> datetime:
|
) -> datetime | None:
|
||||||
"""Calculate the uptime of the device."""
|
"""Calculate the approximate time the device started (based on uptime returned from API, in seconds)."""
|
||||||
return (dt_util.now() - timedelta(seconds=device.uptime)).replace(
|
if device.uptime <= 0:
|
||||||
second=0, microsecond=0
|
# Library defaults to 0 if uptime is not provided, e.g. when offline
|
||||||
)
|
return None
|
||||||
|
return (dt_util.now() - timedelta(seconds=device.uptime)).replace(microsecond=0)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_device_uptime_value_changed_fn(
|
||||||
|
old: StateType | date | datetime | Decimal, new: datetime | float | str | None
|
||||||
|
) -> bool:
|
||||||
|
"""Reject the new uptime value if it's too similar to the old one. Avoids unwanted fluctuation."""
|
||||||
|
if isinstance(old, datetime) and isinstance(new, datetime):
|
||||||
|
return new != old and abs((new - old).total_seconds()) > 120
|
||||||
|
return old is None or (new != old)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -169,6 +182,11 @@ class UnifiSensorEntityDescription(
|
||||||
"""Class describing UniFi sensor entity."""
|
"""Class describing UniFi sensor entity."""
|
||||||
|
|
||||||
is_connected_fn: Callable[[UniFiController, str], bool] | None = None
|
is_connected_fn: Callable[[UniFiController, str], bool] | None = None
|
||||||
|
# Custom function to determine whether a state change should be recorded
|
||||||
|
value_changed_fn: Callable[
|
||||||
|
[StateType | date | datetime | Decimal, datetime | float | str | None],
|
||||||
|
bool,
|
||||||
|
] = lambda old, new: old != new
|
||||||
|
|
||||||
|
|
||||||
ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
|
ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
|
||||||
|
@ -349,6 +367,7 @@ ENTITY_DESCRIPTIONS: tuple[UnifiSensorEntityDescription, ...] = (
|
||||||
supported_fn=lambda controller, obj_id: True,
|
supported_fn=lambda controller, obj_id: True,
|
||||||
unique_id_fn=lambda controller, obj_id: f"device_uptime-{obj_id}",
|
unique_id_fn=lambda controller, obj_id: f"device_uptime-{obj_id}",
|
||||||
value_fn=async_device_uptime_value_fn,
|
value_fn=async_device_uptime_value_fn,
|
||||||
|
value_changed_fn=async_device_uptime_value_changed_fn,
|
||||||
),
|
),
|
||||||
UnifiSensorEntityDescription[Devices, Device](
|
UnifiSensorEntityDescription[Devices, Device](
|
||||||
key="Device temperature",
|
key="Device temperature",
|
||||||
|
@ -425,7 +444,10 @@ class UnifiSensorEntity(UnifiEntity[HandlerT, ApiItemT], SensorEntity):
|
||||||
"""
|
"""
|
||||||
description = self.entity_description
|
description = self.entity_description
|
||||||
obj = description.object_fn(self.controller.api, self._obj_id)
|
obj = description.object_fn(self.controller.api, self._obj_id)
|
||||||
if (value := description.value_fn(self.controller, obj)) != self.native_value:
|
# Update the value only if value is considered to have changed relative to its previous state
|
||||||
|
if description.value_changed_fn(
|
||||||
|
self.native_value, (value := description.value_fn(self.controller, obj))
|
||||||
|
):
|
||||||
self._attr_native_value = value
|
self._attr_native_value = value
|
||||||
|
|
||||||
if description.is_connected_fn is not None:
|
if description.is_connected_fn is not None:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue