From 4f5abdd2b7e5d636d5bce1a7173f1544a506493c Mon Sep 17 00:00:00 2001 From: TheJulianJES Date: Wed, 29 Nov 2023 05:25:26 +0100 Subject: [PATCH] Support power/energy for more Aqara (H1) switches, add devices (#2784) * Have `OppleCluster` in `opple_remote.py` inherit `XiaomiAqaraE1Cluster` * Add `MODELS_INFO` to `XiaomiOpple2ButtonSwitchFace1`, `XiaomiOpple2ButtonSwitchFace2` These are the only models I found online. * Add power and energy support for `lumi.switch.n2aeu1`, `lumi.switch.b2naus01` Seems to be supported * Add alternative signature for `lumi.switch.n1aeu1`, support power/energy, refactor * Remove redundant `XiaomiMeteringCluster` * Use `LUMI` constant for `MODELS_INFO` * Remove cluster ID comments for opple_switch.py * Remove redundant device_automation_triggers assignment * Rename `switch_h1.py` to `switch_h1_single.py`, add comment * Add `switch_h1_double.py` and move H1 double switch quirk from `opple_switch.py` * Remove cluster ID comments from `XiaomiOpple2ButtonSwitchFace2` * Rename `lumi.switch.n2aeu1` quirk to `AqaraH1DoubleRockerSwitchWithNeutral` * Change imports slightly * Add support for `lumi.switch.l2aeu1`, `lumi.switch.l2aeu1` * Improve docstring for opple_switch.py --- tests/test_quirks.py | 2 - zhaquirks/xiaomi/__init__.py | 4 - zhaquirks/xiaomi/aqara/opple_remote.py | 12 +- zhaquirks/xiaomi/aqara/opple_switch.py | 142 ++++------ zhaquirks/xiaomi/aqara/switch_h1.py | 226 --------------- zhaquirks/xiaomi/aqara/switch_h1_double.py | 194 +++++++++++++ zhaquirks/xiaomi/aqara/switch_h1_single.py | 309 +++++++++++++++++++++ 7 files changed, 559 insertions(+), 330 deletions(-) delete mode 100644 zhaquirks/xiaomi/aqara/switch_h1.py create mode 100644 zhaquirks/xiaomi/aqara/switch_h1_double.py create mode 100644 zhaquirks/xiaomi/aqara/switch_h1_single.py diff --git a/tests/test_quirks.py b/tests/test_quirks.py index 2300c478e2..ab96ef3cf3 100644 --- a/tests/test_quirks.py +++ b/tests/test_quirks.py @@ -401,8 +401,6 @@ def _check_range(cluster: zcl.Cluster) -> bool: # Some quirks do not yet have model info: zhaquirks.xbee.xbee_io.XBeeSensor, zhaquirks.xbee.xbee3_io.XBee3Sensor, - zhaquirks.xiaomi.aqara.opple_switch.XiaomiOpple2ButtonSwitchFace2, - zhaquirks.xiaomi.aqara.opple_switch.XiaomiOpple2ButtonSwitchFace1, zhaquirks.tuya.ts0201.MoesTemperatureHumidtySensorWithScreen, zhaquirks.smartthings.tag_v4.SmartThingsTagV4, zhaquirks.smartthings.multi.SmartthingsMultiPurposeSensor, diff --git a/zhaquirks/xiaomi/__init__.py b/zhaquirks/xiaomi/__init__.py index 9b0713e69a..a01ad46b4b 100644 --- a/zhaquirks/xiaomi/__init__.py +++ b/zhaquirks/xiaomi/__init__.py @@ -542,10 +542,6 @@ class DeviceTemperatureCluster(LocalDataCluster, DeviceTemperature): """Device Temperature Cluster.""" -class XiaomiMeteringCluster(LocalDataCluster, Metering): - """Xiaomi Metering Cluster.""" - - class TemperatureMeasurementCluster(CustomCluster, TemperatureMeasurement): """Temperature cluster that filters out invalid temperature readings.""" diff --git a/zhaquirks/xiaomi/aqara/opple_remote.py b/zhaquirks/xiaomi/aqara/opple_remote.py index bdbf401867..a3590e4f44 100644 --- a/zhaquirks/xiaomi/aqara/opple_remote.py +++ b/zhaquirks/xiaomi/aqara/opple_remote.py @@ -50,7 +50,12 @@ VALUE, ZHA_SEND_EVENT, ) -from zhaquirks.xiaomi import LUMI, BasicCluster, XiaomiCustomDevice +from zhaquirks.xiaomi import ( + LUMI, + BasicCluster, + XiaomiAqaraE1Cluster, + XiaomiCustomDevice, +) PRESS_TYPES = {0: "hold", 1: "single", 2: "double", 3: "triple", 255: "release"} STATUS_TYPE_ATTR = 0x0055 # decimal = 85 @@ -91,7 +96,6 @@ COMMAND_6_HOLD = "6_hold" COMMAND_6_RELEASE = "6_release" -OPPLE_CLUSTER_ID = 0xFCC0 OPPLE_MFG_CODE = 0x115F @@ -129,11 +133,9 @@ def _update_attribute(self, attrid, value): super()._update_attribute(0, action) -class OppleCluster(CustomCluster): +class OppleCluster(XiaomiAqaraE1Cluster): """Opple cluster.""" - ep_attribute = "opple_cluster" - cluster_id = OPPLE_CLUSTER_ID attributes = { 0x0009: ("mode", types.uint8_t, True), } diff --git a/zhaquirks/xiaomi/aqara/opple_switch.py b/zhaquirks/xiaomi/aqara/opple_switch.py index 5a08794195..d101d21064 100644 --- a/zhaquirks/xiaomi/aqara/opple_switch.py +++ b/zhaquirks/xiaomi/aqara/opple_switch.py @@ -1,4 +1,4 @@ -"""Xiaomi aqara single key wall switch devices.""" +"""Xiaomi Aqara wall switch devices. Also see switch_h1 files similar H1 rocker switches.""" import copy from enum import Enum @@ -33,6 +33,7 @@ ENDPOINT_ID, ENDPOINTS, INPUT_CLUSTERS, + MODELS_INFO, OUTPUT_CLUSTERS, PRESS_TYPE, PROFILE_ID, @@ -40,11 +41,14 @@ ZHA_SEND_EVENT, ) from zhaquirks.xiaomi import ( + LUMI, + AnalogInputCluster, BasicCluster, DeviceTemperatureCluster, + ElectricalMeasurementCluster, + MeteringCluster, OnOffCluster, XiaomiCustomDevice, - XiaomiMeteringCluster, ) from .opple_remote import MultistateInputCluster, OppleCluster @@ -85,7 +89,6 @@ class OppleSwitchCluster(OppleCluster): """Xiaomi mfg cluster implementation.""" attributes = copy.deepcopy(OppleCluster.attributes) - attributes.update( { 0x0002: ("power_outage_count", t.uint8_t, True), @@ -124,15 +127,16 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ BasicCluster, - DeviceTemperatureCluster, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOffCluster, # 6 - Alarms.cluster_id, # 9 - MultistateInputCluster, # 18 - XiaomiMeteringCluster, # 0x0702 - OppleSwitchCluster, # 0xFCC0 / 64704 + DeviceTemperatureCluster, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + Alarms.cluster_id, + MultistateInputCluster, + MeteringCluster, + ElectricalMeasurementCluster, + OppleSwitchCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, @@ -140,12 +144,12 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ BasicCluster, - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOffCluster, # 6 - MultistateInputCluster, # 18 - OppleSwitchCluster, # 0xFCC0 / 64704 + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + OppleSwitchCluster, ], OUTPUT_CLUSTERS: [], }, @@ -154,7 +158,7 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - AnalogInput.cluster_id, # 12 + AnalogInputCluster, ], OUTPUT_CLUSTERS: [], }, @@ -163,7 +167,7 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - AnalogInput.cluster_id, # 12 + AnalogInput.cluster_id, ], OUTPUT_CLUSTERS: [], }, @@ -172,7 +176,7 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster, # 18 + MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, @@ -181,7 +185,7 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster, # 18 + MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, @@ -190,7 +194,7 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster, # 18 + MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, @@ -199,7 +203,7 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster, # 18 + MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, @@ -273,23 +277,22 @@ class XiaomiOpple2ButtonSwitchBase(XiaomiCustomDevice): class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): """Xiaomi Opple 2 Button Switch. Face 1.""" - device_automation_triggers = XiaomiOpple2ButtonSwitchBase.device_automation_triggers - signature = { + MODELS_INFO: [(LUMI, "lumi.switch.b2naus01")], ENDPOINTS: { # input_clusters=[0, 2, 3, 4, 5, 6, 18, 64704], output_clusters=[10, 25] 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - Basic.cluster_id, # 0 - DeviceTemperatureCluster.cluster_id, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOff.cluster_id, # 6 - MultistateInputCluster.cluster_id, # 18 - OppleSwitchCluster.cluster_id, # 0xFCC0 / 64704 + Basic.cluster_id, + DeviceTemperatureCluster.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInputCluster.cluster_id, + OppleSwitchCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, @@ -298,13 +301,13 @@ class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - Basic.cluster_id, # 0 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOff.cluster_id, # 6 - MultistateInputCluster.cluster_id, # 18 - OppleSwitchCluster.cluster_id, # 0xFCC0 / 64704 + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInputCluster.cluster_id, + OppleSwitchCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, @@ -313,7 +316,7 @@ class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - AnalogInput.cluster_id, # 12 + AnalogInput.cluster_id, ], OUTPUT_CLUSTERS: [], }, @@ -322,7 +325,7 @@ class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - AnalogInput.cluster_id, # 12 + AnalogInput.cluster_id, ], OUTPUT_CLUSTERS: [], }, @@ -331,7 +334,7 @@ class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster.cluster_id, # 18 + MultistateInputCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, @@ -340,7 +343,7 @@ class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster.cluster_id, # 18 + MultistateInputCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, @@ -349,54 +352,7 @@ class XiaomiOpple2ButtonSwitchFace1(XiaomiOpple2ButtonSwitchBase): PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ - MultistateInputCluster.cluster_id, # 18 - ], - OUTPUT_CLUSTERS: [], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - }, - }, - } - - -class XiaomiOpple2ButtonSwitchFace2(XiaomiOpple2ButtonSwitchBase): - """Xiaomi Opple 2 Button Switch. Face 2.""" - - device_automation_triggers = XiaomiOpple2ButtonSwitchBase.device_automation_triggers - - signature = { - ENDPOINTS: { - # input_clusters=[0, 2, 3, 4, 5, 6, 18, 64704], output_clusters=[10, 25] - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, - INPUT_CLUSTERS: [ - Basic.cluster_id, # 0 - DeviceTemperatureCluster.cluster_id, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOff.cluster_id, # 6 - Alarms.cluster_id, # 9 - XiaomiMeteringCluster.cluster_id, # 0x0702 - 0x0B04, - ], - OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], - }, - # input_clusters=[0, 3, 4, 5, 6, 18, 64704], output_clusters=[] - 2: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, - INPUT_CLUSTERS: [ - Basic.cluster_id, # 0 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOff.cluster_id, # 6 + MultistateInputCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, diff --git a/zhaquirks/xiaomi/aqara/switch_h1.py b/zhaquirks/xiaomi/aqara/switch_h1.py deleted file mode 100644 index 1932eb7910..0000000000 --- a/zhaquirks/xiaomi/aqara/switch_h1.py +++ /dev/null @@ -1,226 +0,0 @@ -from zigpy.profiles import zgp, zha -from zigpy.quirks import CustomDevice -from zigpy.zcl.clusters.general import ( - Alarms, - Basic, - GreenPowerProxy, - Groups, - Identify, - OnOff, - Ota, - Scenes, - Time, -) - -from zhaquirks.const import ( - ARGS, - ATTR_ID, - BUTTON, - CLUSTER_ID, - COMMAND, - COMMAND_DOUBLE, - COMMAND_HOLD, - COMMAND_SINGLE, - DEVICE_TYPE, - DOUBLE_PRESS, - ENDPOINT_ID, - ENDPOINTS, - INPUT_CLUSTERS, - LONG_PRESS, - MODELS_INFO, - OUTPUT_CLUSTERS, - PRESS_TYPE, - PROFILE_ID, - SHORT_PRESS, - VALUE, -) -from zhaquirks.xiaomi import ( - LUMI, - BasicCluster, - DeviceTemperatureCluster, - OnOffCluster, - XiaomiMeteringCluster, -) -from zhaquirks.xiaomi.aqara.opple_remote import MultistateInputCluster -from zhaquirks.xiaomi.aqara.opple_switch import OppleSwitchCluster - -XIAOMI_COMMAND_SINGLE = "41_single" -XIAOMI_COMMAND_DOUBLE = "41_double" -XIAOMI_COMMAND_HOLD = "1_hold" - - -class AqaraH1SingleRockerBase(CustomDevice): - """Device automation triggers for the Aqara H1 Single Rocker Switches""" - - device_automation_triggers = { - (SHORT_PRESS, BUTTON): { - ENDPOINT_ID: 41, - CLUSTER_ID: 18, - COMMAND: XIAOMI_COMMAND_SINGLE, - ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_SINGLE, VALUE: 1}, - }, - (DOUBLE_PRESS, BUTTON): { - ENDPOINT_ID: 41, - CLUSTER_ID: 18, - COMMAND: XIAOMI_COMMAND_DOUBLE, - ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_DOUBLE, VALUE: 2}, - }, - (LONG_PRESS, BUTTON): { - ENDPOINT_ID: 1, - CLUSTER_ID: 64704, - COMMAND: XIAOMI_COMMAND_HOLD, - ARGS: {ATTR_ID: 0x00FC, PRESS_TYPE: COMMAND_HOLD, VALUE: 0}, - }, - } - - -class AqaraH1SingleRockerSwitchWithNeutral(AqaraH1SingleRockerBase): - """Aqara H1 Single Rocker Switch (with neutral).""" - - signature = { - MODELS_INFO: [(LUMI, "lumi.switch.n1aeu1")], - ENDPOINTS: { - # input_clusters=[0, 2, 3, 4, 5, 6, 18, 64704], output_clusters=[10, 25] - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, - INPUT_CLUSTERS: [ - Basic.cluster_id, # 0 - DeviceTemperatureCluster.cluster_id, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOff.cluster_id, # 6 - Alarms.cluster_id, # 9 - XiaomiMeteringCluster.cluster_id, # 0x0702 - 0x0B04, - ], - OUTPUT_CLUSTERS: [ - Time.cluster_id, # 0x000a - Ota.cluster_id, # 0x0019 - ], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [ - GreenPowerProxy.cluster_id, # 0x0021 - ], - }, - }, - } - - replacement = { - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, - INPUT_CLUSTERS: [ - BasicCluster, # 0 - DeviceTemperatureCluster.cluster_id, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOffCluster, # 6 - Alarms.cluster_id, # 9 - MultistateInputCluster, # 18 - XiaomiMeteringCluster.cluster_id, # 0x0702 - OppleSwitchCluster, # 0xFCC0 / 64704 - 0x0B04, - ], - OUTPUT_CLUSTERS: [ - Time.cluster_id, - Ota.cluster_id, - ], - }, - 41: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, - INPUT_CLUSTERS: [ - MultistateInputCluster, # 18 - ], - OUTPUT_CLUSTERS: [], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - }, - }, - } - - -class AqaraH1SingleRockerSwitchNoNeutral(AqaraH1SingleRockerBase): - """Aqara H1 Single Rocker Switch (no neutral).""" - - signature = { - MODELS_INFO: [(LUMI, "lumi.switch.l1aeu1")], - ENDPOINTS: { - # input_clusters=[0, 2, 3, 4, 5, 6, 9], output_clusters=[10, 25] - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, - INPUT_CLUSTERS: [ - Basic.cluster_id, # 0 - DeviceTemperatureCluster.cluster_id, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOff.cluster_id, # 6 - Alarms.cluster_id, # 9 - ], - OUTPUT_CLUSTERS: [ - Time.cluster_id, # 0x000a - Ota.cluster_id, # 0x0019 - ], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [ - GreenPowerProxy.cluster_id, # 0x0021 - ], - }, - }, - } - - replacement = { - ENDPOINTS: { - 1: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, - INPUT_CLUSTERS: [ - BasicCluster, # 0 - DeviceTemperatureCluster.cluster_id, # 2 - Identify.cluster_id, # 3 - Groups.cluster_id, # 4 - Scenes.cluster_id, # 5 - OnOffCluster, # 6 - Alarms.cluster_id, # 9 - MultistateInputCluster, # 18 - OppleSwitchCluster, # 0xFCC0 / 64704 - ], - OUTPUT_CLUSTERS: [ - Time.cluster_id, - Ota.cluster_id, - ], - }, - 41: { - PROFILE_ID: zha.PROFILE_ID, - DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, - INPUT_CLUSTERS: [ - MultistateInputCluster, # 18 - ], - OUTPUT_CLUSTERS: [], - }, - 242: { - PROFILE_ID: zgp.PROFILE_ID, - DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, - INPUT_CLUSTERS: [], - OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], - }, - }, - } diff --git a/zhaquirks/xiaomi/aqara/switch_h1_double.py b/zhaquirks/xiaomi/aqara/switch_h1_double.py new file mode 100644 index 0000000000..b89722450f --- /dev/null +++ b/zhaquirks/xiaomi/aqara/switch_h1_double.py @@ -0,0 +1,194 @@ +"""Aqara H1 double rocker switch quirks. Also see opple_switch.py for similar double rocker switches.""" +from zigpy.profiles import zgp, zha +from zigpy.zcl.clusters.general import ( + Alarms, + Basic, + DeviceTemperature, + GreenPowerProxy, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + Time, +) +from zigpy.zcl.clusters.smartenergy import Metering + +from zhaquirks.const import ( + DEVICE_TYPE, + ENDPOINTS, + INPUT_CLUSTERS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PROFILE_ID, +) +from zhaquirks.xiaomi import LUMI +from zhaquirks.xiaomi.aqara.opple_switch import XiaomiOpple2ButtonSwitchBase + + +class AqaraH1DoubleRockerSwitchWithNeutral(XiaomiOpple2ButtonSwitchBase): + """Aqara H1 Double Rocker Switch (with neutral).""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.switch.n2aeu1")], + ENDPOINTS: { + # input_clusters=[0, 2, 3, 4, 5, 6, 18, 64704], output_clusters=[10, 25] + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + DeviceTemperature.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + Alarms.cluster_id, + Metering.cluster_id, + 0x0B04, + ], + OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], + }, + # input_clusters=[0, 3, 4, 5, 6, 18, 64704], output_clusters=[] + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], + }, + }, + } + + +class AqaraH1DoubleRockerSwitchNoNeutral(XiaomiOpple2ButtonSwitchBase): + """Aqara H1 Double Rocker Switch (no neutral).""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.switch.l2aeu1")], + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + DeviceTemperature.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + 0xFCC0, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + 0xFCC0, + ], + OUTPUT_CLUSTERS: [], + }, + 41: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + MultistateInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 42: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + MultistateInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 51: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + MultistateInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [ + GreenPowerProxy.cluster_id, + ], + }, + }, + } + + +class AqaraH1DoubleRockerSwitchNoNeutralAlt(XiaomiOpple2ButtonSwitchBase): + """Aqara H1 Double Rocker Switch (no neutral) alternative signature.""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.switch.l2aeu1")], + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + DeviceTemperature.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + Alarms.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 2: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [ + GreenPowerProxy.cluster_id, + ], + }, + }, + } diff --git a/zhaquirks/xiaomi/aqara/switch_h1_single.py b/zhaquirks/xiaomi/aqara/switch_h1_single.py new file mode 100644 index 0000000000..b9d2f915ac --- /dev/null +++ b/zhaquirks/xiaomi/aqara/switch_h1_single.py @@ -0,0 +1,309 @@ +"""Aqara H1 single rocker switch quirks. Also see opple_switch.py for similar double rocker switches.""" +from zigpy.profiles import zgp, zha +from zigpy.quirks import CustomDevice +from zigpy.zcl.clusters.general import ( + Alarms, + AnalogInput, + Basic, + DeviceTemperature, + GreenPowerProxy, + Groups, + Identify, + MultistateInput, + OnOff, + Ota, + Scenes, + Time, +) + +from zhaquirks.const import ( + ARGS, + ATTR_ID, + BUTTON, + CLUSTER_ID, + COMMAND, + COMMAND_DOUBLE, + COMMAND_HOLD, + COMMAND_SINGLE, + DEVICE_TYPE, + DOUBLE_PRESS, + ENDPOINT_ID, + ENDPOINTS, + INPUT_CLUSTERS, + LONG_PRESS, + MODELS_INFO, + OUTPUT_CLUSTERS, + PRESS_TYPE, + PROFILE_ID, + SHORT_PRESS, + VALUE, +) +from zhaquirks.xiaomi import ( + LUMI, + AnalogInputCluster, + BasicCluster, + DeviceTemperatureCluster, + ElectricalMeasurementCluster, + MeteringCluster, + OnOffCluster, +) +from zhaquirks.xiaomi.aqara.opple_remote import MultistateInputCluster +from zhaquirks.xiaomi.aqara.opple_switch import OppleSwitchCluster + +XIAOMI_COMMAND_SINGLE = "41_single" +XIAOMI_COMMAND_DOUBLE = "41_double" +XIAOMI_COMMAND_HOLD = "1_hold" + + +class AqaraH1SingleRockerBase(CustomDevice): + """Device automation triggers for the Aqara H1 Single Rocker Switches""" + + device_automation_triggers = { + (SHORT_PRESS, BUTTON): { + ENDPOINT_ID: 41, + CLUSTER_ID: 18, + COMMAND: XIAOMI_COMMAND_SINGLE, + ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_SINGLE, VALUE: 1}, + }, + (DOUBLE_PRESS, BUTTON): { + ENDPOINT_ID: 41, + CLUSTER_ID: 18, + COMMAND: XIAOMI_COMMAND_DOUBLE, + ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_DOUBLE, VALUE: 2}, + }, + (LONG_PRESS, BUTTON): { + ENDPOINT_ID: 1, + CLUSTER_ID: 64704, + COMMAND: XIAOMI_COMMAND_HOLD, + ARGS: {ATTR_ID: 0x00FC, PRESS_TYPE: COMMAND_HOLD, VALUE: 0}, + }, + } + + +class AqaraH1SingleRockerSwitchWithNeutral(AqaraH1SingleRockerBase): + """Aqara H1 Single Rocker Switch (with neutral) (inherits above class for device automation triggers).""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.switch.n1aeu1")], + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + DeviceTemperature.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + MultistateInput.cluster_id, + OppleSwitchCluster.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 31: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 41: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + MultistateInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [ + GreenPowerProxy.cluster_id, + ], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + BasicCluster, + DeviceTemperature.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + MultistateInputCluster, + MeteringCluster, + ElectricalMeasurementCluster, + OppleSwitchCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 21: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + 31: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + AnalogInput.cluster_id, + ], + OUTPUT_CLUSTERS: [], + }, + 41: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MultistateInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [ + GreenPowerProxy.cluster_id, + ], + }, + }, + } + + +class AqaraH1SingleRockerSwitchWithNeutralAlt(AqaraH1SingleRockerSwitchWithNeutral): + """Aqara H1 Single Rocker Switch (with neutral) signature 2 (inherits above class for replacement + triggers).""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.switch.n1aeu1")], + ENDPOINTS: { + # input_clusters=[0, 2, 3, 4, 5, 6, 18, 64704], output_clusters=[10, 25] + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + DeviceTemperatureCluster.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + Alarms.cluster_id, + MeteringCluster.cluster_id, + 0x0B04, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [ + GreenPowerProxy.cluster_id, + ], + }, + }, + } + + +class AqaraH1SingleRockerSwitchNoNeutral(AqaraH1SingleRockerBase): + """Aqara H1 Single Rocker Switch (no neutral) (inherits class for device triggers).""" + + signature = { + MODELS_INFO: [(LUMI, "lumi.switch.l1aeu1")], + ENDPOINTS: { + # input_clusters=[0, 2, 3, 4, 5, 6, 9], output_clusters=[10, 25] + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, + INPUT_CLUSTERS: [ + Basic.cluster_id, + DeviceTemperatureCluster.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOff.cluster_id, + Alarms.cluster_id, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [ + GreenPowerProxy.cluster_id, + ], + }, + }, + } + + replacement = { + ENDPOINTS: { + 1: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + BasicCluster, + DeviceTemperatureCluster.cluster_id, + Identify.cluster_id, + Groups.cluster_id, + Scenes.cluster_id, + OnOffCluster, + Alarms.cluster_id, + MultistateInputCluster, + OppleSwitchCluster, + ], + OUTPUT_CLUSTERS: [ + Time.cluster_id, + Ota.cluster_id, + ], + }, + 41: { + PROFILE_ID: zha.PROFILE_ID, + DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, + INPUT_CLUSTERS: [ + MultistateInputCluster, + ], + OUTPUT_CLUSTERS: [], + }, + 242: { + PROFILE_ID: zgp.PROFILE_ID, + DEVICE_TYPE: zgp.DeviceType.PROXY_BASIC, + INPUT_CLUSTERS: [], + OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], + }, + }, + }