mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 07:07:28 +00:00
Rework state change detection for UniFi Protect entities (#107766)
This commit is contained in:
parent
335a1f6e09
commit
ec8a33b52d
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
NVR,
|
NVR,
|
||||||
@ -573,6 +574,16 @@ class ProtectDeviceBinarySensor(ProtectDeviceEntity, BinarySensorEntity):
|
|||||||
else:
|
else:
|
||||||
self._attr_device_class = self.entity_description.device_class
|
self._attr_device_class = self.entity_description.device_class
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (self._attr_available, self._attr_is_on, self._attr_device_class)
|
||||||
|
|
||||||
|
|
||||||
class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity):
|
class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity):
|
||||||
"""A UniFi Protect NVR Disk Binary Sensor."""
|
"""A UniFi Protect NVR Disk Binary Sensor."""
|
||||||
@ -617,6 +628,16 @@ class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity):
|
|||||||
|
|
||||||
self._attr_is_on = not self._disk.is_healthy
|
self._attr_is_on = not self._disk.is_healthy
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (self._attr_available, self._attr_is_on)
|
||||||
|
|
||||||
|
|
||||||
class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
|
class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
|
||||||
"""A UniFi Protect Device Binary Sensor for events."""
|
"""A UniFi Protect Device Binary Sensor for events."""
|
||||||
@ -633,32 +654,15 @@ class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
|
|||||||
self._attr_extra_state_attributes = {}
|
self._attr_extra_state_attributes = {}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the is_on, _attr_extra_state_attributes, and available are ever
|
Called before and after updating entity and state is only written if there
|
||||||
updated for these entities, and since the websocket update for the
|
is a change.
|
||||||
device will trigger an update for all entities connected to the device,
|
|
||||||
we want to avoid writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_is_on = self._attr_is_on
|
|
||||||
previous_available = self._attr_available
|
return (
|
||||||
previous_extra_state_attributes = self._attr_extra_state_attributes
|
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_is_on != previous_is_on
|
|
||||||
or self._attr_extra_state_attributes != previous_extra_state_attributes
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s (%s, %s) -> %s (%s, %s)",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_is_on,
|
|
||||||
previous_available,
|
|
||||||
previous_extra_state_attributes,
|
|
||||||
self._attr_is_on,
|
|
||||||
self._attr_available,
|
self._attr_available,
|
||||||
|
self._attr_is_on,
|
||||||
self._attr_extra_state_attributes,
|
self._attr_extra_state_attributes,
|
||||||
)
|
)
|
||||||
self.async_write_ha_state()
|
|
||||||
|
@ -193,24 +193,3 @@ class ProtectButton(ProtectDeviceEntity, ButtonEntity):
|
|||||||
|
|
||||||
if self.entity_description.ufp_press is not None:
|
if self.entity_description.ufp_press is not None:
|
||||||
await getattr(self.device, self.entity_description.ufp_press)()
|
await getattr(self.device, self.entity_description.ufp_press)()
|
||||||
|
|
||||||
@callback
|
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
|
||||||
|
|
||||||
Only available is updated for these entities, and since the websocket
|
|
||||||
update for the device will trigger an update for all entities connected
|
|
||||||
to the device, we want to avoid writing state unless something has
|
|
||||||
actually changed.
|
|
||||||
"""
|
|
||||||
previous_available = self._attr_available
|
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if self._attr_available != previous_available:
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s -> %s",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_available,
|
|
||||||
self._attr_available,
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from collections.abc import Generator
|
from collections.abc import Generator
|
||||||
import logging
|
import logging
|
||||||
from typing import cast
|
from typing import Any, cast
|
||||||
|
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
Camera as UFPCamera,
|
Camera as UFPCamera,
|
||||||
@ -181,6 +181,20 @@ class ProtectCamera(ProtectDeviceEntity, Camera):
|
|||||||
else:
|
else:
|
||||||
self._attr_supported_features = CameraEntityFeature(0)
|
self._attr_supported_features = CameraEntityFeature(0)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
self._attr_available,
|
||||||
|
self._attr_is_recording,
|
||||||
|
self._attr_motion_detection_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
||||||
super()._async_update_device_from_protect(device)
|
super()._async_update_device_from_protect(device)
|
||||||
|
@ -255,10 +255,36 @@ class ProtectDeviceEntity(Entity):
|
|||||||
and (not async_get_ufp_enabled or async_get_ufp_enabled(device))
|
and (not async_get_ufp_enabled or async_get_ufp_enabled(device))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (self._attr_available,)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
"""Call back for incoming data."""
|
"""When device is updated from Protect."""
|
||||||
|
|
||||||
|
previous_attrs = self._async_get_state_attrs()
|
||||||
self._async_update_device_from_protect(device)
|
self._async_update_device_from_protect(device)
|
||||||
|
current_attrs = self._async_get_state_attrs()
|
||||||
|
if previous_attrs != current_attrs:
|
||||||
|
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||||
|
device_name = device.name
|
||||||
|
if hasattr(self, "entity_description") and self.entity_description.name:
|
||||||
|
device_name += f" {self.entity_description.name}"
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Updating state [%s (%s)] %s -> %s",
|
||||||
|
device_name,
|
||||||
|
device.mac,
|
||||||
|
previous_attrs,
|
||||||
|
current_attrs,
|
||||||
|
)
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
|
@ -70,6 +70,16 @@ class ProtectLight(ProtectDeviceEntity, LightEntity):
|
|||||||
_attr_color_mode = ColorMode.BRIGHTNESS
|
_attr_color_mode = ColorMode.BRIGHTNESS
|
||||||
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
|
_attr_supported_color_modes = {ColorMode.BRIGHTNESS}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (self._attr_available, self._attr_brightness)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
||||||
super()._async_update_device_from_protect(device)
|
super()._async_update_device_from_protect(device)
|
||||||
|
@ -70,6 +70,22 @@ class ProtectLock(ProtectDeviceEntity, LockEntity):
|
|||||||
|
|
||||||
self._attr_name = f"{self.device.display_name} Lock"
|
self._attr_name = f"{self.device.display_name} Lock"
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
self._attr_available,
|
||||||
|
self._attr_is_locked,
|
||||||
|
self._attr_is_locking,
|
||||||
|
self._attr_is_unlocking,
|
||||||
|
self._attr_is_jammed,
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
||||||
super()._async_update_device_from_protect(device)
|
super()._async_update_device_from_protect(device)
|
||||||
|
@ -116,35 +116,14 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
|
|||||||
self._attr_available = is_connected and updated_device.feature_flags.has_speaker
|
self._attr_available = is_connected and updated_device.feature_flags.has_speaker
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the state, volume, and available are ever updated for these
|
Called before and after updating entity and state is only written if there
|
||||||
entities, and since the websocket update for the device will trigger
|
is a change.
|
||||||
an update for all entities connected to the device, we want to avoid
|
|
||||||
writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_state = self._attr_state
|
|
||||||
previous_available = self._attr_available
|
return (self._attr_available, self._attr_state, self._attr_volume_level)
|
||||||
previous_volume_level = self._attr_volume_level
|
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_state != previous_state
|
|
||||||
or self._attr_volume_level != previous_volume_level
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s (%s, %s) -> %s (%s, %s)",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_state,
|
|
||||||
previous_available,
|
|
||||||
previous_volume_level,
|
|
||||||
self._attr_state,
|
|
||||||
self._attr_available,
|
|
||||||
self._attr_volume_level,
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def async_set_volume_level(self, volume: float) -> None:
|
async def async_set_volume_level(self, volume: float) -> None:
|
||||||
"""Set volume level, range 0..1."""
|
"""Set volume level, range 0..1."""
|
||||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
Camera,
|
Camera,
|
||||||
@ -273,28 +274,11 @@ class ProtectNumbers(ProtectDeviceEntity, NumberEntity):
|
|||||||
await self.entity_description.ufp_set(self.device, value)
|
await self.entity_description.ufp_set(self.device, value)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the native value and available are ever updated for these
|
Called before and after updating entity and state is only written if there
|
||||||
entities, and since the websocket update for the device will trigger
|
is a change.
|
||||||
an update for all entities connected to the device, we want to avoid
|
|
||||||
writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_value = self._attr_native_value
|
|
||||||
previous_available = self._attr_available
|
return (self._attr_available, self._attr_native_value)
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_native_value != previous_value
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s (%s) -> %s (%s)",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_value,
|
|
||||||
previous_available,
|
|
||||||
self._attr_native_value,
|
|
||||||
self._attr_available,
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
@ -403,32 +403,11 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
|
|||||||
await self.entity_description.ufp_set(self.device, unifi_value)
|
await self.entity_description.ufp_set(self.device, unifi_value)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the options, option, and available are ever updated for these
|
Called before and after updating entity and state is only written if there
|
||||||
entities, and since the websocket update for the device will trigger
|
is a change.
|
||||||
an update for all entities connected to the device, we want to avoid
|
|
||||||
writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_option = self._attr_current_option
|
|
||||||
previous_options = self._attr_options
|
return (self._attr_available, self._attr_options, self._attr_current_option)
|
||||||
previous_available = self._attr_available
|
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_current_option != previous_option
|
|
||||||
or self._attr_options != previous_options
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s (%s, %s) -> %s (%s, %s)",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_option,
|
|
||||||
previous_available,
|
|
||||||
previous_options,
|
|
||||||
self._attr_current_option,
|
|
||||||
self._attr_available,
|
|
||||||
self._attr_options,
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
@ -715,31 +715,14 @@ class ProtectDeviceSensor(ProtectDeviceEntity, SensorEntity):
|
|||||||
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
|
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the native value and available are ever updated for these
|
Called before and after updating entity and state is only written if there
|
||||||
entities, and since the websocket update for the device will trigger
|
is a change.
|
||||||
an update for all entities connected to the device, we want to avoid
|
|
||||||
writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_value = self._attr_native_value
|
|
||||||
previous_available = self._attr_available
|
return (self._attr_available, self._attr_native_value)
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_native_value != previous_value
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s (%s) -> %s (%s)",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_value,
|
|
||||||
previous_available,
|
|
||||||
self._attr_native_value,
|
|
||||||
self._attr_available,
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectNVRSensor(ProtectNVREntity, SensorEntity):
|
class ProtectNVRSensor(ProtectNVREntity, SensorEntity):
|
||||||
@ -752,22 +735,14 @@ class ProtectNVRSensor(ProtectNVREntity, SensorEntity):
|
|||||||
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
|
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the native value and available are ever updated for these
|
Called before and after updating entity and state is only written if there
|
||||||
entities, and since the websocket update for the device will trigger
|
is a change.
|
||||||
an update for all entities connected to the device, we want to avoid
|
|
||||||
writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_value = self._attr_native_value
|
|
||||||
previous_available = self._attr_available
|
return (self._attr_available, self._attr_native_value)
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_native_value != previous_value
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectEventSensor(EventEntityMixin, SensorEntity):
|
class ProtectEventSensor(EventEntityMixin, SensorEntity):
|
||||||
@ -803,3 +778,17 @@ class ProtectEventSensor(EventEntityMixin, SensorEntity):
|
|||||||
self._attr_native_value = event.metadata.license_plate.name # type: ignore[union-attr]
|
self._attr_native_value = event.metadata.license_plate.name # type: ignore[union-attr]
|
||||||
else:
|
else:
|
||||||
self._attr_native_value = event.smart_detect_types[0].value
|
self._attr_native_value = event.smart_detect_types[0].value
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (
|
||||||
|
self._attr_available,
|
||||||
|
self._attr_native_value,
|
||||||
|
self._attr_extra_state_attributes,
|
||||||
|
)
|
||||||
|
@ -445,31 +445,14 @@ class ProtectSwitch(ProtectDeviceEntity, SwitchEntity):
|
|||||||
await self.entity_description.ufp_set(self.device, False)
|
await self.entity_description.ufp_set(self.device, False)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
"""Call back for incoming data that only writes when state has changed.
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
Only the is_on and available are ever updated for these
|
Called before and after updating entity and state is only written if there
|
||||||
entities, and since the websocket update for the device will trigger
|
is a change.
|
||||||
an update for all entities connected to the device, we want to avoid
|
|
||||||
writing state unless something has actually changed.
|
|
||||||
"""
|
"""
|
||||||
previous_is_on = self._attr_is_on
|
|
||||||
previous_available = self._attr_available
|
return (self._attr_available, self._attr_is_on)
|
||||||
self._async_update_device_from_protect(device)
|
|
||||||
if (
|
|
||||||
self._attr_is_on != previous_is_on
|
|
||||||
or self._attr_available != previous_available
|
|
||||||
):
|
|
||||||
_LOGGER.debug(
|
|
||||||
"Updating state [%s (%s)] %s (%s) -> %s (%s)",
|
|
||||||
device.name,
|
|
||||||
device.mac,
|
|
||||||
previous_is_on,
|
|
||||||
previous_available,
|
|
||||||
self._attr_is_on,
|
|
||||||
self._attr_available,
|
|
||||||
)
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectNVRSwitch(ProtectNVREntity, SwitchEntity):
|
class ProtectNVRSwitch(ProtectNVREntity, SwitchEntity):
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
Camera,
|
Camera,
|
||||||
@ -101,6 +102,16 @@ class ProtectDeviceText(ProtectDeviceEntity, TextEntity):
|
|||||||
super()._async_update_device_from_protect(device)
|
super()._async_update_device_from_protect(device)
|
||||||
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
|
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_get_state_attrs(self) -> tuple[Any, ...]:
|
||||||
|
"""Retrieve data that goes into the current state of the entity.
|
||||||
|
|
||||||
|
Called before and after updating entity and state is only written if there
|
||||||
|
is a change.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return (self._attr_available, self._attr_native_value)
|
||||||
|
|
||||||
async def async_set_value(self, value: str) -> None:
|
async def async_set_value(self, value: str) -> None:
|
||||||
"""Change the value."""
|
"""Change the value."""
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ async def test_binary_sensor_update_mount_type_garage(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test binary_sensor motion entity."""
|
"""Test binary_sensor motion entity."""
|
||||||
|
|
||||||
await init_entry(hass, ufp, [sensor_all])
|
await init_entry(hass, ufp, [sensor_all], debug=True)
|
||||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 11, 11)
|
assert_entity_counts(hass, Platform.BINARY_SENSOR, 11, 11)
|
||||||
|
|
||||||
_, entity_id = ids_from_device_description(
|
_, entity_id = ids_from_device_description(
|
||||||
|
@ -25,6 +25,7 @@ from homeassistant.const import Platform
|
|||||||
from homeassistant.core import HomeAssistant, split_entity_id
|
from homeassistant.core import HomeAssistant, split_entity_id
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.helpers.entity import EntityDescription
|
from homeassistant.helpers.entity import EntityDescription
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
@ -161,6 +162,7 @@ async def init_entry(
|
|||||||
ufp: MockUFPFixture,
|
ufp: MockUFPFixture,
|
||||||
devices: Sequence[ProtectAdoptableDeviceModel],
|
devices: Sequence[ProtectAdoptableDeviceModel],
|
||||||
regenerate_ids: bool = True,
|
regenerate_ids: bool = True,
|
||||||
|
debug: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize Protect entry with given devices."""
|
"""Initialize Protect entry with given devices."""
|
||||||
|
|
||||||
@ -168,6 +170,14 @@ async def init_entry(
|
|||||||
for device in devices:
|
for device in devices:
|
||||||
add_device(ufp.api.bootstrap, device, regenerate_ids)
|
add_device(ufp.api.bootstrap, device, regenerate_ids)
|
||||||
|
|
||||||
|
if debug:
|
||||||
|
assert await async_setup_component(hass, "logger", {"logger": {}})
|
||||||
|
await hass.services.async_call(
|
||||||
|
"logger",
|
||||||
|
"set_level",
|
||||||
|
{"homeassistant.components.unifiprotect": "DEBUG"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user