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
750 stars 686 forks source link

[Device Support Request] Aqara Curtain Motor B1 (ZNCLDJ11LM) #2451

Open hastarin opened 1 year ago

hastarin commented 1 year ago

Problem description

I'm using ZHA to try controlling this curtain motor and it seems to have a few issues.

  1. The Cover is reporting the inverse state of "open"/"closed".
  2. The Number control shows NaN and can't be dragged to a value.
  3. It shows Occupancy and Opening sensors but neither seem to function. To my knowledge there is no Occupancy sensor and I'm unsure what an Opening sensor is meant to do but it never changed state in my testing.

Solution description

Either correcting the open/closed state reporting and/or add a configuration option to invert it.

Hide the Occupancy/Opening sensors by default.

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device signature

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=0, maximum_outgoing_transfer_size=100, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=False, *is_full_function_device=True, *is_mains_powered=True, *is_receiver_on_when_idle=True, *is_router=True, *is_security_capable=False)", "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0202", "input_clusters": [ "0x0000", "0x0001", "0x0003", "0x0004", "0x0005", "0x0006", "0x000a", "0x000d", "0x0013", "0x0102", "0x0406" ], "output_clusters": [ "0x0001", "0x0006", "0x000a", "0x000d", "0x0013", "0x0019", "0x0102", "0x0406" ] } }, "manufacturer": "LUMI", "model": "lumi.curtain", "class": "zigpy.device.Device" } ```

Diagnostic information

Diagnostic information I get an error trying to download this.

Logs

Logs ``` 2023-07-02 17:25:08.676 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e5c000700aa00 2023-07-02 17:25:08.676 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:08.677 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:08.681 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:08.682 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x175c000800010001 2023-07-02 17:25:08.689 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x175c0028002100220200000102f7e90204010c000a0018390a550039295cd14100afff97b60e00bd 2023-07-02 17:25:08.690 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[33, , , 1, , 2, 260, 12, b'\x189\nU\x009)\\\xd1A', 0, 175, 255, 151, 182, 14, 0, -67] 2023-07-02 17:25:08.690 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [33, , , 1, , 2, 260, 12, b'\x189\nU\x009)\\\xd1A', 0, 175, 255, 151, 182, 14, 0, -67] 2023-07-02 17:25:08.690 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0xE9F7), src_ep=2, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=12, data=Serialized[b'\x189\nU\x009)\\\xd1A'], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-67) 2023-07-02 17:25:08.691 DEBUG (MainThread) [zigpy.zcl] [0xE9F7:2:0x000c] Received ZCL frame: b'\x189\nU\x009)\\\xd1A' 2023-07-02 17:25:08.692 DEBUG (MainThread) [zigpy.zcl] [0xE9F7:2:0x000c] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=0, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=57, command_id=10, *direction=) 2023-07-02 17:25:08.693 DEBUG (MainThread) [zigpy.zcl] [0xE9F7:2:0x000c] Decoded ZCL frame: AnalogInputCluster:Report_Attributes(attribute_reports=[Attribute(attrid=0x0055, value=TypeValue(type=Single, value=26.170000076293945))]) 2023-07-02 17:25:08.693 DEBUG (MainThread) [zigpy.zcl] [0xE9F7:2:0x000c] Received command 0x0A (TSN 57): Report_Attributes(attribute_reports=[Attribute(attrid=0x0055, value=TypeValue(type=Single, value=26.170000076293945))]) 2023-07-02 17:25:08.694 DEBUG (MainThread) [zigpy.zcl] [0xE9F7:2:0x000c] Attribute report received: present_value=26.170000076293945 2023-07-02 17:25:08.703 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 2, profile: 0x0104, cluster_id: 0x000c, data: b'18390a550039295cd141' 2023-07-02 17:25:09.186 DEBUG (MainThread) [zigpy_deconz.api] Command Command.write_parameter (5, , b'X\x02\x00\x00') 2023-07-02 17:25:09.186 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x0b5d000c0005002658020000 2023-07-02 17:25:09.193 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0b5d000800010026 2023-07-02 17:25:09.193 DEBUG (MainThread) [zigpy_deconz.api] Received command write_parameter[1, 38] 2023-07-02 17:25:09.193 DEBUG (MainThread) [zigpy_deconz.api] Write parameter watchdog_ttl: SUCCESS 2023-07-02 17:25:09.506 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e5e000700aa00 2023-07-02 17:25:09.507 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:09.507 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:09.511 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:09.511 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x175e000800010001 2023-07-02 17:25:09.514 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x175e0023001c00220200000102461b0104010a000500104900000000afff97b60e00c2 2023-07-02 17:25:09.515 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[28, , , 1, , 1, 260, 10, b'\x10I\x00\x00\x00', 0, 175, 255, 151, 182, 14, 0, -62] 2023-07-02 17:25:09.516 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [28, , , 1, , 1, 260, 10, b'\x10I\x00\x00\x00', 0, 175, 255, 151, 182, 14, 0, -62] 2023-07-02 17:25:09.517 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x1B46), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=10, data=Serialized[b'\x10I\x00\x00\x00'], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-62) 2023-07-02 17:25:09.517 DEBUG (MainThread) [zigpy.zcl] [0x1B46:1:0x000a] Received ZCL frame: b'\x10I\x00\x00\x00' 2023-07-02 17:25:09.518 DEBUG (MainThread) [zigpy.zcl] [0x1B46:1:0x000a] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=0, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=73, command_id=0, *direction=) 2023-07-02 17:25:09.519 DEBUG (MainThread) [zigpy.zcl] [0x1B46:1:0x000a] Decoded ZCL frame: Time:Read_Attributes(attribute_ids=[0]) 2023-07-02 17:25:09.519 DEBUG (MainThread) [zigpy.zcl] [0x1B46:1:0x000a] Received command 0x00 (TSN 73): Read_Attributes(attribute_ids=[0]) 2023-07-02 17:25:09.522 DEBUG (MainThread) [zigpy.zcl] [0x1B46:1:0x000a] Sending reply header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=False, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=73, command_id=, *direction=) 2023-07-02 17:25:09.522 DEBUG (MainThread) [zigpy.zcl] [0x1B46:1:0x000a] Sending reply: Read_Attributes_rsp(status_records=[ReadAttributeRecord(attrid=0x0000, status=, value=TypeValue(type=UTCTime, value=741597909))]) 2023-07-02 17:25:09.523 DEBUG (MainThread) [zigpy_deconz.zigbee.application] Sending packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x0000), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x1B46), dst_ep=1, source_route=None, extended_timeout=False, tsn=73, profile_id=260, cluster_id=10, data=Serialized[b'\x18I\x01\x00\x00\x00\xe2\xd5\xe23,'], tx_options=, radius=0, non_member_radius=0, lqi=None, rssi=None) 2023-07-02 17:25:09.524 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 1, profile: 0x0104, cluster_id: 0x000a, data: b'1049000000' 2023-07-02 17:25:09.524 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_request (26, 242, , , 260, 10, 1, b'\x18I\x01\x00\x00\x00\xe2\xd5\xe23,', , 0) 2023-07-02 17:25:09.525 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x125f0021001a00f20002461b0104010a00010b00184901000000e2d5e2332c0600 2023-07-02 17:25:09.537 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x125f000900020022f2 2023-07-02 17:25:09.537 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_request[2, , 242] 2023-07-02 17:25:09.537 DEBUG (MainThread) [zigpy_deconz.api] APS data request response: [2, , 242] 2023-07-02 17:25:09.539 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xE9F7](lumi.plug): Device seen - marking the device available and resetting counter 2023-07-02 17:25:09.539 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xE9F7](lumi.plug): Update device availability - device available: True - new availability: True - changed: False 2023-07-02 17:25:09.548 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e60000700a600 2023-07-02 17:25:09.548 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:09.548 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:09.549 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_confirm (0,) 2023-07-02 17:25:09.549 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x04600007000000 2023-07-02 17:25:09.553 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x04600013000c0022f202461b01010000000000 2023-07-02 17:25:09.553 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_confirm[12, , 242, , 1, , 0, 0, 0, 0] 2023-07-02 17:25:09.554 DEBUG (MainThread) [zigpy_deconz.api] APS data confirm response for request with id 242: 00 2023-07-02 17:25:09.554 DEBUG (MainThread) [zigpy_deconz.api] Request id: 0xf2 'aps_data_confirm' for , status: 0x00 2023-07-02 17:25:11.952 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e61000700aa00 2023-07-02 17:25:11.953 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:11.953 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:11.954 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:11.955 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x1761000800010001 2023-07-02 17:25:11.958 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x17610052004b002202000001023867010401000034001c5f110d0a01ff422b64100003281b9839000000009539ee74084405211e009a200008212013072700000000000000000921010700afff97b60e00c9 2023-07-02 17:25:11.959 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[75, , , 1, , 1, 260, 0, b"\x1c_\x11\r\n\x01\xffB+d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07", 0, 175, 255, 151, 182, 14, 0, -55] 2023-07-02 17:25:11.959 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [75, , , 1, , 1, 260, 0, b"\x1c_\x11\r\n\x01\xffB+d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07", 0, 175, 255, 151, 182, 14, 0, -55] 2023-07-02 17:25:11.960 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x6738), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=0, data=Serialized[b"\x1c_\x11\r\n\x01\xffB+d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07"], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-55) 2023-07-02 17:25:11.962 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x0000] Received ZCL frame: b"\x1c_\x11\r\n\x01\xffA+d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07" 2023-07-02 17:25:11.963 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x0000] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=True, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), manufacturer=4447, tsn=13, command_id=10, *direction=) 2023-07-02 17:25:11.964 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x0000] Decoded ZCL frame: BasicCluster:Report_Attributes(attribute_reports=[Attribute(attrid=0xFF01, value=TypeValue(type=LVBytes, value=b"d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07"))]) 2023-07-02 17:25:11.965 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x0000] Received command 0x0A (TSN 13): Report_Attributes(attribute_reports=[Attribute(attrid=0xFF01, value=TypeValue(type=LVBytes, value=b"d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07"))]) 2023-07-02 17:25:11.966 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x0000] Attribute report received: 0xFF01=b"d\x10\x00\x03(\x1b\x989\x00\x00\x00\x00\x959\xeet\x08D\x05!\x1e\x00\x9a \x00\x08! \x13\x07'\x00\x00\x00\x00\x00\x00\x00\x00\t!\x01\x07" 2023-07-02 17:25:11.966 DEBUG (MainThread) [zhaquirks.xiaomi] 00:15:8d:00:01:71:a9:a7 - Attribute report. attribute_id: [65281] value: [{'0xff01-100': , 'temperature': 27, 'power': 0.0, 'consumption': 545.8270263671875, 'X-attrib-5': 30, '0xff01-154': 0, '0xff01-8': 4896, '0xff01-7': 0, '0xff01-9': 1793}] 2023-07-02 17:25:11.969 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 1, profile: 0x0104, cluster_id: 0x0000, data: b'1c5f110d0a01ff422b64100003281b9839000000009539ee74084405211e009a2000082120130727000000000000000009210107' 2023-07-02 17:25:13.837 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xE0FD](lumi.plug): Device seen - marking the device available and resetting counter 2023-07-02 17:25:13.837 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xE0FD](lumi.plug): Update device availability - device available: True - new availability: True - changed: False 2023-07-02 17:25:14.320 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0102] Sending request header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=False, direction=, disable_default_response=0, reserved=0, *is_cluster=True, *is_general=False), tsn=243, command_id=0, *direction=) 2023-07-02 17:25:14.321 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0102] Sending request: up_open() 2023-07-02 17:25:14.321 DEBUG (MainThread) [zigpy_deconz.zigbee.application] Sending packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x0000), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x1027), dst_ep=1, source_route=None, extended_timeout=False, tsn=243, profile_id=260, cluster_id=258, data=Serialized[b'\x01\xf3\x00'], tx_options=, radius=0, non_member_radius=0, lqi=None, rssi=None) 2023-07-02 17:25:14.322 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_request (18, 244, , , 260, 258, 1, b'\x01\xf3\x00', , 0) 2023-07-02 17:25:14.322 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x12620019001200f400022710010401020101030001f3000200 2023-07-02 17:25:14.334 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x1262000900020022f4 2023-07-02 17:25:14.335 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_request[2, , 244] 2023-07-02 17:25:14.335 DEBUG (MainThread) [zigpy_deconz.api] APS data request response: [2, , 244] 2023-07-02 17:25:14.335 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e63000700a600 2023-07-02 17:25:14.335 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:14.335 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:14.336 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_confirm (0,) 2023-07-02 17:25:14.336 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x04630007000000 2023-07-02 17:25:14.339 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x04630013000c0022f402271001010000000000 2023-07-02 17:25:14.339 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_confirm[12, , 244, , 1, , 0, 0, 0, 0] 2023-07-02 17:25:14.339 DEBUG (MainThread) [zigpy_deconz.api] APS data confirm response for request with id 244: 00 2023-07-02 17:25:14.340 DEBUG (MainThread) [zigpy_deconz.api] Request id: 0xf4 'aps_data_confirm' for , status: 0x00 2023-07-02 17:25:14.357 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e64000700aa00 2023-07-02 17:25:14.358 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:14.358 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:14.359 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:14.359 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x1764000800010001 2023-07-02 17:25:14.362 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x17640023001c0022020000010227100104010201050018f30b000000afff97b60e00d1 2023-07-02 17:25:14.362 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[28, , , 1, , 1, 260, 258, b'\x18\xf3\x0b\x00\x00', 0, 175, 255, 151, 182, 14, 0, -47] 2023-07-02 17:25:14.362 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [28, , , 1, , 1, 260, 258, b'\x18\xf3\x0b\x00\x00', 0, 175, 255, 151, 182, 14, 0, -47] 2023-07-02 17:25:14.363 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x1027), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=258, data=Serialized[b'\x18\xf3\x0b\x00\x00'], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-47) 2023-07-02 17:25:14.363 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0102] Received ZCL frame: b'\x18\xf3\x0b\x00\x00' 2023-07-02 17:25:14.364 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0102] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=0, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=243, command_id=11, *direction=) 2023-07-02 17:25:14.365 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0102] Decoded ZCL frame: WindowCovering:Default_Response(command_id=0, status=) 2023-07-02 17:25:14.365 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x1027:1:0x0102]: executed 'up_open' command with args: '()' kwargs: '{}' result: Default_Response(command_id=0, status=) 2023-07-02 17:25:14.366 DEBUG (MainThread) [homeassistant.components.zha.cover] state=opening 2023-07-02 17:25:14.367 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 1, profile: 0x0104, cluster_id: 0x0102, data: b'18f30b0000' 2023-07-02 17:25:14.608 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0x859F](lumi.sensor_motion): Device seen - marking the device available and resetting counter 2023-07-02 17:25:14.608 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0x859F](lumi.sensor_motion): Update device availability - device available: True - new availability: True - changed: False 2023-07-02 17:25:14.666 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e65000700aa00 2023-07-02 17:25:14.667 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:14.667 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:14.667 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:14.668 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x1765000800010001 2023-07-02 17:25:14.670 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x176500270020002202000001022710010401000009001c5f110c0a0404200100afff97b60e00d1 2023-07-02 17:25:14.670 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[32, , , 1, , 1, 260, 0, b'\x1c_\x11\x0c\n\x04\x04 \x01', 0, 175, 255, 151, 182, 14, 0, -47] 2023-07-02 17:25:14.670 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [32, , , 1, , 1, 260, 0, b'\x1c_\x11\x0c\n\x04\x04 \x01', 0, 175, 255, 151, 182, 14, 0, -47] 2023-07-02 17:25:14.671 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x1027), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=0, data=Serialized[b'\x1c_\x11\x0c\n\x04\x04 \x01'], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-47) 2023-07-02 17:25:14.671 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0000] Received ZCL frame: b'\x1c_\x11\x0c\n\x04\x04 \x01' 2023-07-02 17:25:14.672 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0000] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=True, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), manufacturer=4447, tsn=12, command_id=10, *direction=) 2023-07-02 17:25:14.673 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0000] Decoded ZCL frame: Basic:Report_Attributes(attribute_reports=[Attribute(attrid=0x0404, value=TypeValue(type=uint8_t, value=1))]) 2023-07-02 17:25:14.673 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0000] Received command 0x0A (TSN 12): Report_Attributes(attribute_reports=[Attribute(attrid=0x0404, value=TypeValue(type=uint8_t, value=1))]) 2023-07-02 17:25:14.674 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x0000] Attribute report received: 0x0404=1 2023-07-02 17:25:14.675 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 1, profile: 0x0104, cluster_id: 0x0000, data: b'1c5f110c0a04042001' 2023-07-02 17:25:14.680 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e66000700aa00 2023-07-02 17:25:14.681 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:14.681 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:14.681 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:14.681 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x1766000800010001 2023-07-02 17:25:14.685 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x1766002f00280022020000010227100104010d001100180d0a5500390000000000f0230027100300afff97b60e00d1 2023-07-02 17:25:14.686 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[40, , , 1, , 1, 260, 13, b"\x18\r\nU\x009\x00\x00\x00\x00\x00\xf0#\x00'\x10\x03", 0, 175, 255, 151, 182, 14, 0, -47] 2023-07-02 17:25:14.686 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [40, , , 1, , 1, 260, 13, b"\x18\r\nU\x009\x00\x00\x00\x00\x00\xf0#\x00'\x10\x03", 0, 175, 255, 151, 182, 14, 0, -47] 2023-07-02 17:25:14.686 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x1027), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=13, data=Serialized[b"\x18\r\nU\x009\x00\x00\x00\x00\x00\xf0#\x00'\x10\x03"], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-47) 2023-07-02 17:25:14.687 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x000d] Received ZCL frame: b"\x18\r\nU\x009\x00\x00\x00\x00\x00\xf0#\x00'\x10\x03" 2023-07-02 17:25:14.687 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x000d] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=0, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=13, command_id=10, *direction=) 2023-07-02 17:25:14.688 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x000d] Decoded ZCL frame: AnalogOutput:Report_Attributes(attribute_reports=[Attribute(attrid=0x0055, value=TypeValue(type=Single, value=0.0)), Attribute(attrid=0xF000, value=TypeValue(type=uint32_t, value=51390208))]) 2023-07-02 17:25:14.689 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x000d] Received command 0x0A (TSN 13): Report_Attributes(attribute_reports=[Attribute(attrid=0x0055, value=TypeValue(type=Single, value=0.0)), Attribute(attrid=0xF000, value=TypeValue(type=uint32_t, value=51390208))]) 2023-07-02 17:25:14.689 DEBUG (MainThread) [zigpy.zcl] [0x1027:1:0x000d] Attribute report received: present_value=0.0, 0xF000=51390208 2023-07-02 17:25:14.690 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 1, profile: 0x0104, cluster_id: 0x000d, data: b'180d0a5500390000000000f02300271003' 2023-07-02 17:25:14.779 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xEA0E](lumi.remote.b1acn01): Device seen - marking the device available and resetting counter 2023-07-02 17:25:14.779 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xEA0E](lumi.remote.b1acn01): Update device availability - device available: True - new availability: True - changed: False 2023-07-02 17:25:16.107 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x78ED:1:0x0b04]: async_update 2023-07-02 17:25:16.107 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x78ED:1:0x0b04]: Reading attributes in chunks: ['active_power', 'rms_voltage'] 2023-07-02 17:25:16.113 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x6738:1:0x0b04]: async_update 2023-07-02 17:25:16.115 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x6738:1:0x0b04]: Reading attributes in chunks: ['active_power', 'rms_voltage'] 2023-07-02 17:25:16.119 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0xE9F7:1:0x0b04]: async_update 2023-07-02 17:25:16.119 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0xE9F7:1:0x0b04]: Reading attributes in chunks: ['active_power', 'rms_voltage'] 2023-07-02 17:25:16.121 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x1B46:1:0x0b04]: async_update 2023-07-02 17:25:16.121 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0x1B46:1:0x0b04]: Reading attributes in chunks: ['active_power', 'rms_voltage'] 2023-07-02 17:25:16.122 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0xE0FD:1:0x0b04]: async_update 2023-07-02 17:25:16.123 DEBUG (MainThread) [homeassistant.components.zha.core.cluster_handlers] [0xE0FD:1:0x0b04]: Reading attributes in chunks: ['active_power', 'rms_voltage'] 2023-07-02 17:25:16.927 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0x60CE](lumi.sen_ill.mgl01): Device seen - marking the device available and resetting counter 2023-07-02 17:25:16.927 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0x60CE](lumi.sen_ill.mgl01): Update device availability - device available: True - new availability: True - changed: False 2023-07-02 17:25:17.043 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e67000700aa00 2023-07-02 17:25:17.043 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:17.044 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:17.045 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:17.046 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x1767000800010001 2023-07-02 17:25:17.051 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x17670023001c0022020000010238670104010a000500101400000000afff97b60e00c9 2023-07-02 17:25:17.051 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[28, , , 1, , 1, 260, 10, b'\x10\x14\x00\x00\x00', 0, 175, 255, 151, 182, 14, 0, -55] 2023-07-02 17:25:17.052 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [28, , , 1, , 1, 260, 10, b'\x10\x14\x00\x00\x00', 0, 175, 255, 151, 182, 14, 0, -55] 2023-07-02 17:25:17.053 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x6738), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=10, data=Serialized[b'\x10\x14\x00\x00\x00'], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-55) 2023-07-02 17:25:17.053 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x000a] Received ZCL frame: b'\x10\x14\x00\x00\x00' 2023-07-02 17:25:17.055 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x000a] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=0, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=20, command_id=0, *direction=) 2023-07-02 17:25:17.055 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x000a] Decoded ZCL frame: Time:Read_Attributes(attribute_ids=[0]) 2023-07-02 17:25:17.056 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x000a] Received command 0x00 (TSN 20): Read_Attributes(attribute_ids=[0]) 2023-07-02 17:25:17.058 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x000a] Sending reply header: ZCLHeader(frame_control=FrameControl(frame_type=, is_manufacturer_specific=False, direction=, disable_default_response=1, reserved=0, *is_cluster=False, *is_general=True), tsn=20, command_id=, *direction=) 2023-07-02 17:25:17.059 DEBUG (MainThread) [zigpy.zcl] [0x6738:1:0x000a] Sending reply: Read_Attributes_rsp(status_records=[ReadAttributeRecord(attrid=0x0000, status=, value=TypeValue(type=UTCTime, value=741597917))]) 2023-07-02 17:25:17.059 DEBUG (MainThread) [zigpy_deconz.zigbee.application] Sending packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0x0000), src_ep=1, dst=AddrModeAddress(addr_mode=, address=0x6738), dst_ep=1, source_route=None, extended_timeout=False, tsn=20, profile_id=260, cluster_id=10, data=Serialized[b'\x18\x14\x01\x00\x00\x00\xe2\xdd\xe23,'], tx_options=, radius=0, non_member_radius=0, lqi=None, rssi=None) 2023-07-02 17:25:17.060 DEBUG (MainThread) [zigpy_deconz.api] 'aps_data_indication' response from , ep: 1, profile: 0x0104, cluster_id: 0x000a, data: b'1014000000' 2023-07-02 17:25:17.063 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_request (26, 245, , , 260, 10, 1, b'\x18\x14\x01\x00\x00\x00\xe2\xdd\xe23,', , 0) 2023-07-02 17:25:17.063 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x12680021001a00f5000238670104010a00010b00181401000000e2dde2332c0600 2023-07-02 17:25:17.075 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x1268000900020022f5 2023-07-02 17:25:17.076 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_request[2, , 245] 2023-07-02 17:25:17.076 DEBUG (MainThread) [zigpy_deconz.api] APS data request response: [2, , 245] 2023-07-02 17:25:17.080 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e69000700a600 2023-07-02 17:25:17.081 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:17.081 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:17.082 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_confirm (0,) 2023-07-02 17:25:17.082 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x04690007000000 2023-07-02 17:25:17.086 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x04690013000c0022f502386701010000000000 2023-07-02 17:25:17.087 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_confirm[12, , 245, , 1, , 0, 0, 0, 0] 2023-07-02 17:25:17.087 DEBUG (MainThread) [zigpy_deconz.api] APS data confirm response for request with id 245: 00 2023-07-02 17:25:17.088 DEBUG (MainThread) [zigpy_deconz.api] Request id: 0xf5 'aps_data_confirm' for , status: 0x00 2023-07-02 17:25:19.911 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xEBA8](lumi.sen_ill.mgl01): Device seen - marking the device available and resetting counter 2023-07-02 17:25:19.911 DEBUG (MainThread) [homeassistant.components.zha.core.device] [0xEBA8](lumi.sen_ill.mgl01): Update device availability - device available: True - new availability: True - changed: False 2023-07-02 17:25:20.202 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x0e6a000700aa00 2023-07-02 17:25:20.203 DEBUG (MainThread) [zigpy_deconz.api] Received command device_state_changed[, 0] 2023-07-02 17:25:20.203 DEBUG (MainThread) [zigpy_deconz.api] Device state changed response: [, 0] 2023-07-02 17:25:20.206 DEBUG (MainThread) [zigpy_deconz.api] Command Command.aps_data_indication (1, ) 2023-07-02 17:25:20.206 DEBUG (MainThread) [zigpy_deconz.uart] Send: 0x176a000800010001 2023-07-02 17:25:20.210 DEBUG (MainThread) [zigpy_deconz.uart] Frame received: 0x176a0028002100220200000102f7e90204010c000a0018450a5500396666674200afff9bb60e00be 2023-07-02 17:25:20.211 DEBUG (MainThread) [zigpy_deconz.api] Received command aps_data_indication[33, , , 1, , 2, 260, 12, b'\x18E\nU\x009ffgB', 0, 175, 255, 155, 182, 14, 0, -66] 2023-07-02 17:25:20.211 DEBUG (MainThread) [zigpy_deconz.api] APS data indication response: [33, , , 1, , 2, 260, 12, b'\x18E\nU\x009ffgB', 0, 175, 255, 155, 182, 14, 0, -66] 2023-07-02 17:25:20.212 DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=, address=0xE9F7), src_ep=2, dst=AddrModeAddress(addr_mode=, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=None, profile_id=260, cluster_id=12, data=Serialized[b'\x18E\nU\x009ffgB'], tx_options=, radius=0, non_member_radius=0, lqi=255, rssi=-66) ```

Custom quirk

Custom quirk ```python """Aqara Curtain Driver B1 device.""" from __future__ import annotations from typing import Any import logging from zigpy import types as t from zigpy.profiles import zha from zigpy.zcl import foundation from zigpy.zcl.clusters.closures import WindowCovering from zigpy.zcl.clusters.general import ( AnalogOutput, Basic, Groups, Identify, MultistateOutput, OnOff, Ota, PowerConfiguration, Scenes, Time, ) from zigpy.zcl.clusters.measurement import OccupancySensing from zhaquirks import CustomCluster from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.xiaomi import LUMI, XiaomiCustomDevice _LOGGER = logging.getLogger(__name__) class AnalogOutputB1(CustomCluster, AnalogOutput): """Analog output cluster, used to relay current_value to WindowCovering.""" def __init__(self, *args: Any, **kwargs: Any) -> None: """Init.""" _LOGGER.debug("AnalogOutputB1 Cluster init") super().__init__(*args, **kwargs) self._update_attribute( self.attributes_by_name["max_present_value"].id, 100.0 ) # max_present_value self._update_attribute( self.attributes_by_name["min_present_value"].id, 0.0 ) # min_present_value def _update_attribute(self, attrid: int, value: Any) -> None: _LOGGER.debug("AnalogOutput update attribute %04x to %s... ", attrid, value) super()._update_attribute(attrid, value) if attrid == self.attributes_by_name["present_value"].id: self.endpoint.window_covering._update_attribute( # pylint: disable=protected-access WindowCovering.attributes_by_name[ "current_position_lift_percentage" ].id, (100 - value), ) self.endpoint.on_off._update_attribute( # pylint: disable=protected-access OnOff.attributes_by_name["on_off"].id, value > 0 ) class WindowCoveringB1(CustomCluster, WindowCovering): """Window covering cluster, used to cause commands to update the AnalogOutput present_value.""" stop_called = bool(False) def __init__(self, *args: Any, **kwargs: Any) -> None: """Init.""" _LOGGER.debug("WindowCoveringB1 Cluster init") super().__init__(*args, **kwargs) def _update_attribute(self, attrid: int, value: Any) -> None: _LOGGER.debug( "WindowCovering update attribute %04x to %s and stop_called is %s", attrid, value, self.stop_called, ) if self.stop_called: _LOGGER.debug("Recently stopped, updating AnalogOutput value") self.stop_called = bool(False) self.endpoint.analog_output._update_attribute( # pylint: disable=protected-access AnalogOutput.attributes_by_name["present_value"].id, value ) super()._update_attribute(attrid, value) async def command( self, command_id: foundation.GeneralCommand | int | t.uint8_t, *args: Any, manufacturer: int | t.uint16_t | None = None, expect_reply: bool = True, tsn: int | t.uint8_t | None = None, **kwargs: Any, ) -> Any: """Overwrite the commands. Overwrite analog_output's current_value value to make the curtain work as expected. """ _LOGGER.debug("WindowCovering command %04x", command_id) if command_id == self.commands_by_name["up_open"].id: (res,) = await self.endpoint.analog_output.write_attributes( {"present_value": 100} ) return foundation.GENERAL_COMMANDS[ foundation.GeneralCommand.Default_Response ].schema(command_id=command_id, status=res[0].status) if command_id == self.commands_by_name["down_close"].id: (res,) = await self.endpoint.analog_output.write_attributes( {"present_value": 0} ) return foundation.GENERAL_COMMANDS[ foundation.GeneralCommand.Default_Response ].schema(command_id=command_id, status=res[0].status) if command_id == self.commands_by_name["go_to_lift_percentage"].id: _LOGGER.debug("go_to_lift_percentage %d", args[0]) (res,) = await self.endpoint.analog_output.write_attributes( {"present_value": (100 - args[0])} ) return foundation.GENERAL_COMMANDS[ foundation.GeneralCommand.Default_Response ].schema(command_id=command_id, status=res[0].status) if command_id == self.commands_by_name["stop"].id: self.stop_called = bool(True) return await super().command( command_id, *args, manufacturer=manufacturer, expect_reply=expect_reply, tsn=tsn, **kwargs, ) class OnOffB1(CustomCluster, OnOff): """On Off Cluster, used to update state based on AnalogOutput""" def __init__(self, *args: Any, **kwargs: Any) -> None: """Init.""" _LOGGER.debug("OnOffB1 Cluster init") super().__init__(*args, **kwargs) def _update_attribute(self, attrid: int, value: Any) -> None: _LOGGER.debug("OnOff update attribute %04x to %s", attrid, value) super()._update_attribute(attrid, value) class CurtainB1(XiaomiCustomDevice): """Aqara Curtain Driver B1 device.""" def __init__(self, *args: Any, **kwargs: Any) -> None: """Init.""" super().__init__(*args, **kwargs) # type: ignore _LOGGER.info("CurtainB1 custom quirk loaded") signature = { MODELS_INFO: [(LUMI, "lumi.curtain")], ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, PowerConfiguration.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, Time.cluster_id, AnalogOutput.cluster_id, MultistateOutput.cluster_id, WindowCovering.cluster_id, OccupancySensing.cluster_id, ], OUTPUT_CLUSTERS: [ PowerConfiguration.cluster_id, OnOff.cluster_id, Time.cluster_id, AnalogOutput.cluster_id, MultistateOutput.cluster_id, Ota.cluster_id, WindowCovering.cluster_id, OccupancySensing.cluster_id, ], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, PowerConfiguration.cluster_id, OnOffB1, AnalogOutputB1, WindowCoveringB1, ], OUTPUT_CLUSTERS: [ OnOffB1, Time.cluster_id, AnalogOutputB1, Ota.cluster_id, WindowCoveringB1, ], }, }, } ```

Additional information

Now with a working custom quirk attached.

hastarin commented 1 year ago

I've made a start on the custom quirk but it's still not working the way I'd like so requires more work when time permits.

hastarin commented 1 year ago

If anyone can tell me how to get the log output showing via Home Assistant it would be most appreciated. I thought just enabling debug logging for zhaquirks would do the trick but it's still not showing up no matter what I try putting in for the name of the logger.

hastarin commented 1 year ago

Just adding some TS code for reference from https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/devices/xiaomi.ts in case it helps me figure things out:

    {
        zigbeeModel: ['lumi.curtain'],
        model: 'ZNCLDJ11LM',
        description: 'Aqara curtain motor',
        vendor: 'Xiaomi',
        fromZigbee: [fz.xiaomi_basic, fz.xiaomi_curtain_position, fz.xiaomi_curtain_position_tilt],
        toZigbee: [tz.xiaomi_curtain_position_state, tz.xiaomi_curtain_options],
        onEvent: async (type, data, device) => {
            if (type === 'message' && data.type === 'attributeReport' && data.cluster === 'genBasic' &&
                data.data.hasOwnProperty('1028') && data.data['1028'] == 0) {
                // Try to read the position after the motor stops, the device occasionally report wrong data right after stopping
                // Might need to add delay, seems to be working without one but needs more tests.
                await device.getEndpoint(1).read('genAnalogOutput', ['presentValue']);
            }
        },
        exposes: [e.cover_position().setAccess('state', ea.ALL),
            e.binary('running', ea.STATE, true, false)
                .withDescription('Whether the motor is moving or not'),
            e.enum('motor_state', ea.STATE, ['stopped', 'opening', 'closing'])
                .withDescription('Motor state')],
        ota: ota.zigbeeOTA,
    },
hastarin commented 1 year ago

Original comment has been updated with a working custom quirk. I'll need to clean it up at some point and get a pull request sorted.

mgwrd commented 1 year ago

Thank you very much for this quirk! Could it be, that you are refering to the model ZNCLDJ12LM?

I had to tweak the signature for my Aqara device (ZNCLDJ11LM) accordingly (see commented values in INPUT_CLUSTERS and OUTPUT_CLUSTERS):

signature = {
        MODELS_INFO: [(LUMI, "lumi.curtain")],
        ENDPOINTS: {
            # <SizePrefixedSimpleDescriptor endpoint=1 profile=260 device_type=514
            # device_version=1
            # input_clusters=["0x0000", "0x0001", "0x0003", "0x0004", "0x0005", "0x0006", "0x000a", "0x000d", "0x0013", "0x0102", "0x0406"]
            # output_clusters=["0x0001", "0x0006", "0x000a", "0x000d", "0x0013", "0x0019", "0x0102", "0x0406"]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    # PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    Time.cluster_id,
                    AnalogOutput.cluster_id,
                    MultistateOutput.cluster_id,
                    WindowCovering.cluster_id,
                    # OccupancySensing.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    # PowerConfiguration.cluster_id,
                    OnOff.cluster_id,
                    Time.cluster_id,
                    AnalogOutput.cluster_id,
                    MultistateOutput.cluster_id,
                    Ota.cluster_id,
                    WindowCovering.cluster_id,
                    # OccupancySensing.cluster_id,
                ],
            },
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    # PowerConfiguration.cluster_id,
                    OnOffB1,
                    AnalogOutputB1,
                    WindowCoveringB1,
                ],
                OUTPUT_CLUSTERS: [
                    OnOffB1,
                    Time.cluster_id,
                    AnalogOutputB1,
                    Ota.cluster_id,
                    WindowCoveringB1,
                ],
            },
        },
    }
hastarin commented 1 year ago

No I'm guessing maybe you're got a newer variant?

20230820_235219.jpg

mgwrd commented 1 year ago

Yes, you're right. Mine seems to be manufacured in 2022 (HW-Versoin 17, app/ota-file version 36): aqara

Programmierus commented 1 year ago

Can confirm this quirk working with my device, thank you!

image

poldim commented 7 months ago

I just migrated my curtains from the aqara gateway to ZHA and unfortunately ZHA is not correctly supporting these curtains. I'm going to try this quirk.

Is this difficult to PR into core HA?

gordio commented 2 months ago

My version required some deletions about PowerConfiguration.cluster_id and OccupancySensing.cluster_id. image In Manage Zigbee device hw_version: 17 and final file looks like:

"""Aqara Curtain Driver B1 device."""
from __future__ import annotations

import logging
from typing import Any

from zigpy import types as t
from zigpy.profiles import zha
from zigpy.zcl import foundation
from zigpy.zcl.clusters.closures import WindowCovering
from zigpy.zcl.clusters.general import (
    AnalogOutput,
    Basic,
    Groups,
    Identify,
    MultistateOutput,
    OnOff,
    Ota,
    #PowerConfiguration,
    Scenes,
    Time,
)
from zigpy.zcl.clusters.measurement import OccupancySensing

from zhaquirks import CustomCluster
from zhaquirks.const import (
    DEVICE_TYPE,
    ENDPOINTS,
    INPUT_CLUSTERS,
    MODELS_INFO,
    OUTPUT_CLUSTERS,
    PROFILE_ID,
)
from zhaquirks.xiaomi import LUMI, XiaomiCustomDevice

_LOGGER = logging.getLogger(__name__)

class AnalogOutputB1(CustomCluster, AnalogOutput):
    """Analog output cluster, used to relay current_value to WindowCovering."""

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        _LOGGER.debug("AnalogOutputB1 Cluster init")
        super().__init__(*args, **kwargs)
        self._update_attribute(
            self.attributes_by_name["max_present_value"].id, 100.0
        )  # max_present_value
        self._update_attribute(
            self.attributes_by_name["min_present_value"].id, 0.0
        )  # min_present_value

    def _update_attribute(self, attrid: int, value: Any) -> None:
        _LOGGER.debug("AnalogOutput update attribute %04x to %s... ", attrid, value)
        super()._update_attribute(attrid, value)
        if attrid == self.attributes_by_name["present_value"].id:
            self.endpoint.window_covering._update_attribute(  # pylint: disable=protected-access
                WindowCovering.attributes_by_name[
                    "current_position_lift_percentage"
                ].id,
                (100 - value),
            )
            self.endpoint.on_off._update_attribute(  # pylint: disable=protected-access
                OnOff.attributes_by_name["on_off"].id, value > 0
            )

class WindowCoveringB1(CustomCluster, WindowCovering):
    """Window covering cluster, used to cause commands to update the AnalogOutput present_value."""

    stop_called = bool(False)

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        _LOGGER.debug("WindowCoveringB1 Cluster init")
        super().__init__(*args, **kwargs)

    def _update_attribute(self, attrid: int, value: Any) -> None:
        _LOGGER.debug(
            "WindowCovering update attribute %04x to %s and stop_called is %s",
            attrid,
            value,
            self.stop_called,
        )
        if self.stop_called:
            _LOGGER.debug("Recently stopped, updating AnalogOutput value")
            self.stop_called = bool(False)
            self.endpoint.analog_output._update_attribute(  # pylint: disable=protected-access
                AnalogOutput.attributes_by_name["present_value"].id, value
            )
        super()._update_attribute(attrid, value)

    async def command(
        self,
        command_id: foundation.GeneralCommand | int | t.uint8_t,
        *args: Any,
        manufacturer: int | t.uint16_t | None = None,
        expect_reply: bool = True,
        tsn: int | t.uint8_t | None = None,
        **kwargs: Any,
    ) -> Any:
        """Overwrite the commands.

        Overwrite analog_output's current_value
        value to make the curtain work as expected.
        """
        _LOGGER.debug("WindowCovering command %04x", command_id)
        if command_id == self.commands_by_name["up_open"].id:
            (res,) = await self.endpoint.analog_output.write_attributes(
                {"present_value": 100}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)
        if command_id == self.commands_by_name["down_close"].id:
            (res,) = await self.endpoint.analog_output.write_attributes(
                {"present_value": 0}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)
        if command_id == self.commands_by_name["go_to_lift_percentage"].id:
            _LOGGER.debug("go_to_lift_percentage %d", args[0])
            (res,) = await self.endpoint.analog_output.write_attributes(
                {"present_value": (100 - args[0])}
            )
            return foundation.GENERAL_COMMANDS[
                foundation.GeneralCommand.Default_Response
            ].schema(command_id=command_id, status=res[0].status)
        if command_id == self.commands_by_name["stop"].id:
            self.stop_called = bool(True)
            return await super().command(
                command_id,
                *args,
                manufacturer=manufacturer,
                expect_reply=expect_reply,
                tsn=tsn,
                **kwargs,
            )

class OnOffB1(CustomCluster, OnOff):
    """On Off Cluster, used to update state based on AnalogOutput"""

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        _LOGGER.debug("OnOffB1 Cluster init")
        super().__init__(*args, **kwargs)

    def _update_attribute(self, attrid: int, value: Any) -> None:
        _LOGGER.debug("OnOff update attribute %04x to %s", attrid, value)
        super()._update_attribute(attrid, value)

class CurtainB1(XiaomiCustomDevice):
    """Aqara Curtain Driver B1 device."""

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Init."""
        super().__init__(*args, **kwargs)  # type: ignore
        _LOGGER.info("CurtainB1 custom quirk loaded for model ZNCLDJ11LM")

    signature = {
        MODELS_INFO: [(LUMI, "lumi.curtain")],
        ENDPOINTS: {
            # <SizePrefixedSimpleDescriptor endpoint=1 profile=260 device_type=514
            # device_version=1
            # input_clusters=["0x0000", #"0x0001", "0x0003", "0x0004", "0x0005", "0x0006", "0x000a", "0x000d", "0x0013", "0x0102", #"0x0406"]
            # output_clusters=["0x0001", "0x0006", "0x000a", "0x000d", "0x0013", "0x0019", "0x0102", "0x0406"]>
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    #PowerConfiguration.cluster_id,
                    Identify.cluster_id,
                    Groups.cluster_id,
                    Scenes.cluster_id,
                    OnOff.cluster_id,
                    Time.cluster_id,
                    AnalogOutput.cluster_id,
                    MultistateOutput.cluster_id,
                    WindowCovering.cluster_id,
                    #OccupancySensing.cluster_id,
                ],
                OUTPUT_CLUSTERS: [
                    #PowerConfiguration.cluster_id,
                    OnOff.cluster_id,
                    Time.cluster_id,
                    AnalogOutput.cluster_id,
                    MultistateOutput.cluster_id,
                    Ota.cluster_id,
                    WindowCovering.cluster_id,
                    #OccupancySensing.cluster_id,
                ],
            },
        },
    }
    replacement = {
        ENDPOINTS: {
            1: {
                PROFILE_ID: zha.PROFILE_ID,
                DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
                INPUT_CLUSTERS: [
                    Basic.cluster_id,
                    #PowerConfiguration.cluster_id,
                    OnOffB1,
                    AnalogOutputB1,
                    WindowCoveringB1,
                ],
                OUTPUT_CLUSTERS: [
                    OnOffB1,
                    Time.cluster_id,
                    AnalogOutputB1,
                    Ota.cluster_id,
                    WindowCoveringB1,
                ],
            },
        },
    }
gordio commented 2 months ago

AND I changed one parameter in Manage Zigbee device:

  1. Clusters tab;
  2. Select AnalogOutputB1 (Endpoint id: 1, Id: 0x000d, Type: in) in Clusters dropdown;
  3. Select max_present_value (id: 0x0041) in Attributes of the selected cluster;
  4. Press READ ATTRIBUTE;
  5. If Value loaded as 0 -> manually change to 100 and press WRITE ATTRIBUTE.
poldim commented 1 month ago

AND I changed one parameter in Manage Zigbee device:

  1. Clusters tab;
  2. Select AnalogOutputB1 (Endpoint id: 1, Id: 0x000d, Type: in) in Clusters dropdown;
  3. Select max_present_value (id: 0x0041) in Attributes of the selected cluster;
  4. Press READ ATTRIBUTE;
  5. If Value loaded as 0 -> manually change to 100 and press WRITE ATTRIBUTE.

What did doing this fix/solve?

poldim commented 1 month ago

Original comment has been updated with a working custom quirk. I'll need to clean it up at some point and get a pull request sorted.

@hastarin did you do any more cleanup / change to this quirk?

My device signature:

{
  "node_descriptor": {
    "logical_type": 1,
    "complex_descriptor_available": 0,
    "user_descriptor_available": 0,
    "reserved": 0,
    "aps_flags": 0,
    "frequency_band": 8,
    "mac_capability_flags": 142,
    "manufacturer_code": 4447,
    "maximum_buffer_size": 127,
    "maximum_incoming_transfer_size": 100,
    "server_mask": 0,
    "maximum_outgoing_transfer_size": 100,
    "descriptor_capability_field": 0
  },
  "endpoints": {
    "1": {
      "profile_id": "0x0104",
      "device_type": "0x0202",
      "input_clusters": [
        "0x0000",
        "0x0001",
        "0x0006",
        "0x000d",
        "0x0102"
      ],
      "output_clusters": [
        "0x0006",
        "0x000a",
        "0x000d",
        "0x0019",
        "0x0102"
      ]
    }
  },
  "manufacturer": "LUMI",
  "model": "lumi.curtain",
  "class": "aqara-curtain.CurtainB1"
}

Photo:

image

Device in HA:

image
hastarin commented 1 month ago

No I haven't as it's been working for me and I barely understood things enough to do that at the time so I'm sticking with the old, if it ain't broke don't fix it.

ZHA used to show a switch which vanished with an update sometime ago now but I just changed anything that was using it and moved on.

You can certainly try commenting those things out it obviously doesn't have an occupancy sensor but I have no idea what power configuration might do.

Seeing that inverted option in your screenshot does make me wonder if perhaps it might work without the quirk and just setting that instead in the current ZHA but again if it ain't broke...

poldim commented 1 month ago

Good stuff...

Also, I'm not sure what identify does, I don't see it doing anything noticeable