Refactor write_ha_state logic in Tuya (#163431)

This commit is contained in:
epenet
2026-02-19 10:13:54 +01:00
committed by GitHub
parent 39909b7493
commit 676c42d578
10 changed files with 110 additions and 72 deletions
+10 -8
View File
@@ -473,14 +473,16 @@ class TuyaBinarySensorEntity(TuyaEntity, BinarySensorEntity):
"""Return true if sensor is on."""
return self._read_wrapper(self._dpcode_wrapper)
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
+27 -1
View File
@@ -59,7 +59,33 @@ class TuyaEntity(Entity):
updated_status_properties: list[str] | None,
dp_timestamps: dict[str, int] | None,
) -> None:
self.async_write_ha_state()
"""Called when Tuya device sends an update."""
if (
# If updated_status_properties is None, we should not skip,
# as we don't have information on what was updated
# This happens for example on online/offline updates, where
# we still want to update the entity state but we have nothing
# to process
updated_status_properties is None
# If we have data to process, we check if we should skip the
# state_write based on the dpcode wrapper logic
or await self._process_device_update(
updated_status_properties, dp_timestamps
)
):
self.async_write_ha_state()
async def _process_device_update(
self,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return True
async def _async_send_commands(self, commands: list[dict[str, Any]]) -> None:
"""Send a list of commands to the device."""
+10 -5
View File
@@ -215,16 +215,21 @@ class TuyaEventEntity(TuyaEntity, EventEntity):
self._dpcode_wrapper = dpcode_wrapper
self._attr_event_types = dpcode_wrapper.options
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
if self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
) or not (event_data := self._dpcode_wrapper.read_device_status(self.device)):
return
return False
event_type, event_attributes = event_data
self._trigger_event(event_type, event_attributes)
self.async_write_ha_state()
return True
+3 -10
View File
@@ -46,7 +46,7 @@ class DeviceWrapper[T]:
def skip_update(
self,
device: CustomerDevice,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> bool:
"""Determine if the wrapper should skip an update.
@@ -85,7 +85,7 @@ class DPCodeWrapper(DeviceWrapper):
def skip_update(
self,
device: CustomerDevice,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> bool:
"""Determine if the wrapper should skip an update.
@@ -252,20 +252,13 @@ class DPCodeDeltaIntegerWrapper(DPCodeIntegerWrapper):
def skip_update(
self,
device: CustomerDevice,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> bool:
"""Override skip_update to process delta updates.
Processes delta accumulation before determining if update should be skipped.
"""
# If updated_status_properties is None, we should not skip,
# as we don't have information on what was updated
# This happens for example on online/offline updates, where
# we still want to update the entity state but we have nothing
# to accumulate, so we return False to not skip the update
if updated_status_properties is None:
return False
if (
super().skip_update(device, updated_status_properties, dp_timestamps)
or dp_timestamps is None
+10 -8
View File
@@ -551,17 +551,19 @@ class TuyaNumberEntity(TuyaEntity, NumberEntity):
"""Return the entity value to represent the entity state."""
return self._read_wrapper(self._dpcode_wrapper)
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
async def async_set_native_value(self, value: float) -> None:
"""Set new value."""
+10 -8
View File
@@ -407,17 +407,19 @@ class TuyaSelectEntity(TuyaEntity, SelectEntity):
"""Return the selected entity option to represent the entity state."""
return self._read_wrapper(self._dpcode_wrapper)
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
async def async_select_option(self, option: str) -> None:
"""Change the selected option."""
+10 -8
View File
@@ -1856,14 +1856,16 @@ class TuyaSensorEntity(TuyaEntity, SensorEntity):
"""Return the value reported by the sensor."""
return self._read_wrapper(self._dpcode_wrapper)
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
+10 -8
View File
@@ -107,17 +107,19 @@ class TuyaSirenEntity(TuyaEntity, SirenEntity):
"""Return true if siren is on."""
return self._read_wrapper(self._dpcode_wrapper)
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the siren on."""
+10 -8
View File
@@ -1040,17 +1040,19 @@ class TuyaSwitchEntity(TuyaEntity, SwitchEntity):
"""Return true if switch is on."""
return self._read_wrapper(self._dpcode_wrapper)
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
+10 -8
View File
@@ -137,17 +137,19 @@ class TuyaValveEntity(TuyaEntity, ValveEntity):
return None
return not is_open
async def _handle_state_update(
async def _process_device_update(
self,
updated_status_properties: list[str] | None,
updated_status_properties: list[str],
dp_timestamps: dict[str, int] | None,
) -> None:
"""Handle state update, only if this entity's dpcode was actually updated."""
if self._dpcode_wrapper.skip_update(
) -> bool:
"""Called when Tuya device sends an update with updated properties.
Returns True if the Home Assistant state should be written,
or False if the state write should be skipped.
"""
return not self._dpcode_wrapper.skip_update(
self.device, updated_status_properties, dp_timestamps
):
return
self.async_write_ha_state()
)
async def async_open_valve(self) -> None:
"""Open the valve."""