zha-device-handlers icon indicating copy to clipboard operation
zha-device-handlers copied to clipboard

[Device Support Request] Aqara E1 Single Rocker Remote Switch - WXKG16LM

Open bartlomiejmaciejowski opened this issue 3 years ago • 15 comments

Is your feature request related to a problem? Please describe. The problem is that the device does not trigger zha_event.

Describe the solution you'd like I would like the device to be supported and working :)

Device signature
{
  "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4447, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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": "0x0000",
      "in_clusters": [
        "0x0000",
        "0x0001",
        "0x0003",
        "0x0012",
        "0xfcc0"
      ],
      "out_clusters": [
        "0x0003",
        "0x0006",
        "0x0019"
      ]
    }
  },
  "manufacturer": "LUMI",
  "model": "lumi.remote.acn003",
  "class": "zigpy.device.Device"
}
Diagnostic information
{
  "home_assistant": {
    "installation_type": "Home Assistant Container",
    "version": "2022.9.7",
    "dev": false,
    "hassio": false,
    "virtualenv": false,
    "python_version": "3.10.5",
    "docker": true,
    "arch": "armv7l",
    "timezone": "Europe/Warsaw",
    "os_name": "Linux",
    "os_version": "5.10.103-v7l+",
    "run_as_root": true
  },
  "custom_components": {
    "philips_airpurifier_coap": {
      "version": "0.7.0",
      "requirements": [
        "aioairctrl @ git+https://github.com/betaboon/[email protected]"
      ]
    },
    "ble_monitor": {
      "version": "10.4.0",
      "requirements": [
        "pycryptodomex>=3.14.1",
        "janus>=1.0.0",
        "aioblescan>=0.2.13",
        "btsocket>=0.2.0",
        "pyric>=0.1.6.3"
      ]
    },
    "sonoff": {
      "version": "3.3.1",
      "requirements": [
        "pycryptodome>=3.6.6"
      ]
    },
    "smartthinq_sensors": {
      "version": "0.24.2",
      "requirements": [
        "pycountry>=20.7.3",
        "xmltodict>=0.12.0",
        "cchardet>=2.1.7"
      ]
    },
    "hacs": {
      "version": "1.27.2",
      "requirements": [
        "aiogithubapi>=22.2.4"
      ]
    },
    "localtuya": {
      "version": "4.1.0",
      "requirements": []
    },
    "xiaomi_cloud_map_extractor": {
      "version": "v2.2.0",
      "requirements": [
        "pillow",
        "pybase64",
        "python-miio",
        "requests",
        "pycryptodome"
      ]
    }
  },
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "config_flow": true,
    "documentation": "https://www.home-assistant.io/integrations/zha",
    "requirements": [
      "bellows==0.33.1",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.79",
      "zigpy-deconz==0.18.1",
      "zigpy==0.50.3",
      "zigpy-xbee==0.15.0",
      "zigpy-zigate==0.9.2",
      "zigpy-znp==0.8.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*"
      }
    ],
    "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": 26415,
    "manufacturer": "LUMI",
    "model": "lumi.remote.acn003",
    "name": "LUMI lumi.remote.acn003",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "manufacturer_code": 4447,
    "power_source": "Battery or Unknown",
    "lqi": 255,
    "rssi": -45,
    "last_seen": "2022-10-05T07:36:05",
    "available": true,
    "device_type": "EndDevice",
    "signature": {
      "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.EndDevice: 2>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.AllocateAddress: 128>, manufacturer_code=4447, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *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": "0x0000",
          "in_clusters": [
            "0x0000",
            "0x0001",
            "0x0003",
            "0x0012",
            "0xfcc0"
          ],
          "out_clusters": [
            "0x0003",
            "0x0006",
            "0x0019"
          ]
        }
      }
    },
    "active_coordinator": false,
    "entities": [
      {
        "entity_id": "sensor.lumi_lumi_remote_acn003_battery_2",
        "name": "LUMI lumi.remote.acn003"
      },
      {
        "entity_id": "button.lumi_lumi_remote_acn003_identifybutton_2",
        "name": "LUMI lumi.remote.acn003"
      }
    ],
    "neighbors": [],
    "endpoint_names": [
      {
        "name": "ON_OFF_SWITCH"
      }
    ],
    "user_given_name": null,
    "device_reg_id": "de47e3ed1ea0efc162da43e974eec63f",
    "area_id": null,
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "ON_OFF_SWITCH",
          "id": 0
        },
        "profile_id": 260,
        "in_clusters": {
          "0x0000": {
            "endpoint_attribute": "basic",
            "attributes": {
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "LUMI"
              },
              "0x0005": {
                "attribute_name": "model",
                "value": "lumi.remote.acn003"
              }
            },
            "unsupported_attributes": {}
          },
          "0x0001": {
            "endpoint_attribute": "power",
            "attributes": {
              "0x0020": {
                "attribute_name": "battery_voltage",
                "value": 29
              }
            },
            "unsupported_attributes": {
              "0x0020": {
                "attribute_name": "battery_voltage"
              },
              "0x0021": {
                "attribute_name": "battery_percentage_remaining"
              },
              "0x0031": {
                "attribute_name": "battery_size"
              },
              "0x0033": {
                "attribute_name": "battery_quantity"
              }
            }
          },
          "0x0003": {
            "endpoint_attribute": "identify",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0012": {
            "endpoint_attribute": "multistate_input",
            "attributes": {
              "0x0055": {
                "attribute_name": "present_value",
                "value": 1.0
              }
            },
            "unsupported_attributes": {}
          },
          "0xfcc0": {
            "endpoint_attribute": "manufacturer_specific",
            "attributes": {},
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x0003": {
            "endpoint_attribute": "identify",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0006": {
            "endpoint_attribute": "on_off",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}
Additional logs
2022-10-05 07:25:51.678 DEBUG (MainThread) [zigpy.zcl] [0xF5EB:1:0x0012] Received ZCL frame: b'\x18)\nU\x00!\x01\x00'
2022-10-05 07:25:51.679 DEBUG (MainThread) [zigpy.zcl] [0xF5EB:1:0x0012] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.GLOBAL_COMMAND: 0>, is_manufacturer_specific=0, direction=<Direction.Client_to_Server: 1>, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True, *is_reply=True), tsn=41, command_id=10, *direction=<Direction.Client_to_Server: 1>, *is_reply=True)
2022-10-05 07:25:51.680 DEBUG (MainThread) [zigpy.zcl] [0xF5EB:1:0x0012] Decoded ZCL frame: MultistateInput:Report_Attributes(attribute_reports=[Attribute(attrid=0x0055, value=TypeValue(type=uint16_t, value=1))])
2022-10-05 07:25:51.680 DEBUG (MainThread) [zigpy.zcl] [0xF5EB:1:0x0012] Received command 0x0A (TSN 41): Report_Attributes(attribute_reports=[Attribute(attrid=0x0055, value=TypeValue(type=uint16_t, value=1))])
2022-10-05 07:25:51.680 DEBUG (MainThread) [zigpy.zcl] [0xF5EB:1:0x0012] Attribute report received: present_value=1

(the log entries above show when the button is pressed on the device)

Additional context Add any other context or screenshots about the feature request here.

bartlomiejmaciejowski avatar Oct 05 '22 08:10 bartlomiejmaciejowski

My quick & dirty proposal would be:

"""Aqara E1-series wireless remote."""
from zigpy.profiles import zha
from zigpy.zcl.clusters.general import (
    Basic,
    Identify,
    MultistateInput,
    OnOff,
    Ota,
    PowerConfiguration,
)

from zhaquirks.const import (
    ALT_DOUBLE_PRESS,
    ALT_SHORT_PRESS,
    ARGS,
    BUTTON,
    COMMAND,
    COMMAND_OFF,
    COMMAND_TOGGLE,
    DEVICE_TYPE,
    DOUBLE_PRESS,
    ENDPOINT_ID,
    ENDPOINTS,
    INPUT_CLUSTERS,
    LEFT,
    LONG_PRESS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
    RIGHT,
    SHORT_PRESS,
    TRIPLE_PRESS,
)
from zhaquirks.xiaomi import (
    LUMI,
    BasicCluster,
    XiaomiCustomDevice,
)
from zhaquirks.xiaomi.aqara.opple_remote import (
    COMMAND_1_DOUBLE,
    COMMAND_1_HOLD,
    COMMAND_1_SINGLE,
    COMMAND_1_TRIPLE,
    COMMAND_2_DOUBLE,
    COMMAND_2_HOLD,
    COMMAND_2_SINGLE,
    COMMAND_2_TRIPLE,
    COMMAND_3_DOUBLE,
    COMMAND_3_HOLD,
    COMMAND_3_SINGLE,
    COMMAND_3_TRIPLE,
    MultistateInputCluster,
)
from zhaquirks.xiaomi.aqara.remote_h1 import (
    AqaraRemoteManuSpecificCluster,
    PowerConfigurationClusterH1Remote,
)

BOTH_BUTTONS = "both_buttons"


class RemoteE1SingleRocker1(XiaomiCustomDevice):
    """Aqara E1 Wireless Remote Double Rocker."""

    manufacturer_id_override = 0x115F

    signature = {
        MODELS_INFO: [(LUMI, "lumi.remote.acn003")],
        ENDPOINTS: {
            1: {
                # SizePrefixedSimpleDescriptor(
                #   endpoint=1, profile=260, device_type=0,
                #   input_clusters=["0x0000", "0x0001", "0x0003", "0x0012", "0xfcc0"],
                #   output_clusters=["0x0003", "0x0006", "0x0019"])
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    MultistateInput.cluster_id,
                    AqaraRemoteManuSpecificCluster.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    Identify.cluster_id,
                    OnOff.cluster_id,
                    Ota.cluster_id,
                ],
            },
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
                INPUT_CLUSTERS: [
                    BasicCluster,
                    Identify.cluster_id,
                    PowerConfigurationClusterH1Remote,
                    MultistateInputCluster,
                    AqaraRemoteManuSpecificCluster,
                ],
                OUTPUT_CLUSTERS: [
                    Identify.cluster_id,
                    OnOff.cluster_id,
                    Ota.cluster_id,
                ],
            },
        }
    }
    device_automation_triggers = {
        # triggers when operation_mode == event
        # the button doesn't send an release event after hold
        (SHORT_PRESS, LEFT): {COMMAND: COMMAND_1_SINGLE},
        (DOUBLE_PRESS, LEFT): {COMMAND: COMMAND_1_DOUBLE},
        (TRIPLE_PRESS, LEFT): {COMMAND: COMMAND_1_TRIPLE},
        (LONG_PRESS, LEFT): {COMMAND: COMMAND_1_HOLD},
        # triggers when operation_mode == command
        (ALT_SHORT_PRESS, BUTTON): {COMMAND: COMMAND_TOGGLE, ENDPOINT_ID: 1, ARGS: []},
        (ALT_DOUBLE_PRESS, BUTTON): {COMMAND: COMMAND_OFF, ENDPOINT_ID: 1, ARGS: []},
    }

Most of the code adapted from remote_h1.py.

Device is supposed to have two click_modes:

            # click_mode:
            # 1 means single click mode, which is low latency (50ms) but only sends
            #   single click events.
            # 2 means multiple click mode, which has a slightly higher latency but
            #   supports single/double/triple click and long press.
            # default value after factory reset: 1.

According to 'herdsman-converters' this line will be required:

manufacturer_id_override = 0x115F

Please, comment the line to confirm if it's needed to work.

javicalle avatar Oct 06 '22 19:10 javicalle

hi @javicalle thanks for the reply. I have actually made my own hack based on the H1 version. It looks very similiar to yours, I think maybe even identical.

Problem is that it kinda works. It only supports single click events and instead of "single", the event reads "1_single". I can work with the latter by adjusting my automations but it still would be good to have click_mode=2 working.

Do you have any ideas on how to force it into this mode? Thanks.

xardas-eu avatar Oct 07 '22 07:10 xardas-eu

Yes I think that can be done this way:

From the device view --> manage cluster --> select the AqaraRemoteManuSpecificCluster cluster --> select the click_mode attribute

First get the current value, and then try to update the value to 2.

Related to: #1794

javicalle avatar Oct 07 '22 08:10 javicalle

Unfortunately this does not work. I don't have "AqaraRemoteManuSpecificCluster" :(

I only have "ManufacturerSpecificCluster" (twice, endpoints #1 and #2) but both have no attributes to select from. image

could this attribute be set at the quirk level with some command/service call? thanks!

bartlomiejmaciejowski avatar Oct 07 '22 09:10 bartlomiejmaciejowski

If you don't have the AqaraRemoteManuSpecificCluster that means your device hasn't caught the quirk. But also your device don't have 2 endpoints. At least not in my quirk.

The device should reflect your local quirk in the signature. Can you confirm it?

javicalle avatar Oct 07 '22 09:10 javicalle

Hi. There was a misconfiguration on my part. now the quirk is picked up correctly: Quirk: remote_e1.RemoteE1SingleRocker1

I was also now able to find the cluster and modify the attribute obraz

but the behavior seems the same. the zha_event logs still show the clicks as single (despite trying single, double, and long press)

event_type: zha_event
data:
  device_ieee: xx:xx:xx:xx:xx:xx:xx:xx
  unique_id: xx:xx:xx:xx:xx:xx:xx:xx:1:0x0012
  device_id: ffb9e4ade4b5b96eaad1a514120d3062
  endpoint_id: 1
  cluster_id: 18
  command: 1_single
  args:
    button: 1
    press_type: single
    attr_id: 85
    value: 1
  params: {}
origin: LOCAL
time_fired: "2022-10-07T10:48:41.099986+00:00"
context:
  id: 01GES0ZGWB380CWCM71BFQ60N2
  parent_id: null
  user_id: null

BTW the "Get zigbee attribute" action just finishes immediately without actually showing the attribute. I tried waking up the switch by pressing it repeatedly before calling it, but it does not seem to work either.

Am I doing something wrong?

xardas-eu avatar Oct 07 '22 10:10 xardas-eu

So, now your device shows only 1 endpoint, right? Can you check if your quirk have the manufacturer_id_override = 0x115F line as mine?

Enable the debug logs and attach the relevant info:

  • https://www.home-assistant.io/integrations/zha/#debug-logging

javicalle avatar Oct 07 '22 10:10 javicalle

Yep only one endpoint there and I have the override in place as well (I just copied your quirk). I'll go thru the logs later but just so I provide the most useful information - what will be the relevant part? you want the "set zigbee attribute" logged, right?

thanks!

xardas-eu avatar Oct 07 '22 11:10 xardas-eu

Well, any device related info can be usefull, specially the reports from device. When you remove the power from device and power on again, the device usually send it's status report. This kind of info is valuable.

javicalle avatar Oct 07 '22 11:10 javicalle

Gotcha. I'll post the logs here in the evening. What I suspect is the problem is that the attribute indices may actually be different from H1, e.g. the click mode is probably not on 0x0125 but some other index. I read somewhere something about 0xff22 so I think I'll try that as well. Thanks for all the help.

xardas-eu avatar Oct 07 '22 11:10 xardas-eu

I think most of the implementation matches the Z2M.

    {
        zigbeeModel: ['lumi.remote.acn003'],
        model: 'WXKG16LM',
        vendor: 'Xiaomi',
        description: 'Aqara wireless remote switch E1 (single rocker)',
        meta: {battery: {voltageToPercentage: '3V_2850_3000'}},
        fromZigbee: [fz.xiaomi_multistate_action, fz.aqara_opple],
        toZigbee: [tz.xiaomi_switch_click_mode],
        exposes: [e.battery(), e.battery_voltage(), e.action(['single', 'double', 'hold']),
            exposes.enum('click_mode', ea.ALL, ['fast', 'multi'])
                .withDescription('Click mode, fast: only supports single click which will be send immediately after clicking.' +
                    'multi: supports more events like double and hold')],
        configure: async (device, coordinatorEndpoint, logger) => {
            await device.getEndpoint(1).write('aqaraOpple', {0x0125: {value: 0x02, type: 0x20}}, {manufacturerCode: 0x115f});
        },
    },

where tz.xiaomi_switch_click_mode is:

    xiaomi_switch_click_mode: {
        key: ['click_mode'],
        convertSet: async (entity, key, value, meta) => {
            const lookupState = {'fast': 0x1, 'multi': 0x02};
            await entity.write('aqaraOpple', {0x0125: {value: lookupState[value], type: 0x20}}, manufacturerOptions.xiaomi);
            return {state: {click_mode: value}};
        },
        convertGet: async (entity, key, meta) => {
            await entity.read('aqaraOpple', [0x125], manufacturerOptions.xiaomi);
        },
    },

and manufacturerOptions.xiaomi are:

xiaomi: {manufacturerCode: herdsman.Zcl.ManufacturerCode.LUMI_UNITED_TECH, disableDefaultResponse: true},

javicalle avatar Oct 07 '22 12:10 javicalle

It seems that there is a special procedure to setting attribute values:

What I had to do when getting or setting attributes is first click the switch 5 times fast, then click either the set or get attribute button. This was the only way my switch would accept this. Don't know for sure if this helps, but it didn't work for me if I first clicked set or get, then woke up the switch by clicking 5 times fast. Also it only accepted the get and set for a really short time after I clicked the 5 times, so I would click 5 times fast immediately followed by clicking the get or set on my computer.

Refs:

  • https://github.com/zigpy/zha-device-handlers/issues/940#issuecomment-1172558963

javicalle avatar Oct 10 '22 20:10 javicalle

It seems that there is a special procedure to setting attribute values:

@javicalle YOU ARE THE BEST. I didn't have time to keep investigating this so had to postpone going for the logs for a while but just saw your reply and out of curiosity, tried it.

After clicking the switch 5 times, I was able to read (and then subsequently write) the click_mode attribute and now everything works.

Thanks so much! Bart

Edit and side note: the manufacturer code override was not needed for this to work

xardas-eu avatar Oct 11 '22 07:10 xardas-eu

the manufacturer code override was not needed for this to work

Thanks for checking it. 👍🏻

Have you need to change the operation_mode=1 to have double press or hold events?

javicalle avatar Oct 11 '22 07:10 javicalle

No, I just set click_mode to 2. Figured the operation_mode was only relevant to the switches with a built-in relay.

BTW now I want to dive a bit deeper and create my own quirk to support one of my devices (E1 Double Rocker No-neutral switch) but I cannot match the endpoints that the device reports with the endpoints from H1 (seems that there 8 clusters in endpoint 1 in my case but I was expecting 5 or 6). If you ever have time then please check https://github.com/zigpy/zha-device-handlers/issues/1810 - not necessarily looking for a ready made solution, but a pointer in the right direction :) If I'm able to get it right, I would like to start contributing to this repository.

xardas-eu avatar Oct 11 '22 07:10 xardas-eu