Repository: zigpy/zha-device-handlers

Issue:
[Device Support Request] Aqara Wall Switch - decoupled mode?
How to configure the button on QBKG12LM to decoupled mode?
I need to receive button events, but control the relay separately.



Discussion:
is it wired?
Yes, it is the wired wall switch with the neutral line.

Decoupled mode is stored on the device itself. Please read this for details
https://www.zigbee2mqtt.io/devices/QBKG12LM.html
https://github.com/Koenkk/zigbee-herdsman-converters/blob/6e316be797065bf31806e110a50470b4da52731b/converters/toZigbee.js#L1046-L1071

@dmulcahey looks like it is controlled by  `0xff22`, `0xff23` attrs of the `Basic` cluster.

@slydiman post Zigbee device signature/information from ZHA config panel
lumi.ctrl_ln2.aq1

Here is the info from zigbee2mqtt database (note the left button is decoupled and the left relay is always on):
{"id":8,"type":"Router","ieeeAddr":"0x00158d0003149e85","nwkAddr":22002,"manufId":4447,"manufName":"LUMI","powerSource":"Mains (single phase)","modelId":"lumi.ctrl_ln2.aq1","epList":[1,2,3,4,5,6,7],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[0,4,3,6,16,5,10,1,2],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65281":{"3":29,"5":28,"7":"0x0000000a7558257d","8":4385,"9":2816,"100":1,"101":0,"149":26.252864837646484,"152":1.5399999618530273,"154":0},"modelId":"lumi.ctrl_ln2.aq1","manufacturerName":"LUMI","powerSource":1,"zclVersion":1,"appVersion":1,"stackVersion":2,"hwVersion":18,"dateCode":"06-25-2018"}},
"genOnOff":{"attributes":{"61440":117440590,"onOff":1}}
},"binds":[]},"2":{"profId":260,"epId":2,"devId":81,"inClusterList":[6,16],"outClusterList":[],"clusters":{"genOnOff":{"attributes":{"61440":55964160,"onOff":0}}},"binds":[]},"3":{"profId":260,"epId":3,"devId":9,"inClusterList":[12],"outClusterList":[12,4],"clusters":{"genAnalogInput":{"attributes":{"presentValue":1.590000033378601}}},"binds":[]},"4":{"profId":260,"epId":4,"devId":83,"inClusterList":[12],"outClusterList":[12],"clusters":{},"binds":[]},"5":{"profId":260,"epId":5,"devId":0,"inClusterList":[16,18],"outClusterList":[],"clusters":{"genMultistateInput":{"attributes":{"presentValue":1}}},"binds":[]},"6":{"profId":260,"epId":6,"devId":0,"inClusterList":[18,16],"outClusterList":[],"clusters":{"genMultistateInput":{"attributes":{"presentValue":1}}},"binds":[]},"7":{"profId":260,"epId":7,"devId":0,"inClusterList":[18,16],"outClusterList":[],"clusters":{},"binds":[]}},"appVersion":33,"stackVersion":2,"hwVersion":18,"dateCode":"06-25-2018","zclVersion":1,"interviewCompleted":true,"meta":{},"lastSeen":1590922252052}

Currently I'm using zigbee2mqtt. 
I want to switch to ZHA, but the show stopper is 
https://github.com/zigpy/zha-device-handlers/issues/363
I have several such devices and they are actively used.

I'm trying to check everything and figure out all possible problem before switch to ZHA.
I have about 100 Xiaomi/Aqara devices.

BTW, is there a way to enable the similar option "power_outage_memory" on ZigBee sockets?
https://www.zigbee2mqtt.io/devices/ZNCZ02LM.html
I have done a Custom quirks in which I have added the following initialization to the basic cluster.

```python
	class BasicClusterAdd (BasicCluster):
		def __init__(self, *args, **kwargs):
			"""Init."""
			self.attributes = BasicCluster.attributes.copy()
			self.attributes.update({ 0xFF22: ("Xiaomi_BL", t.uint8_t)})
			self.attributes.update({ 0xFF23: ("Xiaomi_BR", t.uint8_t)})
			super().__init__(*args, **kwargs)
```
Once this is done, the value of the attribute can be changed with the UI.
![image](https://user-images.githubusercontent.com/52009552/83359936-cc2d9400-a37d-11ea-81ec-a87d13c28df2.png)

Value 254 decoupled
Value 18 coupled

Im`portant manufacturer code: 4447

> BTW, is there a way to enable the similar option "power_outage_memory" on ZigBee sockets?
> https://www.zigbee2mqtt.io/devices/ZNCZ02LM.html


Same as with decoupling mode: just figure out what attribute id, on which cluster what type and what values needs to be written to have that functionality on.


https://github.com/Koenkk/zigbee-herdsman-converters/blob/6e316be797065bf31806e110a50470b4da52731b/converters/toZigbee.js#L1027-L1045

The info from zigbee2mqtt database for ZNCZ02LM with enabled power_outage_memory:
{"id":10,"type":"Router","ieeeAddr":"0x00158d0001226901","nwkAddr":12981,"manufId":4447,"manufName":"LUMI","powerSource":"Mains (single phase)","modelId":"lumi.plug","epList":[1,2,3],"endpoints":{"1":{"profId":260,"epId":1,"devId":81,"inClusterList":[0,4,3,6,16,5,10,1,2],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"65281":{"3":39,"5":40,"7":"0x0000000000000000","8":4894,"9":2816,"100":1,"149":57.70904541015625,"152":20.770000457763672,"154":0},"65520":{"type":"Buffer","data":[170,128,5,209,135,8,1,16,1]},"modelId":"lumi.plug","manufacturerName":"LUMI","powerSource":1,"zclVersion":1,"appVersion":30,"stackVersion":2,"hwVersion":18,"dateCode":"09-29-2017"}},"genOnOff":{"attributes":{"61440":117440516,"onOff":1}}},"binds":[]},"2":{"profId":260,"epId":2,"devId":9,"inClusterList":[12],"outClusterList":[12,4],"clusters":{"genAnalogInput":{"attributes":{"presentValue":127.5}}},"binds":[]},"3":{"profId":260,"epId":3,"devId":83,"inClusterList":[12],"outClusterList":[12],"clusters":{},"binds":[]}},"appVersion":30,"stackVersion":2,"hwVersion":18,"dateCode":"09-29-2017","zclVersion":1,"interviewCompleted":true,"meta":{},"lastSeen":1590922472351}
so for ZNCZ02LM looks like it is attr_id `0xFFF0` of type `t.LVBytes` (need to confirm) of the basic cluster and you apparently have to do two writes of bytes string? am I reading Z2M code correctly?

No idea how many bytes need to be written. 
I posted MQTT payload {"power_outage_memory": true} and then pressed the button on the socket to apply new settings.
Is it so problematic - to add two (three?) attributes to show em in the GUI?
Can't get it to work like this: 
<img width="416" alt="Screenshot" src="https://user-images.githubusercontent.com/9576189/99459782-b6b62180-293f-11eb-8968-c39373748896.png">
So can't wait this to be configurable via UI

Is it any progress with decouple mode for QBKG12LM? It doesn't work in  zha.set_zigbee_cluster_attribute (return error).
Hi there,
 I'm trying to do the same for QBKG04LM.
According to blakadder, this device supports docoupled mode:
* https://zigbee.blakadder.com/Xiaomi_QBKG04LM.html

I want to try some of the things that have been discussed around here but have not made any progress.

Things I have tried:
* implement quirks mode (as @albalaing has done).
In this case I have not even known where to start (how modify and test locally)
* via the `zha.set_zigbee_cluster_attribute` service
In this case, I have an error trying to modify the attribute:
```
2021-01-07 20:09:44 ERROR (MainThread) [zigpy.zcl] [0x133b:1:0x0000] 65314 is not a valid attribute id
```


I attach the data in case it may be useful.

The device signature:
```
{
  "node_descriptor": "NodeDescriptor(byte1=1, byte2=64, mac_capability_flags=132, manufacturer_code=4151, maximum_buffer_size=127, maximum_incoming_transfer_size=100, server_mask=1, maximum_outgoing_transfer_size=100, descriptor_capability_field=0)",
  "endpoints": {
    "1": {
      "profile_id": 260,
      "device_type": "0x0006",
      "in_clusters": [
        "0x0000",
        "0x0001",
        "0x0002",
        "0x0003",
        "0x000a",
        "0x0019"
      ],
      "out_clusters": [
        "0x0000",
        "0x000a",
        "0x0019"
      ]
    },
    "2": {
      "profile_id": 260,
      "device_type": "0x0000",
      "in_clusters": [
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0010"
      ],
      "out_clusters": []
    },
    "3": {
      "profile_id": 260,
      "device_type": "0x0000",
      "in_clusters": [
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0010"
      ],
      "out_clusters": []
    }
  },
  "manufacturer": "LUMI",
  "model": "lumi.ctrl_neutral1",
  "class": "zhaquirks.xiaomi.aqara.ctrl_neutral.CtrlNeutral"
}
```

For this device I have found some [references](https://github.com/guyeeba/Hubitat/blob/master/Drivers/Aqara%20QBKG03LM-QBKG04LM.groovy) (also [here](https://github.com/dresden-elektronik/deconz-rest-plugin/issues/1816)):
```
	// QBKG04LM: one button, no neutral required
	// Disconnect button from relay: write uint8 (0x20) value (connected: 0x12, disconnected: 0xFE) to attribute 0xFF22 of endpoint 0x01, cluster 0x0000
```
(There's also for [QBKG12LM](https://github.com/guyeeba/Hubitat/blob/master/Drivers/Aqara%20QBKG11LM-QBKG12LM-LLKZMK11LM.groovy))


If I have understood correctly, in my case, I should be able to invoke the service with the values:
```
ieee: '00: 11: 22: 33: 44: 55: 66: 77 '(redacted)
endpoint_id: 1
cluster_id: 0
cluster_type: out
attribute: 0xFF22
value: 0xFE
manufacturer: 4151
```

I have tried with diferents manufacturer codes (4151, 4447 ), always the same error.

Any advice?
Is it realisable the same way for `ctrl_ln`?
Yes, I think it should be possible.

According to _blakadder_ the ctrl_ln devices support decoupled mode:
* https://zigbee.blakadder.com/Xiaomi_QBKG11LM.html
* https://zigbee.blakadder.com/Xiaomi_QBKG12LM.html

It should be possible to implement:
* the decoupled attribute [ctrl_neutral.py#L77.L80](https://github.com/javicalle/zha-device-handlers/blob/1158c0ef72286873d9911ccada7395dc0daeb082/zhaquirks/xiaomi/aqara/ctrl_neutral.py#L77.L80)
* the quirks for the button (endpoints: 5, 6 and 7)
* the `device_automation_triggers` [ctrl_neutral.py#L229](https://github.com/javicalle/zha-device-handlers/blob/1158c0ef72286873d9911ccada7395dc0daeb082/zhaquirks/xiaomi/aqara/ctrl_neutral.py#L229)
@javicalle right now I use `lumi.ctrl_ln2.aq1` via `z2m`, with decoupled right button, so I definitely sure it is supported
I want to now if it is possible to implement decoupled mode the same way as in `ctrl_neutral`
just started using `z2m` because of buying unsupported device (and because of dreams about decoupled mode on Aqara switches) and realised that it's much slower on my setup
wanna go back, waiting for `TS0601_dimmer` quirk and aqara decouple mode :)
So it can certainly be implemented.
I think it is possible to take advantage of (and improve) the implementation of `ctrl_neutral`. I don't have any `ctrl_ln` to do the implementation, but I can help and test the refactoring.
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.

Resolving patch:
diff --git a/zhaquirks/xiaomi/aqara/ctrl_ln.py b/zhaquirks/xiaomi/aqara/ctrl_ln.py
--- a/zhaquirks/xiaomi/aqara/ctrl_ln.py
+++ b/zhaquirks/xiaomi/aqara/ctrl_ln.py
@@ -1,4 +1,6 @@
 """Xiaomi aqara single key wall switch devices."""
+
+from zigpy import types as t
 from zigpy.profiles import zha
 from zigpy.zcl.clusters.general import (
     AnalogInput,
@@ -14,15 +16,28 @@
     Time,
 )
 
-from zhaquirks import Bus
+from zhaquirks import Bus, EventableCluster
 from zhaquirks.const import (
+    ARGS,
+    ATTRIBUTE_ID,
+    ATTRIBUTE_NAME,
+    BUTTON_1,
+    BUTTON_2,
+    BUTTON_3,
+    CLUSTER_ID,
+    COMMAND,
+    COMMAND_ATTRIBUTE_UPDATED,
     DEVICE_TYPE,
+    DOUBLE_PRESS,
+    ENDPOINT_ID,
     ENDPOINTS,
     INPUT_CLUSTERS,
     MODELS_INFO,
     OUTPUT_CLUSTERS,
     PROFILE_ID,
+    SHORT_PRESS,
     SKIP_CONFIGURATION,
+    VALUE,
 )
 from zhaquirks.xiaomi import (
     LUMI,
@@ -33,10 +48,26 @@
     XiaomiPowerConfiguration,
 )
 
+ATTRIBUTE_PRESENT_VALUE = "present_value"
+
 
 class CtrlLn(XiaomiCustomDevice):
     """Aqara double key switch device."""
 
+    class BasicClusterDecoupled(BasicCluster):
+        """Adds attributes for decoupled mode."""
+
+        # Known Options for 'decoupled_mode_<button>':
+        # * 254 (decoupled)
+        # * 18 (relay controlled)
+        manufacturer_attributes = {
+            0xFF22: ("decoupled_mode_left", t.uint8_t),
+            0xFF23: ("decoupled_mode_right", t.uint8_t),
+        }
+
+    class WallSwitchMultistateInputCluster(EventableCluster, MultistateInput):
+        """WallSwitchMultistateInputCluster: fire events corresponding to press type."""
+
     def __init__(self, *args, **kwargs):
         """Init."""
         self.power_bus = Bus()
@@ -107,7 +138,7 @@ def __init__(self, *args, **kwargs):
                 PROFILE_ID: zha.PROFILE_ID,
                 DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                 INPUT_CLUSTERS: [
-                    BasicCluster,
+                    BasicClusterDecoupled,
                     XiaomiPowerConfiguration,
                     DeviceTemperature.cluster_id,
                     Identify.cluster_id,
@@ -131,5 +162,68 @@ def __init__(self, *args, **kwargs):
                 INPUT_CLUSTERS: [AnalogInputCluster],
                 OUTPUT_CLUSTERS: [Groups.cluster_id, AnalogInput.cluster_id],
             },
+            5: {
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
+                INPUT_CLUSTERS: [
+                    WallSwitchMultistateInputCluster,
+                    BinaryOutput.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+            6: {
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
+                INPUT_CLUSTERS: [
+                    WallSwitchMultistateInputCluster,
+                    BinaryOutput.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+            7: {
+                DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH,
+                INPUT_CLUSTERS: [
+                    WallSwitchMultistateInputCluster,
+                    BinaryOutput.cluster_id,
+                ],
+                OUTPUT_CLUSTERS: [],
+            },
+        },
+    }
+
+    device_automation_triggers = {
+        (SHORT_PRESS, BUTTON_1): {
+            ENDPOINT_ID: 5,
+            CLUSTER_ID: 18,
+            COMMAND: COMMAND_ATTRIBUTE_UPDATED,
+            ARGS: {ATTRIBUTE_ID: 85, ATTRIBUTE_NAME: ATTRIBUTE_PRESENT_VALUE, VALUE: 1},
+        },
+        (DOUBLE_PRESS, BUTTON_1): {
+            ENDPOINT_ID: 5,
+            CLUSTER_ID: 18,
+            COMMAND: COMMAND_ATTRIBUTE_UPDATED,
+            ARGS: {ATTRIBUTE_ID: 85, ATTRIBUTE_NAME: ATTRIBUTE_PRESENT_VALUE, VALUE: 2},
+        },
+        (SHORT_PRESS, BUTTON_2): {
+            ENDPOINT_ID: 6,
+            CLUSTER_ID: 18,
+            COMMAND: COMMAND_ATTRIBUTE_UPDATED,
+            ARGS: {ATTRIBUTE_ID: 85, ATTRIBUTE_NAME: ATTRIBUTE_PRESENT_VALUE, VALUE: 1},
+        },
+        (DOUBLE_PRESS, BUTTON_2): {
+            ENDPOINT_ID: 6,
+            CLUSTER_ID: 18,
+            COMMAND: COMMAND_ATTRIBUTE_UPDATED,
+            ARGS: {ATTRIBUTE_ID: 85, ATTRIBUTE_NAME: ATTRIBUTE_PRESENT_VALUE, VALUE: 2},
+        },
+        (SHORT_PRESS, BUTTON_3): {
+            ENDPOINT_ID: 7,
+            CLUSTER_ID: 18,
+            COMMAND: COMMAND_ATTRIBUTE_UPDATED,
+            ARGS: {ATTRIBUTE_ID: 85, ATTRIBUTE_NAME: ATTRIBUTE_PRESENT_VALUE, VALUE: 1},
+        },
+        (DOUBLE_PRESS, BUTTON_3): {
+            ENDPOINT_ID: 7,
+            CLUSTER_ID: 18,
+            COMMAND: COMMAND_ATTRIBUTE_UPDATED,
+            ARGS: {ATTRIBUTE_ID: 85, ATTRIBUTE_NAME: ATTRIBUTE_PRESENT_VALUE, VALUE: 2},
         },
     }


Test patch: