From 1da86f9220d6467dea8ffdfc402a89fef4bc50dd Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Fri, 17 Jan 2025 03:42:27 +0100 Subject: [PATCH 1/5] Add V2 Quirk for telink devices with cfw --- zhaquirks/custom/__init__.py | 1 + zhaquirks/custom/telink.py | 206 +++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 zhaquirks/custom/__init__.py create mode 100644 zhaquirks/custom/telink.py diff --git a/zhaquirks/custom/__init__.py b/zhaquirks/custom/__init__.py new file mode 100644 index 0000000000..48ab441808 --- /dev/null +++ b/zhaquirks/custom/__init__.py @@ -0,0 +1 @@ +"""Quirks for diy devices or with custom firmware.""" diff --git a/zhaquirks/custom/telink.py b/zhaquirks/custom/telink.py new file mode 100644 index 0000000000..0149b1e762 --- /dev/null +++ b/zhaquirks/custom/telink.py @@ -0,0 +1,206 @@ +"""Telink TLSR825x based devices with custom firmware. + +see https://github.com/pvvx/ZigbeeTLc +""" + +from zigpy.quirks.v2 import QuirkBuilder +from zigpy.quirks.v2.homeassistant import UnitOfTemperature, UnitOfTime +import zigpy.types as t +from zigpy.zcl import ClusterType +from zigpy.zcl.clusters.hvac import UserInterface +from zigpy.zcl.foundation import ZCLAttributeDef + +from zhaquirks import CustomCluster + + +class Display(t.enum8): + """Turn off the display.""" + + Off = 0x01 + On = 0x00 + + +class CustomUserInterfaceCluster(CustomCluster, UserInterface): + """Custom User Interface Cluster with smiley control.""" + + class AttributeDefs(UserInterface.AttributeDefs): + """Attribute Definitions.""" + + # display. 0 - display is off, 1 - display is on + display = ZCLAttributeDef( + id=0x0106, + type=Display, + access="rw", + is_manufacturer_specific=True, + ) + + # comfort temperature min: A value in 0.01ºC to set minimum comfort temperature for happy face + comfort_temperature_min = ZCLAttributeDef( + id=0x0102, + type=t.int16s, + access="rw", + is_manufacturer_specific=True, + ) + + # comfort temperature max: A value in 0.01ºC to set maximum comfort temperature for happy face + comfort_temperature_max = ZCLAttributeDef( + id=0x0103, + type=t.int16s, + access="rw", + is_manufacturer_specific=True, + ) + + # comfort humidity min: A value in 0.01%RH to set minimum comfort humidity for happy face + comfort_humidity_min = ZCLAttributeDef( + id=0x0104, + type=t.uint16_t, + access="rw", + is_manufacturer_specific=True, + ) + + # comfort humidity max: A value in 0.01%RH to set maximum comfort humidity for happy face + comfort_humidity_max = ZCLAttributeDef( + id=0x0105, + type=t.uint16_t, + access="rw", + is_manufacturer_specific=True, + ) + + # A value in 0.01ºC offset to fix up incorrect values from sensor + temperature_offset = ZCLAttributeDef( + id=0x0100, + type=t.int16s, + access="rw", + is_manufacturer_specific=True, + ) + + # A value in 0.01%RH offset to fix up incorrect values from sensor + humidity_offset = ZCLAttributeDef( + id=0x0101, + type=t.int16s, + access="rw", + is_manufacturer_specific=True, + ) + # Measurement interval, step 1 second, range: 3..255 seconds. Default 10 seconds. + measurement_interval = ZCLAttributeDef( + id=0x0107, + type=t.uint8_t, + access="rw", + is_manufacturer_specific=True, + ) + + +( + QuirkBuilder("MiaoMiaoCe", "MHO-C401N-z") + .applies_to("MiaMiaoCe", "MHO-C401N-z") # typo until v. 1.2.2 + .applies_to("MiaoMiaoCe", "MHO-C401-z") + .applies_to("MiaoMiaoCe", "MHO-C122-z") + .applies_to("MiaMiaoCe", "MHO-C122-z") # typo until v. 1.2.2 + .applies_to("Xiaomi", "LYWSD03MMC-z") + .applies_to("Tuya", "TS0201-z") + .applies_to("Tuya", "TH03Z-z") + .applies_to("Tuya", "ZTH01-z") + .applies_to("Tuya", "ZTH02-z") + .applies_to("Tuya", "LKTMZL02-z") + .applies_to("Sonoff", "TH03-z") + .applies_to("Qingping", "CGDK2-z") + .removes(CustomUserInterfaceCluster.cluster_id, cluster_type=ClusterType.Client) + .adds(CustomUserInterfaceCluster) + .number( + CustomUserInterfaceCluster.AttributeDefs.temperature_offset.name, + CustomUserInterfaceCluster.cluster_id, + min_value=-327.67, + max_value=327.67, + step=0.01, + unit=UnitOfTemperature.CELSIUS, + translation_key="temperature_offset", + fallback_name="Temperature offset", + multiplier=0.01, + mode="box", + ) + .number( + CustomUserInterfaceCluster.AttributeDefs.humidity_offset.name, + CustomUserInterfaceCluster.cluster_id, + min_value=-327.67, + max_value=327.67, + step=0.01, + # unit=PERCENTAGE, + translation_key="humidity_offset", + fallback_name="Humidity offset", + multiplier=0.01, + mode="box", + ) + .number( + CustomUserInterfaceCluster.AttributeDefs.comfort_temperature_min.name, + CustomUserInterfaceCluster.cluster_id, + min_value=-327.67, + max_value=327.67, + step=0.01, + unit=UnitOfTemperature.CELSIUS, + translation_key="comfort_temperature_min", + fallback_name="Comfort temperature min", + multiplier=0.01, + mode="box", + ) + .number( + CustomUserInterfaceCluster.AttributeDefs.comfort_temperature_max.name, + CustomUserInterfaceCluster.cluster_id, + min_value=-327.67, + max_value=327.67, + step=0.01, + unit=UnitOfTemperature.CELSIUS, + translation_key="comfort_temperature_max", + fallback_name="Comfort temperature max", + multiplier=0.01, + mode="box", + ) + .number( + CustomUserInterfaceCluster.AttributeDefs.comfort_humidity_min.name, + CustomUserInterfaceCluster.cluster_id, + min_value=0, + max_value=99, + step=1, + # unit=PERCENTAGE, + translation_key="comfort_humidity_min", + fallback_name="Comfort humidity min", + multiplier=0.01, + mode="box", + ) + .number( + CustomUserInterfaceCluster.AttributeDefs.comfort_humidity_max.name, + CustomUserInterfaceCluster.cluster_id, + min_value=0, + max_value=99, + step=1, + # unit=PERCENTAGE, + translation_key="comfort_humidity_max", + fallback_name="Comfort humidity max", + multiplier=0.01, + mode="box", + ) + .number( + CustomUserInterfaceCluster.AttributeDefs.measurement_interval.name, + CustomUserInterfaceCluster.cluster_id, + min_value=3, + max_value=255, + unit=UnitOfTime.SECONDS, + translation_key="measurement_interval", + fallback_name="Measurement interval", + mode="box", + ) + .switch( + CustomUserInterfaceCluster.AttributeDefs.display.name, + CustomUserInterfaceCluster.cluster_id, + off_value=1, + on_value=0, + translation_key="display_enabled", + fallback_name="Display enabled", + ) + .switch( + CustomUserInterfaceCluster.AttributeDefs.schedule_programming_visibility.name, + CustomUserInterfaceCluster.cluster_id, + translation_key="show_smiley", + fallback_name="Show smiley", + ) + .add_to_registry() +) From 12571bc5ccc7be7c5fcf7be21c7efb34604d74c4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 19 Jan 2025 05:20:55 +0000 Subject: [PATCH 2/5] Apply pre-commit auto fixes --- zhaquirks/custom/telink.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zhaquirks/custom/telink.py b/zhaquirks/custom/telink.py index 0149b1e762..6ef4930fc9 100644 --- a/zhaquirks/custom/telink.py +++ b/zhaquirks/custom/telink.py @@ -92,10 +92,10 @@ class AttributeDefs(UserInterface.AttributeDefs): ( QuirkBuilder("MiaoMiaoCe", "MHO-C401N-z") - .applies_to("MiaMiaoCe", "MHO-C401N-z") # typo until v. 1.2.2 + .applies_to("MiaMiaoCe", "MHO-C401N-z") # typo until v. 1.2.2 .applies_to("MiaoMiaoCe", "MHO-C401-z") .applies_to("MiaoMiaoCe", "MHO-C122-z") - .applies_to("MiaMiaoCe", "MHO-C122-z") # typo until v. 1.2.2 + .applies_to("MiaMiaoCe", "MHO-C122-z") # typo until v. 1.2.2 .applies_to("Xiaomi", "LYWSD03MMC-z") .applies_to("Tuya", "TS0201-z") .applies_to("Tuya", "TH03Z-z") From 0b1b65fe2d0e43ddf893a79816b5182bdc1ffd4b Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Wed, 22 Jan 2025 22:58:43 +0100 Subject: [PATCH 3/5] invert smiley states --- zhaquirks/custom/telink.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zhaquirks/custom/telink.py b/zhaquirks/custom/telink.py index 6ef4930fc9..f695493dfc 100644 --- a/zhaquirks/custom/telink.py +++ b/zhaquirks/custom/telink.py @@ -7,7 +7,7 @@ from zigpy.quirks.v2.homeassistant import UnitOfTemperature, UnitOfTime import zigpy.types as t from zigpy.zcl import ClusterType -from zigpy.zcl.clusters.hvac import UserInterface +from zigpy.zcl.clusters.hvac import ScheduleProgrammingVisibility, UserInterface from zigpy.zcl.foundation import ZCLAttributeDef from zhaquirks import CustomCluster @@ -201,6 +201,8 @@ class AttributeDefs(UserInterface.AttributeDefs): CustomUserInterfaceCluster.cluster_id, translation_key="show_smiley", fallback_name="Show smiley", + off_value=ScheduleProgrammingVisibility.Disabled, + on_value=ScheduleProgrammingVisibility.Enabled, ) .add_to_registry() ) From 24ef02ab72f011e980f95a6d0bb3c204f96df841 Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Mon, 27 Jan 2025 02:46:30 +0100 Subject: [PATCH 4/5] readd percentage units --- zhaquirks/custom/telink.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zhaquirks/custom/telink.py b/zhaquirks/custom/telink.py index f695493dfc..e458c1a4a0 100644 --- a/zhaquirks/custom/telink.py +++ b/zhaquirks/custom/telink.py @@ -4,7 +4,7 @@ """ from zigpy.quirks.v2 import QuirkBuilder -from zigpy.quirks.v2.homeassistant import UnitOfTemperature, UnitOfTime +from zigpy.quirks.v2.homeassistant import PERCENTAGE, UnitOfTemperature, UnitOfTime import zigpy.types as t from zigpy.zcl import ClusterType from zigpy.zcl.clusters.hvac import ScheduleProgrammingVisibility, UserInterface @@ -124,7 +124,7 @@ class AttributeDefs(UserInterface.AttributeDefs): min_value=-327.67, max_value=327.67, step=0.01, - # unit=PERCENTAGE, + unit=PERCENTAGE, translation_key="humidity_offset", fallback_name="Humidity offset", multiplier=0.01, @@ -160,7 +160,7 @@ class AttributeDefs(UserInterface.AttributeDefs): min_value=0, max_value=99, step=1, - # unit=PERCENTAGE, + unit=PERCENTAGE, translation_key="comfort_humidity_min", fallback_name="Comfort humidity min", multiplier=0.01, @@ -172,7 +172,7 @@ class AttributeDefs(UserInterface.AttributeDefs): min_value=0, max_value=99, step=1, - # unit=PERCENTAGE, + unit=PERCENTAGE, translation_key="comfort_humidity_max", fallback_name="Comfort humidity max", multiplier=0.01, From 32f015429587d3f18d459c63de3fdae26afb6f7a Mon Sep 17 00:00:00 2001 From: tr4nt0r <4445816+tr4nt0r@users.noreply.github.com> Date: Mon, 27 Jan 2025 02:53:21 +0100 Subject: [PATCH 5/5] Add Tuya ZY-ZTH02-z --- zhaquirks/custom/telink.py | 1 + 1 file changed, 1 insertion(+) diff --git a/zhaquirks/custom/telink.py b/zhaquirks/custom/telink.py index e458c1a4a0..d874a5d845 100644 --- a/zhaquirks/custom/telink.py +++ b/zhaquirks/custom/telink.py @@ -102,6 +102,7 @@ class AttributeDefs(UserInterface.AttributeDefs): .applies_to("Tuya", "ZTH01-z") .applies_to("Tuya", "ZTH02-z") .applies_to("Tuya", "LKTMZL02-z") + .applies_to("Tuya", "ZY-ZTH02-z") .applies_to("Sonoff", "TH03-z") .applies_to("Qingping", "CGDK2-z") .removes(CustomUserInterfaceCluster.cluster_id, cluster_type=ClusterType.Client)