From 6314584cd1c2502c13fdfed922bd0967f8617bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E9=94=A1=E6=A5=B7?= Date: Mon, 10 Nov 2025 18:05:59 +0800 Subject: [PATCH 1/4] add Sonoff trvzb new quirks --- zhaquirks/sonoff/trvzb.py | 101 +++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/zhaquirks/sonoff/trvzb.py b/zhaquirks/sonoff/trvzb.py index a7e45251b5..d55a11ee9a 100644 --- a/zhaquirks/sonoff/trvzb.py +++ b/zhaquirks/sonoff/trvzb.py @@ -2,7 +2,7 @@ from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import NumberDeviceClass, QuirkBuilder -from zigpy.quirks.v2.homeassistant import UnitOfTemperature +from zigpy.quirks.v2.homeassistant import UnitOfTemperature, UnitOfTime import zigpy.types as t from zigpy.zcl.foundation import BaseAttributeDefs, ZCLAttributeDef @@ -85,6 +85,67 @@ class AttributeDefs(BaseAttributeDefs): type=t.int16s, ) + temporary_mode = ZCLAttributeDef( + id=0x6014, + type=t.uint8_t, + ) + + boost_mode = ZCLAttributeDef( + id=0x6017, + type=t.Bool, + is_manufacturer_specific=True, + ) + + timer_mode = ZCLAttributeDef( + id=0x6018, + type=t.Bool, + is_manufacturer_specific=True, + ) + + temporary_mode_duration = ZCLAttributeDef( + id=0x6015, + type=t.uint32_t, + ) + + timer_mode_target_temperature = ZCLAttributeDef( + id=0x6016, + type=t.int16s, + ) + + def _update_attribute(self, attrid, value): + """Update attribute and handle temporary mode conversion.""" + super()._update_attribute(attrid, value) + + if attrid == self.AttributeDefs.temporary_mode.id: + # Convert value to individual mode states + self._update_attribute( + self.AttributeDefs.boost_mode.id, + value == 0x00, + ) + self._update_attribute( + self.AttributeDefs.timer_mode.id, + value == 0x01, + ) + + async def write_attributes(self, attributes, manufacturer=None, **kwargs): + """Handle writing individual mode attributes by updating temporary_mode.""" + mode_attr = self.AttributeDefs.temporary_mode.id + new_attributes = attributes.copy() + + mode_attr_defs = [ + (self.AttributeDefs.boost_mode, 0x00), + (self.AttributeDefs.timer_mode, 0x01), + ] + + for attrid in attributes: + for attr_def, mode_value in mode_attr_defs: + if attrid in (attr_def.id, attr_def.name): + new_attributes.pop(attrid) + new_attributes[mode_attr] = mode_value + break + + return await super().write_attributes(new_attributes, manufacturer, **kwargs) + @property def _is_manuf_specific(self): return False @@ -105,6 +166,42 @@ def _is_manuf_specific(self): translation_key="open_window", fallback_name="Open window", ) + .number( + CustomSonoffCluster.AttributeDefs.timer_mode_target_temperature.name, + CustomSonoffCluster.cluster_id, + min_value=4.0, + max_value=35.0, + step=0.5, + unit=UnitOfTemperature.CELSIUS, + multiplier=0.01, + translation_key="timer_mode_target_temperature", + fallback_name="Timer mode target temperature", + ) + .number( + CustomSonoffCluster.AttributeDefs.temporary_mode_duration.name, + CustomSonoffCluster.cluster_id, + min_value=0, + max_value=1440, + step=1, + unit=UnitOfTime.MINUTES, + multiplier=1 / 60, + translation_key="temporary_mode_duration", + fallback_name="Temporary mode duration", + ) + .write_attr_button( + attribute_name=CustomSonoffCluster.AttributeDefs.boost_mode.name, + cluster_id=CustomSonoffCluster.cluster_id, + attribute_value=0x00, + translation_key="boost_mode", + fallback_name="Boost mode", + ) + .write_attr_button( + attribute_name=CustomSonoffCluster.AttributeDefs.timer_mode.name, + cluster_id=CustomSonoffCluster.cluster_id, + attribute_value=0x01, + translation_key="timer_mode", + fallback_name="Timer mode", + ) .number( CustomSonoffCluster.AttributeDefs.frost_protection_temperature.name, CustomSonoffCluster.cluster_id, @@ -167,4 +264,4 @@ def _is_manuf_specific(self): fallback_name="External temperature sensor value", ) .add_to_registry() -) +) \ No newline at end of file From fa282b46a0395e23dd0312bc6758899ca085c838 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:10:04 +0000 Subject: [PATCH 2/4] Apply pre-commit auto fixes --- zhaquirks/sonoff/trvzb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhaquirks/sonoff/trvzb.py b/zhaquirks/sonoff/trvzb.py index d55a11ee9a..95cd4f1b3b 100644 --- a/zhaquirks/sonoff/trvzb.py +++ b/zhaquirks/sonoff/trvzb.py @@ -264,4 +264,4 @@ def _is_manuf_specific(self): fallback_name="External temperature sensor value", ) .add_to_registry() -) \ No newline at end of file +) From d8ed8112fa00bbf5cb030837f61561ee87dbcd85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BD=AD=E9=94=A1=E6=A5=B7?= Date: Mon, 10 Nov 2025 18:37:45 +0800 Subject: [PATCH 3/4] add Sonoff trvzb new quirks --- zhaquirks/sonoff/trvzb.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/zhaquirks/sonoff/trvzb.py b/zhaquirks/sonoff/trvzb.py index d55a11ee9a..cf57f4dda3 100644 --- a/zhaquirks/sonoff/trvzb.py +++ b/zhaquirks/sonoff/trvzb.py @@ -115,27 +115,16 @@ class AttributeDefs(BaseAttributeDefs): def _update_attribute(self, attrid, value): """Update attribute and handle temporary mode conversion.""" super()._update_attribute(attrid, value) - if attrid == self.AttributeDefs.temporary_mode.id: # Convert value to individual mode states - self._update_attribute( - self.AttributeDefs.boost_mode.id, - value == 0x00, - ) - self._update_attribute( - self.AttributeDefs.timer_mode.id, - value == 0x01, - ) - + self._update_attribute(self.AttributeDefs.boost_mode.id,value == 0x00) + self._update_attribute(self.AttributeDefs.timer_mode.id,value == 0x01) async def write_attributes(self, attributes, manufacturer=None, **kwargs): """Handle writing individual mode attributes by updating temporary_mode.""" mode_attr = self.AttributeDefs.temporary_mode.id new_attributes = attributes.copy() - mode_attr_defs = [ - (self.AttributeDefs.boost_mode, 0x00), - (self.AttributeDefs.timer_mode, 0x01), - ] + mode_attr_defs = [(self.AttributeDefs.boost_mode, 0x00),(self.AttributeDefs.timer_mode, 0x01)] for attrid in attributes: for attr_def, mode_value in mode_attr_defs: From d23f1e2c88087587de79f83e112358f60b8fc9b6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:39:08 +0000 Subject: [PATCH 4/4] Apply pre-commit auto fixes --- zhaquirks/sonoff/trvzb.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zhaquirks/sonoff/trvzb.py b/zhaquirks/sonoff/trvzb.py index 9c887ddbe4..6bf0e56576 100644 --- a/zhaquirks/sonoff/trvzb.py +++ b/zhaquirks/sonoff/trvzb.py @@ -117,14 +117,18 @@ def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == self.AttributeDefs.temporary_mode.id: # Convert value to individual mode states - self._update_attribute(self.AttributeDefs.boost_mode.id,value == 0x00) - self._update_attribute(self.AttributeDefs.timer_mode.id,value == 0x01) + self._update_attribute(self.AttributeDefs.boost_mode.id, value == 0x00) + self._update_attribute(self.AttributeDefs.timer_mode.id, value == 0x01) + async def write_attributes(self, attributes, manufacturer=None, **kwargs): """Handle writing individual mode attributes by updating temporary_mode.""" mode_attr = self.AttributeDefs.temporary_mode.id new_attributes = attributes.copy() - mode_attr_defs = [(self.AttributeDefs.boost_mode, 0x00),(self.AttributeDefs.timer_mode, 0x01)] + mode_attr_defs = [ + (self.AttributeDefs.boost_mode, 0x00), + (self.AttributeDefs.timer_mode, 0x01), + ] for attrid in attributes: for attr_def, mode_value in mode_attr_defs: