Avoid writing unifiprotect state when nothing has changed (#100439)
This commit is contained in:
parent
7b71d27637
commit
8a98a0e830
7 changed files with 163 additions and 25 deletions
|
@ -621,3 +621,23 @@ class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
|
||||||
if not is_on:
|
if not is_on:
|
||||||
self._event = None
|
self._event = None
|
||||||
self._attr_extra_state_attributes = {}
|
self._attr_extra_state_attributes = {}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the is_on, _attr_extra_state_attributes, and available are ever
|
||||||
|
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_is_on = self._attr_is_on
|
||||||
|
previous_available = self._attr_available
|
||||||
|
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
|
||||||
|
):
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -193,3 +193,17 @@ 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:
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -115,6 +115,26 @@ 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
|
||||||
|
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the state, volume, and available are ever 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_state = self._attr_state
|
||||||
|
previous_available = self._attr_available
|
||||||
|
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
|
||||||
|
):
|
||||||
|
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."""
|
||||||
|
|
||||||
|
|
|
@ -268,3 +268,21 @@ class ProtectNumbers(ProtectDeviceEntity, NumberEntity):
|
||||||
async def async_set_native_value(self, value: float) -> None:
|
async def async_set_native_value(self, value: float) -> None:
|
||||||
"""Set new value."""
|
"""Set new value."""
|
||||||
await self.entity_description.ufp_set(self.device, value)
|
await self.entity_description.ufp_set(self.device, value)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the native value and available are ever 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_value = self._attr_native_value
|
||||||
|
previous_available = self._attr_available
|
||||||
|
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()
|
||||||
|
|
|
@ -349,9 +349,9 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
|
||||||
description: ProtectSelectEntityDescription,
|
description: ProtectSelectEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the unifi protect select entity."""
|
"""Initialize the unifi protect select entity."""
|
||||||
|
self._async_set_options(data, description)
|
||||||
super().__init__(data, device, description)
|
super().__init__(data, device, description)
|
||||||
self._attr_name = f"{self.device.display_name} {self.entity_description.name}"
|
self._attr_name = f"{self.device.display_name} {self.entity_description.name}"
|
||||||
self._async_set_options()
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
||||||
|
@ -366,31 +366,28 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Updating dynamic select options for %s", entity_description.name
|
"Updating dynamic select options for %s", entity_description.name
|
||||||
)
|
)
|
||||||
self._async_set_options()
|
self._async_set_options(self.data, entity_description)
|
||||||
|
if (unifi_value := entity_description.get_ufp_value(device)) is None:
|
||||||
|
unifi_value = TYPE_EMPTY_VALUE
|
||||||
|
self._attr_current_option = self._unifi_to_hass_options.get(
|
||||||
|
unifi_value, unifi_value
|
||||||
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_set_options(self) -> None:
|
def _async_set_options(
|
||||||
|
self, data: ProtectData, description: ProtectSelectEntityDescription
|
||||||
|
) -> None:
|
||||||
"""Set options attributes from UniFi Protect device."""
|
"""Set options attributes from UniFi Protect device."""
|
||||||
|
if (ufp_options := description.ufp_options) is not None:
|
||||||
if self.entity_description.ufp_options is not None:
|
options = ufp_options
|
||||||
options = self.entity_description.ufp_options
|
|
||||||
else:
|
else:
|
||||||
assert self.entity_description.ufp_options_fn is not None
|
assert description.ufp_options_fn is not None
|
||||||
options = self.entity_description.ufp_options_fn(self.data.api)
|
options = description.ufp_options_fn(data.api)
|
||||||
|
|
||||||
self._attr_options = [item["name"] for item in options]
|
self._attr_options = [item["name"] for item in options]
|
||||||
self._hass_to_unifi_options = {item["name"]: item["id"] for item in options}
|
self._hass_to_unifi_options = {item["name"]: item["id"] for item in options}
|
||||||
self._unifi_to_hass_options = {item["id"]: item["name"] for item in options}
|
self._unifi_to_hass_options = {item["id"]: item["name"] for item in options}
|
||||||
|
|
||||||
@property
|
|
||||||
def current_option(self) -> str:
|
|
||||||
"""Return the current selected option."""
|
|
||||||
|
|
||||||
unifi_value = self.entity_description.get_ufp_value(self.device)
|
|
||||||
if unifi_value is None:
|
|
||||||
unifi_value = TYPE_EMPTY_VALUE
|
|
||||||
return self._unifi_to_hass_options.get(unifi_value, unifi_value)
|
|
||||||
|
|
||||||
async def async_select_option(self, option: str) -> None:
|
async def async_select_option(self, option: str) -> None:
|
||||||
"""Change the Select Entity Option."""
|
"""Change the Select Entity Option."""
|
||||||
|
|
||||||
|
@ -404,3 +401,23 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
|
||||||
if self.entity_description.ufp_enum_type is not None:
|
if self.entity_description.ufp_enum_type is not None:
|
||||||
unifi_value = self.entity_description.ufp_enum_type(unifi_value)
|
unifi_value = self.entity_description.ufp_enum_type(unifi_value)
|
||||||
await self.entity_description.ufp_set(self.device, unifi_value)
|
await self.entity_description.ufp_set(self.device, unifi_value)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the options, option, and available are ever 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_option = self._attr_current_option
|
||||||
|
previous_options = self._attr_options
|
||||||
|
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
|
||||||
|
):
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
|
@ -710,22 +710,56 @@ class ProtectDeviceSensor(ProtectDeviceEntity, SensorEntity):
|
||||||
|
|
||||||
entity_description: ProtectSensorEntityDescription
|
entity_description: ProtectSensorEntityDescription
|
||||||
|
|
||||||
@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)
|
||||||
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_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the native value and available are ever 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_value = self._attr_native_value
|
||||||
|
previous_available = self._attr_available
|
||||||
|
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 ProtectNVRSensor(ProtectNVREntity, SensorEntity):
|
class ProtectNVRSensor(ProtectNVREntity, SensorEntity):
|
||||||
"""A Ubiquiti UniFi Protect Sensor."""
|
"""A Ubiquiti UniFi Protect Sensor."""
|
||||||
|
|
||||||
entity_description: ProtectSensorEntityDescription
|
entity_description: ProtectSensorEntityDescription
|
||||||
|
|
||||||
@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)
|
||||||
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_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the native value and available are ever 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_value = self._attr_native_value
|
||||||
|
previous_available = self._attr_available
|
||||||
|
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):
|
||||||
"""A UniFi Protect Device Sensor with access tokens."""
|
"""A UniFi Protect Device Sensor with access tokens."""
|
||||||
|
|
|
@ -420,21 +420,36 @@ class ProtectSwitch(ProtectDeviceEntity, SwitchEntity):
|
||||||
self._attr_name = f"{self.device.display_name} {self.entity_description.name}"
|
self._attr_name = f"{self.device.display_name} {self.entity_description.name}"
|
||||||
self._switch_type = self.entity_description.key
|
self._switch_type = self.entity_description.key
|
||||||
|
|
||||||
@property
|
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
||||||
def is_on(self) -> bool:
|
super()._async_update_device_from_protect(device)
|
||||||
"""Return true if device is on."""
|
self._attr_is_on = self.entity_description.get_ufp_value(self.device) is True
|
||||||
return self.entity_description.get_ufp_value(self.device) is True
|
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn the device on."""
|
"""Turn the device on."""
|
||||||
|
|
||||||
await self.entity_description.ufp_set(self.device, True)
|
await self.entity_description.ufp_set(self.device, True)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn the device off."""
|
"""Turn the device off."""
|
||||||
|
|
||||||
await self.entity_description.ufp_set(self.device, False)
|
await self.entity_description.ufp_set(self.device, False)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_updated_event(self, device: ProtectModelWithId) -> None:
|
||||||
|
"""Call back for incoming data that only writes when state has changed.
|
||||||
|
|
||||||
|
Only the is_on and available are ever 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_is_on = self._attr_is_on
|
||||||
|
previous_available = self._attr_available
|
||||||
|
self._async_update_device_from_protect(device)
|
||||||
|
if (
|
||||||
|
self._attr_is_on != previous_is_on
|
||||||
|
or self._attr_available != previous_available
|
||||||
|
):
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
class ProtectNVRSwitch(ProtectNVREntity, SwitchEntity):
|
class ProtectNVRSwitch(ProtectNVREntity, SwitchEntity):
|
||||||
"""A UniFi Protect NVR Switch."""
|
"""A UniFi Protect NVR Switch."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue