From 985210fca8e935f6447c2a9a6072043687ecd2a6 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Fri, 29 Nov 2024 12:37:54 +0100 Subject: [PATCH 1/7] add impulse_mode_duration quirk to NodOn SIN-4-1-20 --- zhaquirks/nodon/switch.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index 30e09e1372..130c29c5b9 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -1,10 +1,46 @@ """NodOn on/off switch two channels.""" +from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder +from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass +import zigpy.types as t from zigpy.zcl.clusters.general import LevelControl +from zigpy.zcl.clusters.general import OnOff +from zigpy.zcl.foundation import ZCLAttributeDef +from zha.units import UnitOfTime NODON = "NodOn" +class NodOnOnOff(OnOff, CustomCluster): + """NodOn custom OnOff cluster""" + + class AttributeDefs(OnOff.AttributeDefs): + """ Set the impulse duration in milliseconds (set value to 0 to deactivate the impulse mode). """ + impulse_mode_duration = ZCLAttributeDef( + id=0x0001, + type=t.uint16_t, + is_manufacturer_specific=True, + ) + +( + # quirk is similar to https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/devices/nodon.ts#L100 + QuirkBuilder(NODON, "SIN-4-1-20") + .replaces(NodOnOnOff) + .number( + attribute_name=NodOnOnOff.AttributeDefs.impulse_mode_duration.name, + cluster_id=NodOnOnOff.cluster_id, + min_value=0, + max_value=10000, + step=1, + unit=UnitOfTime.MILLISECONDS, + device_class=NumberDeviceClass.DURATION, + initially_disabled=True, + translation_key='impulse_mode_duration', + fallback_name='Impulse mode duration', + ) + .add_to_registry() +) + ( # this quirk is a v2 version of 7397b6a QuirkBuilder(NODON, "SIN-4-2-20") From 62413c0163e46777ffc32b823c194f3a5fd6fd5a Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Fri, 29 Nov 2024 21:36:43 +0100 Subject: [PATCH 2/7] add switch type quirk to NodOn SIN-4-1-20 and alike --- zhaquirks/nodon/switch.py | 79 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index 130c29c5b9..761770989c 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -6,15 +6,33 @@ import zigpy.types as t from zigpy.zcl.clusters.general import LevelControl from zigpy.zcl.clusters.general import OnOff -from zigpy.zcl.foundation import ZCLAttributeDef +from zigpy.zcl.foundation import ZCLAttributeDef, DataTypeId from zha.units import UnitOfTime NODON = "NodOn" -class NodOnOnOff(OnOff, CustomCluster): - """NodOn custom OnOff cluster""" +class NodOnSwitchType(t.enum8): + """NodOn switch type.""" + + Bistable = 0x00 + Monostable = 0x01 + AutoDetect = 0x02 + + +class OnOffSIN4_1_20(OnOff, CustomCluster): + """NodOn custom OnOff cluster for SIN-4-1-20 and alike""" class AttributeDefs(OnOff.AttributeDefs): + """Attribute definitions.""" + + """Select the switch type wire to the device. Available from version > V3.4.0""" + switch_type = ZCLAttributeDef( + id=0x1001, + type=NodOnSwitchType, + zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type + is_manufacturer_specific=True, + ) + """ Set the impulse duration in milliseconds (set value to 0 to deactivate the impulse mode). """ impulse_mode_duration = ZCLAttributeDef( id=0x0001, @@ -22,13 +40,39 @@ class AttributeDefs(OnOff.AttributeDefs): is_manufacturer_specific=True, ) +class OnOffSIN4_2_20(OnOff, CustomCluster): + """NodOn custom OnOff cluster for SIN-4-2-20 and alike""" + + class AttributeDefs(OnOff.AttributeDefs): + """Attribute definitions.""" + + """Select the switch type wire to the device. Available from version > V3.4.0""" + switch_type = ZCLAttributeDef( + id=0x1001, + type=NodOnSwitchType, + zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type + is_manufacturer_specific=True, + ) + ( # quirk is similar to https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/devices/nodon.ts#L100 QuirkBuilder(NODON, "SIN-4-1-20") - .replaces(NodOnOnOff) + .applies_to(NODON, "SIN-4-1-21") + .applies_to(NODON, "SIN-4-1-20_PRO") + # .filter() TODO this should be applied to firmware 3.5.0+, but I didn't find a way to reliably detect it . + # Please let me know if you know how + .replaces(OnOffSIN4_1_20) + .enum( + attribute_name=OnOffSIN4_1_20.AttributeDefs.switch_type.name, + enum_class=NodOnSwitchType, + cluster_id=OnOffSIN4_1_20.cluster_id, + initially_disabled=True, + translation_key="switch_type", + fallback_name="Switch type", + ) .number( - attribute_name=NodOnOnOff.AttributeDefs.impulse_mode_duration.name, - cluster_id=NodOnOnOff.cluster_id, + attribute_name=OnOffSIN4_1_20.AttributeDefs.impulse_mode_duration.name, + cluster_id=OnOffSIN4_1_20.cluster_id, min_value=0, max_value=10000, step=1, @@ -44,7 +88,30 @@ class AttributeDefs(OnOff.AttributeDefs): ( # this quirk is a v2 version of 7397b6a QuirkBuilder(NODON, "SIN-4-2-20") + .applies_to(NODON, "SIN-4-2-20_PRO") .removes(cluster_id=LevelControl.cluster_id, endpoint_id=1) .removes(cluster_id=LevelControl.cluster_id, endpoint_id=2) + # .filter() TODO this should be applied to firmware 3.5.0+, but I didn't find a way to reliably detect it . + # Please let me know if you know how + .replaces(OnOffSIN4_2_20, endpoint_id=1) + .replaces(OnOffSIN4_2_20, endpoint_id=2) + .enum( + attribute_name=OnOffSIN4_1_20.AttributeDefs.switch_type.name, + enum_class=NodOnSwitchType, + cluster_id=OnOffSIN4_1_20.cluster_id, + endpoint_id=1, + initially_disabled=True, + translation_key="switch_type", + fallback_name="Switch type", + ) + .enum( + attribute_name=OnOffSIN4_1_20.AttributeDefs.switch_type.name, + enum_class=NodOnSwitchType, + cluster_id=OnOffSIN4_1_20.cluster_id, + endpoint_id=2, + initially_disabled=True, + translation_key="switch_type", + fallback_name="Switch type", + ) .add_to_registry() ) From 48fa361de9a7989f060982197a4e49ec68a10962 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 29 Nov 2024 22:24:03 +0000 Subject: [PATCH 3/7] Apply pre-commit auto fixes --- zhaquirks/nodon/switch.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index 761770989c..ed024de27f 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -1,16 +1,16 @@ """NodOn on/off switch two channels.""" +from zha.units import UnitOfTime from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass import zigpy.types as t -from zigpy.zcl.clusters.general import LevelControl -from zigpy.zcl.clusters.general import OnOff -from zigpy.zcl.foundation import ZCLAttributeDef, DataTypeId -from zha.units import UnitOfTime +from zigpy.zcl.clusters.general import LevelControl, OnOff +from zigpy.zcl.foundation import DataTypeId, ZCLAttributeDef NODON = "NodOn" + class NodOnSwitchType(t.enum8): """NodOn switch type.""" @@ -29,7 +29,7 @@ class AttributeDefs(OnOff.AttributeDefs): switch_type = ZCLAttributeDef( id=0x1001, type=NodOnSwitchType, - zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type + zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type is_manufacturer_specific=True, ) @@ -40,6 +40,7 @@ class AttributeDefs(OnOff.AttributeDefs): is_manufacturer_specific=True, ) + class OnOffSIN4_2_20(OnOff, CustomCluster): """NodOn custom OnOff cluster for SIN-4-2-20 and alike""" @@ -50,10 +51,11 @@ class AttributeDefs(OnOff.AttributeDefs): switch_type = ZCLAttributeDef( id=0x1001, type=NodOnSwitchType, - zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type + zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type is_manufacturer_specific=True, ) + ( # quirk is similar to https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/devices/nodon.ts#L100 QuirkBuilder(NODON, "SIN-4-1-20") @@ -79,8 +81,8 @@ class AttributeDefs(OnOff.AttributeDefs): unit=UnitOfTime.MILLISECONDS, device_class=NumberDeviceClass.DURATION, initially_disabled=True, - translation_key='impulse_mode_duration', - fallback_name='Impulse mode duration', + translation_key="impulse_mode_duration", + fallback_name="Impulse mode duration", ) .add_to_registry() ) From 664aa78e054268e4495a184b0d61eeadce94014d Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Fri, 29 Nov 2024 23:25:38 +0100 Subject: [PATCH 4/7] fix comments --- zhaquirks/nodon/switch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index ed024de27f..9c8acb62c5 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -61,7 +61,7 @@ class AttributeDefs(OnOff.AttributeDefs): QuirkBuilder(NODON, "SIN-4-1-20") .applies_to(NODON, "SIN-4-1-21") .applies_to(NODON, "SIN-4-1-20_PRO") - # .filter() TODO this should be applied to firmware 3.5.0+, but I didn't find a way to reliably detect it . + # .filter() TODO this should be applied to firmware 3.4.0+, but I didn't find a way to reliably detect it . # Please let me know if you know how .replaces(OnOffSIN4_1_20) .enum( @@ -93,7 +93,7 @@ class AttributeDefs(OnOff.AttributeDefs): .applies_to(NODON, "SIN-4-2-20_PRO") .removes(cluster_id=LevelControl.cluster_id, endpoint_id=1) .removes(cluster_id=LevelControl.cluster_id, endpoint_id=2) - # .filter() TODO this should be applied to firmware 3.5.0+, but I didn't find a way to reliably detect it . + # .filter() TODO this should be applied to firmware 3.4.0+, but I didn't find a way to reliably detect it . # Please let me know if you know how .replaces(OnOffSIN4_2_20, endpoint_id=1) .replaces(OnOffSIN4_2_20, endpoint_id=2) From e5ed1a1216d25f5973f862daa49133df846b7d07 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Fri, 29 Nov 2024 23:26:19 +0100 Subject: [PATCH 5/7] make CI happy --- zhaquirks/nodon/switch.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index 9c8acb62c5..671b21fe9a 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -20,12 +20,12 @@ class NodOnSwitchType(t.enum8): class OnOffSIN4_1_20(OnOff, CustomCluster): - """NodOn custom OnOff cluster for SIN-4-1-20 and alike""" + """NodOn custom OnOff cluster for SIN-4-1-20 and alike.""" class AttributeDefs(OnOff.AttributeDefs): """Attribute definitions.""" - """Select the switch type wire to the device. Available from version > V3.4.0""" + """Select the switch type wire to the device. Available from version > V3.4.0.""" switch_type = ZCLAttributeDef( id=0x1001, type=NodOnSwitchType, @@ -42,12 +42,12 @@ class AttributeDefs(OnOff.AttributeDefs): class OnOffSIN4_2_20(OnOff, CustomCluster): - """NodOn custom OnOff cluster for SIN-4-2-20 and alike""" + """NodOn custom OnOff cluster for SIN-4-2-20 and alike.""" class AttributeDefs(OnOff.AttributeDefs): """Attribute definitions.""" - """Select the switch type wire to the device. Available from version > V3.4.0""" + """Select the switch type wire to the device. Available from version > V3.4.0.""" switch_type = ZCLAttributeDef( id=0x1001, type=NodOnSwitchType, From b33e98d57f423ea27aee096dc9e0573da629aa59 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Fri, 29 Nov 2024 23:32:45 +0100 Subject: [PATCH 6/7] fix UnitOfTime dependency --- zhaquirks/nodon/switch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index 671b21fe9a..afd5b69619 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -1,8 +1,8 @@ """NodOn on/off switch two channels.""" -from zha.units import UnitOfTime from zigpy.quirks import CustomCluster from zigpy.quirks.v2 import QuirkBuilder +from zigpy.quirks.v2.homeassistant import UnitOfTime from zigpy.quirks.v2.homeassistant.number import NumberDeviceClass import zigpy.types as t from zigpy.zcl.clusters.general import LevelControl, OnOff From 3b536c2388b7e5d031e846d5a631b81421ffa7d3 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Sat, 30 Nov 2024 12:08:15 +0100 Subject: [PATCH 7/7] access=rw --- zhaquirks/nodon/switch.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zhaquirks/nodon/switch.py b/zhaquirks/nodon/switch.py index afd5b69619..8865a70525 100644 --- a/zhaquirks/nodon/switch.py +++ b/zhaquirks/nodon/switch.py @@ -30,6 +30,7 @@ class AttributeDefs(OnOff.AttributeDefs): id=0x1001, type=NodOnSwitchType, zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type + access="rw", is_manufacturer_specific=True, ) @@ -37,6 +38,7 @@ class AttributeDefs(OnOff.AttributeDefs): impulse_mode_duration = ZCLAttributeDef( id=0x0001, type=t.uint16_t, + access="rw", is_manufacturer_specific=True, ) @@ -52,6 +54,7 @@ class AttributeDefs(OnOff.AttributeDefs): id=0x1001, type=NodOnSwitchType, zcl_type=DataTypeId.enum8, # need to explicitly set ZCL type + access="rw", is_manufacturer_specific=True, )