zigpy / zha-device-handlers

ZHA device handlers bridge the functionality gap created when manufacturers deviate from the ZCL specification, handling deviations and exceptions by parsing custom messages to and from Zigbee devices.
Apache License 2.0
705 stars 650 forks source link

Support for Aqara 2 Gang switch without neutral (lumi.switch.l2aeu1) #2270

Closed sophipl closed 1 month ago

sophipl commented 1 year ago

I would like to have the ability to trigger on double button press, which I read that this device have in Zigbee2MQTT mode. The device has automation abilities in HA on state changes (light on, light off), but not of button presses, apparently you can have the ability to disconnect button from it's internal relay and automate it differently as well.

I didn't see any pre-existing quirks. Sadly I understand too zigbee protocol too little to write this quirk myself.

Device signature ```yaml { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4447, maximum_buffer_size=127, maximum_incoming_transfer_size=100, server_mask=11264, maximum_outgoing_transfer_size=100, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0100", "in_clusters": [ "0x0000", "0x0002", "0x0003", "0x0004", "0x0005", "0x0006", "0x0009" ], "out_clusters": [ "0x000a", "0x0019" ] }, "2": { "profile_id": 260, "device_type": "0x0100", "in_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0x0012", "0xfcc0" ], "out_clusters": [] }, "242": { "profile_id": 41440, "device_type": "0x0061", "in_clusters": [], "out_clusters": [ "0x0021" ] } }, "manufacturer": "LUMI", "model": "lumi.switch.l2aeu1", "class": "zigpy.device.Device" } ```
Diagnostic information ```yaml { "home_assistant": { "installation_type": "Home Assistant Container", "version": "2023.1.6", "dev": false, "hassio": false, "virtualenv": false, "python_version": "3.10.7", "docker": true, "arch": "aarch64", "timezone": "Europe/Warsaw", "os_name": "Linux", "os_version": "5.15.84-v8+", "run_as_root": true }, "custom_components": {}, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ "bellows==0.34.6", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.90", "zigpy-deconz==0.19.2", "zigpy==0.53.0", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", "zigpy-znp==0.9.2" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "1A86", "pid": "55D4", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "vid": "10C4", "pid": "EA60", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus" ] }, { "vid": "10C4", "pid": "EA60", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*zigstar*", "known_devices": [ "ZigStar Coordinators" ] }, { "vid": "1CF1", "pid": "0030", "description": "*conbee*", "known_devices": [ "Conbee II" ] }, { "vid": "10C4", "pid": "8A2A", "description": "*zigbee*", "known_devices": [ "Nortek HUSBZB-1" ] }, { "vid": "0403", "pid": "6015", "description": "*zigate*", "known_devices": [ "ZiGate+" ] }, { "vid": "10C4", "pid": "EA60", "description": "*zigate*", "known_devices": [ "ZiGate" ] }, { "vid": "10C4", "pid": "8B34", "description": "*bv 2010/10*", "known_devices": [ "Bitron Video AV2010/10" ] } ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly" ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "dependencies": [ "file_upload" ], "after_dependencies": [ "onboarding", "usb", "zeroconf" ], "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp" ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 41887, "manufacturer": "LUMI", "model": "lumi.switch.l2aeu1", "name": "LUMI lumi.switch.l2aeu1", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4447, "power_source": "Battery or Unknown", "lqi": 156, "rssi": -61, "last_seen": "2023-03-12T17:09:49", "available": true, "device_type": "EndDevice", "signature": { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4447, maximum_buffer_size=127, maximum_incoming_transfer_size=100, server_mask=11264, maximum_outgoing_transfer_size=100, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0100", "in_clusters": [ "0x0000", "0x0002", "0x0003", "0x0004", "0x0005", "0x0006", "0x0009" ], "out_clusters": [ "0x000a", "0x0019" ] }, "2": { "profile_id": 260, "device_type": "0x0100", "in_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0x0012", "0xfcc0" ], "out_clusters": [] }, "242": { "profile_id": 41440, "device_type": "0x0061", "in_clusters": [], "out_clusters": [ "0x0021" ] } } }, "active_coordinator": false, "entities": [ { "entity_id": "button.lumi_lumi_switch_l2aeu1_identify", "name": "LUMI lumi.switch.l2aeu1" }, { "entity_id": "light.lumi_lumi_switch_l2aeu1_light", "name": "LUMI lumi.switch.l2aeu1" }, { "entity_id": "light.lumi_lumi_switch_l2aeu1_light_2", "name": "LUMI lumi.switch.l2aeu1" }, { "entity_id": "sensor.lumi_lumi_switch_l2aeu1_device_temperature", "name": "LUMI lumi.switch.l2aeu1" }, { "entity_id": "sensor.lumi_lumi_switch_l2aeu1_rssi", "name": "LUMI lumi.switch.l2aeu1" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "ON_OFF_LIGHT" }, { "name": "ON_OFF_LIGHT" }, { "name": "unknown 97 device_type of 0xa1e0 profile id" } ], "user_given_name": "\u015awiat\u0142o \u0142azienka", "device_reg_id": "9e67232a205c08488d57d75848d40f27", "area_id": "lazienka", "cluster_details": { "1": { "device_type": { "name": "ON_OFF_LIGHT", "id": 256 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0004": { "attribute_name": "manufacturer", "value": "LUMI" }, "0x0005": { "attribute_name": "model", "value": "lumi.switch.l2aeu1" }, "0x0006": { "attribute_name": "date_code", "value": "01-12-2021" } }, "unsupported_attributes": {} }, "0x0002": { "endpoint_attribute": "device_temperature", "attributes": { "0x0000": { "attribute_name": "current_temperature", "value": 30 } }, "unsupported_attributes": {} }, "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": 0 } }, "unsupported_attributes": { "0x4003": { "attribute_name": "start_up_on_off" } } }, "0x0009": { "endpoint_attribute": "alarms", "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} } } }, "2": { "device_type": { "name": "ON_OFF_LIGHT", "id": 256 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": {}, "unsupported_attributes": {} }, "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": 0 } }, "unsupported_attributes": { "0x4003": { "attribute_name": "start_up_on_off" } } }, "0x0012": { "endpoint_attribute": "multistate_input", "attributes": {}, "unsupported_attributes": {} }, "0xfcc0": { "endpoint_attribute": "manufacturer_specific", "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": {} }, "242": { "device_type": { "name": "unknown", "id": 97 }, "profile_id": 41440, "in_clusters": {}, "out_clusters": { "0x0021": { "endpoint_attribute": "green_power", "attributes": {}, "unsupported_attributes": {} } } } } } } ```
sophipl commented 1 year ago

Well, I think I got as far as I could The cluster commands works fine, I can detach relay, invert LEDs, disable anti-flicker But buttons doesn't work I sit with wireshark and HA logs, didn't manage to make it work. And it's maddening, everything is a string in quirk device_automation_triggers, how can you know how to match things and from where they came from? There is something to be said about strongly typed languages.

Quirk so far ```python import copy from enum import Enum from zigpy import types as t from zigpy.profiles import zha from zigpy.quirks import CustomDevice from zhaquirks import CustomCluster from zigpy.zcl.clusters.general import ( Basic, DeviceTemperature, Groups, Identify, OnOff, Ota, Scenes, Time, MultistateInput, GreenPowerProxy, Alarms, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, SHORT_PRESS, TRIPLE_PRESS, LONG_PRESS, ALT_DOUBLE_PRESS, DOUBLE_PRESS, ALT_SHORT_PRESS, LEFT, RIGHT, COMMAND_TOGGLE, COMMAND, BUTTON, ENDPOINT_ID, ARGS, CLUSTER_ID, ATTR_ID, PRESS_TYPE, VALUE, ) from zhaquirks.xiaomi import ( LUMI ) COMMAND_SINGLE = "single" COMMAND_DOUBLE = "double" COMMAND_BUTTON_HOLD = "button_hold" from zhaquirks.xiaomi.aqara.opple_switch import (OppleSwitchCluster, BOTH_BUTTONS) from zhaquirks.xiaomi.aqara.opple_remote import ( MultistateInputCluster, OppleCluster, OPPLE_CLUSTER_ID ) class XiaomiAqaraOpMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraFlickerMode(t.uint16_t, Enum): Anti_flicker = 0x0004 Quick = 0x0001 class XiaomiAqaraIndicatorLight(t.uint8_t, Enum): Normal = 0x00 Reverse = 0x01 class XiaomiAqaraOperationMode(t.uint8_t, Enum): """Opple operation_mode enum.""" Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraMasterCluster(OppleCluster): attributes = copy.deepcopy(OppleCluster.attributes) attributes.update( { 0x0004: ("anti_flicker", XiaomiAqaraFlickerMode, True), 0x0002: ("power_outage_count", t.uint8_t, True), 0x0201: ("power_outage_memory", t.Bool, True), 0x0203: ("led_disabled_night", t.Bool, True), 0x00F0: ("indicator_light", XiaomiAqaraIndicatorLight, True), 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) class XiaomiAqaraSecondaryCluster(CustomCluster): ep_attribute = "opple_cluster" cluster_id = OPPLE_CLUSTER_ID attributes = ( { 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) class XiaomiAqaraWSEUK02(CustomDevice): """WS-EUK02 Aqara smart wall switch H1 EU (no neutral, double rocker)""" 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, MultistateInput.cluster_id, OppleSwitchCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { 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, XiaomiAqaraMasterCluster, ], 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, MultistateInputCluster, XiaomiAqaraSecondaryCluster, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, } } device_automation_triggers = { (SHORT_PRESS, LEFT): {ENDPOINT_ID:41, CLUSTER_ID : MultistateInputCluster.cluster_id, ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_SINGLE, VALUE: 1}}, (DOUBLE_PRESS, LEFT): {ENDPOINT_ID:41, CLUSTER_ID : MultistateInputCluster.cluster_id, ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_DOUBLE, VALUE: 2}}, (SHORT_PRESS, RIGHT): {ENDPOINT_ID:42, CLUSTER_ID : MultistateInputCluster.cluster_id, ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_SINGLE, VALUE: 1}}, (DOUBLE_PRESS, RIGHT): {ENDPOINT_ID:42, CLUSTER_ID : MultistateInputCluster.cluster_id, ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_DOUBLE, VALUE: 2}}, (SHORT_PRESS, BOTH_BUTTONS): {ENDPOINT_ID:42, CLUSTER_ID : MultistateInputCluster.cluster_id, ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_SINGLE, VALUE: 1}}, (DOUBLE_PRESS, BOTH_BUTTONS): {ENDPOINT_ID:42, CLUSTER_ID : MultistateInputCluster.cluster_id, ARGS: {ATTR_ID: 0x0055, PRESS_TYPE: COMMAND_DOUBLE, VALUE: 2}}, (COMMAND_BUTTON_HOLD, BOTH_BUTTONS): {ENDPOINT_ID: 1, CLUSTER_ID: XiaomiAqaraMasterCluster.cluster_id, ARGS: {ATTR_ID: 0x00FC, VALUE: 0, PRESS_TYPE: COMMAND_BUTTON_HOLD}}, } ```

The buttons work like this, left button is source_endpoint 41, right button is source_endpoint 42, both buttons is 51 It is sending value of uint16 of 1 or 2, for one press or 2 presses, attribute id 0x0055 (Preset value) When you press 3 times, it does something funny, it introduces itself, sends string value with model information. Holding a button sends data for cluster 0xfcc0 with value false of boolean, attribute id 0x00fc

It seems that ZHA cannot decode the values as event, I don't know, just a packet


2023-03-24 22:06:34.973 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD3A6), src_ep=41, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000),dst_ep=1, source_route=None, extended_timeout=False, tsn=116, profile_id=260, cluster_id=18, data=Serialized[b'\x18>\nU\x00!\x02\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-34)
2023-03-24 22:05:06.802 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD3A6), src_ep=41, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000),dst_ep=1, source_route=None, extended_timeout=False, tsn=109, profile_id=260, cluster_id=18, data=Serialized[b'\x188\nU\x00!\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-33)
2023-03-24 22:05:08.801 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD3A6), src_ep=41, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000),dst_ep=1, source_route=None, extended_timeout=False, tsn=110, profile_id=260, cluster_id=18, data=Serialized[b'\x189\nU\x00!\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-33)
2023-03-24 22:39:43.094 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD3A6), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=161, profile_id=260, cluster_id=64704, data=Serialized[b'\x1c_\x11L\n\xfc\x00\x10\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-34)
2023-03-24 22:05:09.654 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD3A6), src_ep=42, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000),dst_ep=1, source_route=None, extended_timeout=False, tsn=112, profile_id=260, cluster_id=18, data=Serialized[b'\x18;\nU\x00!\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-33)
2023-03-24 22:25:20.180 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD3A6), src_ep=51, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=141, profile_id=260, cluster_id=18, data=Serialized[b'\x18E\nU\x00!\x01\x00'], tx_options=<TransmitOptions.NONE: 0>, radius=0, non_member_radius=0, lqi=255, rssi=-34)

Maybe you will be able to fix the problem, my head explodes now :)

sophipl commented 1 year ago

I managed to do it, it took some effort I don't know why device_automation_triggers didn't wanted to match at some point of my development, so I limited it to COMMAND

If someone want's to use it, please go ahead

aqara_i2aeu1.py ```python import copy from enum import Enum from zigpy import types as t from zigpy.profiles import zha from zigpy.quirks import CustomDevice from zhaquirks import CustomCluster from zigpy.zcl.clusters.general import ( Basic, DeviceTemperature, Groups, Identify, OnOff, Ota, Scenes, Time, MultistateInput, GreenPowerProxy, Alarms, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, SHORT_PRESS, TRIPLE_PRESS, LONG_PRESS, ALT_DOUBLE_PRESS, DOUBLE_PRESS, ALT_SHORT_PRESS, LEFT, RIGHT, COMMAND_TOGGLE, COMMAND, BUTTON, ENDPOINT_ID, ARGS, CLUSTER_ID, ATTR_ID, PRESS_TYPE, VALUE, ZHA_SEND_EVENT, COMMAND_HOLD, COMMAND_TRIPLE ) from zhaquirks.xiaomi import ( LUMI ) COMMAND_SINGLE = "single" COMMAND_DOUBLE = "double" BUTTON_ANY = "any" LEFT_BUTTON_ENDPOINT=41 RIGHT_BUTTON_ENDPOINT=42 BOTH_BUTTONS_ENDPOINT=51 from zhaquirks.xiaomi.aqara.opple_switch import (OppleSwitchCluster, BOTH_BUTTONS) from zhaquirks.xiaomi.aqara.opple_remote import ( OppleCluster, OPPLE_CLUSTER_ID, STATUS_TYPE_ATTR, PRESS_TYPES ) class XiaomiAqaraOpMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraFlickerMode(t.uint16_t, Enum): Anti_flicker = 0x0004 Quick = 0x0001 class XiaomiAqaraIndicatorLight(t.uint8_t, Enum): Normal = 0x00 Reverse = 0x01 class XiaomiAqaraOperationMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraMasterCluster(OppleCluster): attributes = copy.deepcopy(OppleCluster.attributes) attributes.update( { 0x0004: ("anti_flicker", XiaomiAqaraFlickerMode, True), 0x0002: ("power_outage_count", t.uint8_t, True), 0x0201: ("power_outage_memory", t.Bool, True), 0x0203: ("led_disabled_night", t.Bool, True), 0x00F0: ("indicator_light", XiaomiAqaraIndicatorLight, True), 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == 0x00FC and value == False: event_args = { BUTTON: BUTTON_ANY, PRESS_TYPE: COMMAND_HOLD, ATTR_ID: attrid, VALUE: value, } action = "{}_{}".format(BUTTON_ANY, COMMAND_HOLD) self.listener_event(ZHA_SEND_EVENT, action, event_args) super()._update_attribute(0, action) # elif attrid == 0x00f7: # event_args = { # BUTTON: BUTTON_ANY, # PRESS_TYPE: COMMAND_TRIPLE, # ATTR_ID: attrid # } # action = "{}_{}".format(BUTTON_ANY, COMMAND_TRIPLE) # self.listener_event(ZHA_SEND_EVENT, action, event_args) # super()._update_attribute(0, action) class XiaomiAqaraSecondaryCluster(CustomCluster): ep_attribute = "opple_cluster" cluster_id = OPPLE_CLUSTER_ID attributes = ( { 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) class MultistateInputCluster(CustomCluster, MultistateInput): cluster_id = MultistateInput.cluster_id def __init__(self, *args, **kwargs): self._current_state = None super().__init__(*args, **kwargs) async def configure_reporting( self, attribute, min_interval, max_interval, reportable_change, manufacturer=None, ): """Configure reporting.""" def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == STATUS_TYPE_ATTR: self._current_state = PRESS_TYPES.get(value) event_args = { BUTTON: self.endpoint.endpoint_id, PRESS_TYPE: self._current_state, ATTR_ID: attrid, VALUE: value, } button = None if self.endpoint.endpoint_id == LEFT_BUTTON_ENDPOINT: button=LEFT elif self.endpoint.endpoint_id == RIGHT_BUTTON_ENDPOINT: button=RIGHT elif self.endpoint.endpoint_id == BOTH_BUTTONS_ENDPOINT: button=BOTH_BUTTONS action=action = "{}_{}".format(self.endpoint.endpoint_id, self._current_state) if button != None: action = "{}_{}".format(button, self._current_state) self.listener_event(ZHA_SEND_EVENT, action, event_args) super()._update_attribute(0, action) class XiaomiAqaraWSEUK02(CustomDevice): """WS-EUK02 Aqara smart wall switch H1 EU (no neutral, double rocker)""" 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, MultistateInput.cluster_id, OppleSwitchCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { 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, MultistateInputCluster, XiaomiAqaraMasterCluster, ], 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, MultistateInputCluster, XiaomiAqaraSecondaryCluster, ], OUTPUT_CLUSTERS: [], }, LEFT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, RIGHT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, BOTH_BUTTONS_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, } } device_automation_triggers = { (SHORT_PRESS, LEFT): { COMMAND: "{}_{}".format(LEFT, COMMAND_SINGLE)}, (DOUBLE_PRESS, LEFT): { COMMAND: "{}_{}".format(LEFT, COMMAND_DOUBLE)}, (SHORT_PRESS, RIGHT): { COMMAND: "{}_{}".format(RIGHT, COMMAND_SINGLE)}, (DOUBLE_PRESS, RIGHT): { COMMAND: "{}_{}".format(RIGHT, COMMAND_DOUBLE)}, (SHORT_PRESS, BOTH_BUTTONS): { COMMAND: "{}_{}".format(BOTH_BUTTONS, COMMAND_SINGLE)}, (DOUBLE_PRESS, BOTH_BUTTONS): { COMMAND: "{}_{}".format(BOTH_BUTTONS, COMMAND_DOUBLE)}, (COMMAND_HOLD, BUTTON): { COMMAND: "{}_{}".format(BUTTON_ANY, COMMAND_HOLD)}, } ```
sophipl commented 1 year ago

FYI @TheJulianJES If someone thinks it's worth integrating

TheJulianJES commented 1 year ago

We can leave this open for now (until zha-quirks maybe fixes this).

grzegor commented 1 year ago

@sophipl thank you! Decoupling worked and both the single and double press automations work flawlessly!

sophipl commented 1 year ago

@grzegor Thanks! :)

cpressland commented 1 year ago

@sophipl hello! I just received my lumi.switch.l2aeu1 today. I've loaded your quirk into Home Assistant and can confirm it's being used by ZHA. The only thing I'm not sure about is how to put the device in decoupled mode, any advice you can give me?

EDIT: I can see two clusters: XiaomiAqaraMasterCluster & XiaomiAqaraSecondaryCluster, and within each there is an operation_mode (id: 0x0200), Reading the value of this on either returns XiaomiAqaraOperationMode.Coupled. So I'm absolutely in the right place, I just can't figure out what to write back to move it to Decoupled.

EDIT2: Send a zero. I tried everything except writing a zero. Sorry! Thanks for the Quirk. You're awesome.

sophipl commented 1 year ago

@cpressland Great that you figured out! I don't think there is a way to export the value into selection box in HA, so we have to stumble in the menus. :)

Leworoe commented 1 year ago

I needed to edit this quirk. I don't know why my switch has more ENDPOINTS in the signature. The INPUT_CLUSTERS in the first endpoint differ.

Device signature ```json { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4447, maximum_buffer_size=127, maximum_incoming_transfer_size=100, server_mask=11264, maximum_outgoing_transfer_size=100, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0000", "0x0002", "0x0003", "0x0004", "0x0005", "0x0006", "0x0012", "0xfcc0" ], "output_clusters": [ "0x000a", "0x0019" ] }, "2": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0x0012", "0xfcc0" ], "output_clusters": [] }, "41": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0012" ], "output_clusters": [] }, "42": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0012" ], "output_clusters": [] }, "51": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0012" ], "output_clusters": [] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "LUMI", "model": "lumi.switch.l2aeu1" } ```
The edited quirk ```python import copy from enum import Enum from zigpy import types as t from zigpy.profiles import zha from zigpy.quirks import CustomDevice from zhaquirks import CustomCluster from zigpy.zcl.clusters.general import ( Basic, DeviceTemperature, Groups, Identify, OnOff, Ota, Scenes, Time, MultistateInput, GreenPowerProxy, Alarms, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, SHORT_PRESS, TRIPLE_PRESS, LONG_PRESS, ALT_DOUBLE_PRESS, DOUBLE_PRESS, ALT_SHORT_PRESS, LEFT, RIGHT, COMMAND_TOGGLE, COMMAND, BUTTON, ENDPOINT_ID, ARGS, CLUSTER_ID, ATTR_ID, PRESS_TYPE, VALUE, ZHA_SEND_EVENT, COMMAND_HOLD, COMMAND_TRIPLE ) from zhaquirks.xiaomi import ( LUMI ) COMMAND_SINGLE = "single" COMMAND_DOUBLE = "double" BUTTON_ANY = "any" LEFT_BUTTON_ENDPOINT=41 RIGHT_BUTTON_ENDPOINT=42 BOTH_BUTTONS_ENDPOINT=51 from zhaquirks.xiaomi.aqara.opple_switch import (OppleSwitchCluster, BOTH_BUTTONS) from zhaquirks.xiaomi.aqara.opple_remote import ( OppleCluster, OPPLE_CLUSTER_ID, STATUS_TYPE_ATTR, PRESS_TYPES ) class XiaomiAqaraOpMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraFlickerMode(t.uint16_t, Enum): Anti_flicker = 0x0004 Quick = 0x0001 class XiaomiAqaraIndicatorLight(t.uint8_t, Enum): Normal = 0x00 Reverse = 0x01 class XiaomiAqaraOperationMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraMasterCluster(OppleCluster): attributes = copy.deepcopy(OppleCluster.attributes) attributes.update( { 0x0004: ("anti_flicker", XiaomiAqaraFlickerMode, True), 0x0002: ("power_outage_count", t.uint8_t, True), 0x0201: ("power_outage_memory", t.Bool, True), 0x0203: ("led_disabled_night", t.Bool, True), 0x00F0: ("indicator_light", XiaomiAqaraIndicatorLight, True), 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == 0x00FC and value == False: event_args = { BUTTON: BUTTON_ANY, PRESS_TYPE: COMMAND_HOLD, ATTR_ID: attrid, VALUE: value, } action = "{}_{}".format(BUTTON_ANY, COMMAND_HOLD) self.listener_event(ZHA_SEND_EVENT, action, event_args) super()._update_attribute(0, action) # elif attrid == 0x00f7: # event_args = { # BUTTON: BUTTON_ANY, # PRESS_TYPE: COMMAND_TRIPLE, # ATTR_ID: attrid # } # action = "{}_{}".format(BUTTON_ANY, COMMAND_TRIPLE) # self.listener_event(ZHA_SEND_EVENT, action, event_args) # super()._update_attribute(0, action) class XiaomiAqaraSecondaryCluster(CustomCluster): ep_attribute = "opple_cluster" cluster_id = OPPLE_CLUSTER_ID attributes = ( { 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) class MultistateInputCluster(CustomCluster, MultistateInput): cluster_id = MultistateInput.cluster_id def __init__(self, *args, **kwargs): self._current_state = None super().__init__(*args, **kwargs) async def configure_reporting( self, attribute, min_interval, max_interval, reportable_change, manufacturer=None, ): """Configure reporting.""" def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == STATUS_TYPE_ATTR: self._current_state = PRESS_TYPES.get(value) event_args = { BUTTON: self.endpoint.endpoint_id, PRESS_TYPE: self._current_state, ATTR_ID: attrid, VALUE: value, } button = None if self.endpoint.endpoint_id == LEFT_BUTTON_ENDPOINT: button=LEFT elif self.endpoint.endpoint_id == RIGHT_BUTTON_ENDPOINT: button=RIGHT elif self.endpoint.endpoint_id == BOTH_BUTTONS_ENDPOINT: button=BOTH_BUTTONS action=action = "{}_{}".format(self.endpoint.endpoint_id, self._current_state) if button != None: action = "{}_{}".format(button, self._current_state) self.listener_event(ZHA_SEND_EVENT, action, event_args) super()._update_attribute(0, action) class XiaomiAqaraWSEUK02(CustomDevice): """WS-EUK02 Aqara smart wall switch H1 EU (no neutral, double rocker)""" 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, OppleSwitchCluster.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, MultistateInput.cluster_id, OppleSwitchCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, LEFT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInput.cluster_id, ], OUTPUT_CLUSTERS: [], }, RIGHT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInput.cluster_id, ], OUTPUT_CLUSTERS: [], }, BOTH_BUTTONS_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInput.cluster_id, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { 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, MultistateInputCluster, XiaomiAqaraMasterCluster, ], 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, MultistateInputCluster, XiaomiAqaraSecondaryCluster, ], OUTPUT_CLUSTERS: [], }, LEFT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, RIGHT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, BOTH_BUTTONS_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, } } device_automation_triggers = { (SHORT_PRESS, LEFT): { COMMAND: "{}_{}".format(LEFT, COMMAND_SINGLE)}, (DOUBLE_PRESS, LEFT): { COMMAND: "{}_{}".format(LEFT, COMMAND_DOUBLE)}, (SHORT_PRESS, RIGHT): { COMMAND: "{}_{}".format(RIGHT, COMMAND_SINGLE)}, (DOUBLE_PRESS, RIGHT): { COMMAND: "{}_{}".format(RIGHT, COMMAND_DOUBLE)}, (SHORT_PRESS, BOTH_BUTTONS): { COMMAND: "{}_{}".format(BOTH_BUTTONS, COMMAND_SINGLE)}, (DOUBLE_PRESS, BOTH_BUTTONS): { COMMAND: "{}_{}".format(BOTH_BUTTONS, COMMAND_DOUBLE)}, (COMMAND_HOLD, BUTTON): { COMMAND: "{}_{}".format(BUTTON_ANY, COMMAND_HOLD)}, } ```
ardstuebck commented 9 months ago

Hi, unfortunately, neither of the quirks posted here work for my device. I don't receive any events when listening to "zha_event." My device is shown as two lights, and the right button doesn't change when I press the button physically. Can anyone help me?

My device signature ```json { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4447, maximum_buffer_size=127, maximum_incoming_transfer_size=100, server_mask=11264, maximum_outgoing_transfer_size=100, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0000", "0x0002", "0x0003", "0x0004", "0x0005", "0x0006", "0x0009" ], "output_clusters": [ "0x000a", "0x0019" ] }, "2": { "profile_id": "0x0104", "device_type": "0x0100", "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006" ], "output_clusters": [] }, "242": { "profile_id": "0xa1e0", "device_type": "0x0061", "input_clusters": [], "output_clusters": [ "0x0021" ] } }, "manufacturer": "LUMI", "model": "lumi.switch.l2aeu1", "class": "zigpy.device.Device" } ```
theorlangur commented 8 months ago

@sophipl , thanks for the quirk, it works for me (l2aeu1) for anyone attempting to apply it as-is: it seems that it is a little bit outdated and there is no 'OPPLE_CLUSTER_ID':

from zhaquirks.xiaomi.aqara.opple_remote import (
    OppleCluster,
    OPPLE_CLUSTER_ID,
    STATUS_TYPE_ATTR,
    PRESS_TYPES
)

my fix was to remove OPPLE_CLUSTER_ID from the imports and replace all usages in this file with OppleCluster.cluster_id

updated version of aqara_l2aeu1.py ````python import copy from enum import Enum from zigpy import types as t from zigpy.profiles import zha from zigpy.quirks import CustomDevice from zhaquirks import CustomCluster from zigpy.zcl.clusters.general import ( Basic, DeviceTemperature, Groups, Identify, OnOff, Ota, Scenes, Time, MultistateInput, GreenPowerProxy, Alarms, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, SHORT_PRESS, TRIPLE_PRESS, LONG_PRESS, ALT_DOUBLE_PRESS, DOUBLE_PRESS, ALT_SHORT_PRESS, LEFT, RIGHT, COMMAND_TOGGLE, COMMAND, BUTTON, ENDPOINT_ID, ARGS, CLUSTER_ID, ATTR_ID, PRESS_TYPE, VALUE, ZHA_SEND_EVENT, COMMAND_HOLD, COMMAND_TRIPLE ) from zhaquirks.xiaomi import ( LUMI ) COMMAND_SINGLE = "single" COMMAND_DOUBLE = "double" BUTTON_ANY = "any" LEFT_BUTTON_ENDPOINT=41 RIGHT_BUTTON_ENDPOINT=42 BOTH_BUTTONS_ENDPOINT=51 from zhaquirks.xiaomi.aqara.opple_switch import (OppleSwitchCluster, BOTH_BUTTONS) from zhaquirks.xiaomi.aqara.opple_remote import ( OppleCluster, STATUS_TYPE_ATTR, PRESS_TYPES ) class XiaomiAqaraOpMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraFlickerMode(t.uint16_t, Enum): Anti_flicker = 0x0004 Quick = 0x0001 class XiaomiAqaraIndicatorLight(t.uint8_t, Enum): Normal = 0x00 Reverse = 0x01 class XiaomiAqaraOperationMode(t.uint8_t, Enum): Decoupled = 0x00 Coupled = 0x01 class XiaomiAqaraMasterCluster(OppleCluster): attributes = copy.deepcopy(OppleCluster.attributes) attributes.update( { 0x0004: ("anti_flicker", XiaomiAqaraFlickerMode, True), 0x0002: ("power_outage_count", t.uint8_t, True), 0x0201: ("power_outage_memory", t.Bool, True), 0x0203: ("led_disabled_night", t.Bool, True), 0x00F0: ("indicator_light", XiaomiAqaraIndicatorLight, True), 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == 0x00FC and value == False: event_args = { BUTTON: BUTTON_ANY, PRESS_TYPE: COMMAND_HOLD, ATTR_ID: attrid, VALUE: value, } action = "{}_{}".format(BUTTON_ANY, COMMAND_HOLD) self.listener_event(ZHA_SEND_EVENT, action, event_args) super()._update_attribute(0, action) # elif attrid == 0x00f7: # event_args = { # BUTTON: BUTTON_ANY, # PRESS_TYPE: COMMAND_TRIPLE, # ATTR_ID: attrid # } # action = "{}_{}".format(BUTTON_ANY, COMMAND_TRIPLE) # self.listener_event(ZHA_SEND_EVENT, action, event_args) # super()._update_attribute(0, action) class XiaomiAqaraSecondaryCluster(CustomCluster): ep_attribute = "opple_cluster" cluster_id = OppleCluster.cluster_id attributes = ( { 0x0200: ("operation_mode", XiaomiAqaraOperationMode, True), } ) class MultistateInputCluster(CustomCluster, MultistateInput): cluster_id = MultistateInput.cluster_id def __init__(self, *args, **kwargs): self._current_state = None super().__init__(*args, **kwargs) async def configure_reporting( self, attribute, min_interval, max_interval, reportable_change, manufacturer=None, ): """Configure reporting.""" def _update_attribute(self, attrid, value): super()._update_attribute(attrid, value) if attrid == STATUS_TYPE_ATTR: self._current_state = PRESS_TYPES.get(value) event_args = { BUTTON: self.endpoint.endpoint_id, PRESS_TYPE: self._current_state, ATTR_ID: attrid, VALUE: value, } button = None if self.endpoint.endpoint_id == LEFT_BUTTON_ENDPOINT: button=LEFT elif self.endpoint.endpoint_id == RIGHT_BUTTON_ENDPOINT: button=RIGHT elif self.endpoint.endpoint_id == BOTH_BUTTONS_ENDPOINT: button=BOTH_BUTTONS action=action = "{}_{}".format(self.endpoint.endpoint_id, self._current_state) if button != None: action = "{}_{}".format(button, self._current_state) self.listener_event(ZHA_SEND_EVENT, action, event_args) super()._update_attribute(0, action) class XiaomiAqaraWSEUK02(CustomDevice): """WS-EUK02 Aqara smart wall switch H1 EU (no neutral, double rocker)""" 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, MultistateInput.cluster_id, OppleSwitchCluster.cluster_id, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, }, } replacement = { 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, MultistateInputCluster, XiaomiAqaraMasterCluster, ], 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, MultistateInputCluster, XiaomiAqaraSecondaryCluster, ], OUTPUT_CLUSTERS: [], }, LEFT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, RIGHT_BUTTON_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, BOTH_BUTTONS_ENDPOINT: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT, INPUT_CLUSTERS: [ MultistateInputCluster, ], OUTPUT_CLUSTERS: [], }, 242: { PROFILE_ID: 0xA1E0, DEVICE_TYPE: 0x0061, INPUT_CLUSTERS: [], OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id], }, } } device_automation_triggers = { (SHORT_PRESS, LEFT): { COMMAND: "{}_{}".format(LEFT, COMMAND_SINGLE)}, (DOUBLE_PRESS, LEFT): { COMMAND: "{}_{}".format(LEFT, COMMAND_DOUBLE)}, (SHORT_PRESS, RIGHT): { COMMAND: "{}_{}".format(RIGHT, COMMAND_SINGLE)}, (DOUBLE_PRESS, RIGHT): { COMMAND: "{}_{}".format(RIGHT, COMMAND_DOUBLE)}, (SHORT_PRESS, BOTH_BUTTONS): { COMMAND: "{}_{}".format(BOTH_BUTTONS, COMMAND_SINGLE)}, (DOUBLE_PRESS, BOTH_BUTTONS): { COMMAND: "{}_{}".format(BOTH_BUTTONS, COMMAND_DOUBLE)}, (COMMAND_HOLD, BUTTON): { COMMAND: "{}_{}".format(BUTTON_ANY, COMMAND_HOLD)}, } ````
github-actions[bot] commented 1 month ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.