Avoid writing unifiprotect state when nothing has changed (#100439)

This commit is contained in:
J. Nick Koston 2023-09-16 09:57:43 -05:00 committed by GitHub
parent 7b71d27637
commit 8a98a0e830
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 163 additions and 25 deletions

View file

@ -621,3 +621,23 @@ class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
if not is_on:
self._event = None
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()

View file

@ -193,3 +193,17 @@ class ProtectButton(ProtectDeviceEntity, ButtonEntity):
if self.entity_description.ufp_press is not None:
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()

View file

@ -115,6 +115,26 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
)
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:
"""Set volume level, range 0..1."""

View file

@ -268,3 +268,21 @@ class ProtectNumbers(ProtectDeviceEntity, NumberEntity):
async def async_set_native_value(self, value: float) -> None:
"""Set new 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()

View file

@ -349,9 +349,9 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
description: ProtectSelectEntityDescription,
) -> None:
"""Initialize the unifi protect select entity."""
self._async_set_options(data, description)
super().__init__(data, device, description)
self._attr_name = f"{self.device.display_name} {self.entity_description.name}"
self._async_set_options()
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
@ -366,31 +366,28 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
_LOGGER.debug(
"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
def _async_set_options(self) -> None:
def _async_set_options(
self, data: ProtectData, description: ProtectSelectEntityDescription
) -> None:
"""Set options attributes from UniFi Protect device."""
if self.entity_description.ufp_options is not None:
options = self.entity_description.ufp_options
if (ufp_options := description.ufp_options) is not None:
options = ufp_options
else:
assert self.entity_description.ufp_options_fn is not None
options = self.entity_description.ufp_options_fn(self.data.api)
assert description.ufp_options_fn is not None
options = description.ufp_options_fn(data.api)
self._attr_options = [item["name"] 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}
@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:
"""Change the Select Entity Option."""
@ -404,3 +401,23 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
if self.entity_description.ufp_enum_type is not None:
unifi_value = self.entity_description.ufp_enum_type(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()

View file

@ -710,22 +710,56 @@ class ProtectDeviceSensor(ProtectDeviceEntity, SensorEntity):
entity_description: ProtectSensorEntityDescription
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
super()._async_update_device_from_protect(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):
"""A Ubiquiti UniFi Protect Sensor."""
entity_description: ProtectSensorEntityDescription
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
super()._async_update_device_from_protect(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):
"""A UniFi Protect Device Sensor with access tokens."""

View file

@ -420,21 +420,36 @@ class ProtectSwitch(ProtectDeviceEntity, SwitchEntity):
self._attr_name = f"{self.device.display_name} {self.entity_description.name}"
self._switch_type = self.entity_description.key
@property
def is_on(self) -> bool:
"""Return true if device is on."""
return self.entity_description.get_ufp_value(self.device) is True
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
super()._async_update_device_from_protect(device)
self._attr_is_on = self.entity_description.get_ufp_value(self.device) is True
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
await self.entity_description.ufp_set(self.device, True)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the device off."""
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):
"""A UniFi Protect NVR Switch."""