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
754 stars 693 forks source link

[Device Support Request] Ikea E2123: Symfonisk sound remote Gen2 #2223

Closed MrDodojo closed 1 year ago

MrDodojo commented 1 year ago

Is your feature request related to a problem? Please describe. Ikea released a new Symfonisk sound remote (gen2) and it is not yet supported by ZHA. The device when added to homeassistant does not have triggers for pressing the various buttons seperately. The only triggers it has are: Device offline, has been pressed and battery level changed(while the battery level does not indicate correctly either).

Describe the solution you'd like Support for all the buttons: Volume up & down, next & previous, play/pause and 2 custom "shortcut" buttons.

Device signature ```yaml { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0006", "in_clusters": [ "0x0000", "0x0001", "0x0003", "0x0020", "0x1000", "0xfc57" ], "out_clusters": [ "0x0003", "0x0004", "0x0006", "0x0008", "0x0019", "0x1000", "0xfc7f" ] } }, "manufacturer": "IKEA of Sweden", "model": "SYMFONISK sound remote gen2", "class": "zigpy.device.Device" } ```
Diagnostic information ```yaml { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.2.1", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.10.7", "docker": true, "arch": "aarch64", "timezone": "Europe/Amsterdam", "os_name": "Linux", "os_version": "5.15.84-v8", "supervisor": "2023.01.1", "host_os": "Home Assistant OS 9.5", "docker_version": "20.10.22", "chassis": "embedded", "run_as_root": true }, "custom_components": { "adaptive_lighting": { "version": "1.4.1", "requirements": [] }, "localtuya": { "version": "5.0.0", "requirements": [] }, "hacs": { "version": "1.30.1", "requirements": [ "aiogithubapi>=22.10.1" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/zha", "requirements": [ "bellows==0.34.7", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.92", "zigpy-deconz==0.19.2", "zigpy==0.53.0", "zigpy-xbee==0.16.2", "zigpy-zigate==0.10.3", "zigpy-znp==0.9.2" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "1A86", "pid": "55D4", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "vid": "10C4", "pid": "EA60", "description": "*sonoff*plus*", "known_devices": [ "sonoff zigbee dongle plus" ] }, { "vid": "10C4", "pid": "EA60", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*tubeszb*", "known_devices": [ "TubesZB Coordinator" ] }, { "vid": "1A86", "pid": "7523", "description": "*zigstar*", "known_devices": [ "ZigStar Coordinators" ] }, { "vid": "1CF1", "pid": "0030", "description": "*conbee*", "known_devices": [ "Conbee II" ] }, { "vid": "10C4", "pid": "8A2A", "description": "*zigbee*", "known_devices": [ "Nortek HUSBZB-1" ] }, { "vid": "0403", "pid": "6015", "description": "*zigate*", "known_devices": [ "ZiGate+" ] }, { "vid": "10C4", "pid": "EA60", "description": "*zigate*", "known_devices": [ "ZiGate" ] }, { "vid": "10C4", "pid": "8B34", "description": "*bv 2010/10*", "known_devices": [ "Bitron Video AV2010/10" ] } ], "codeowners": [ "@dmulcahey", "@adminiuga", "@puddly" ], "zeroconf": [ { "type": "_esphomelib._tcp.local.", "name": "tube*" }, { "type": "_zigate-zigbee-gateway._tcp.local.", "name": "*zigate*" }, { "type": "_zigstar_gw._tcp.local.", "name": "*zigstar*" }, { "type": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "dependencies": [ "file_upload" ], "after_dependencies": [ "onboarding", "usb", "zeroconf" ], "iot_class": "local_polling", "loggers": [ "aiosqlite", "bellows", "crccheck", "pure_pcapy3", "zhaquirks", "zigpy", "zigpy_deconz", "zigpy_xbee", "zigpy_zigate", "zigpy_znp" ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 10838, "manufacturer": "IKEA of Sweden", "model": "SYMFONISK sound remote gen2", "name": "IKEA of Sweden SYMFONISK sound remote gen2", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4476, "power_source": "Battery or Unknown", "lqi": 159, "rssi": null, "last_seen": "2023-02-24T15:07:16", "available": true, "device_type": "EndDevice", "signature": { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, descriptor_capability_field=, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=True, *is_full_function_device=False, *is_mains_powered=False, *is_receiver_on_when_idle=False, *is_router=False, *is_security_capable=False)", "endpoints": { "1": { "profile_id": 260, "device_type": "0x0006", "in_clusters": [ "0x0000", "0x0001", "0x0003", "0x0020", "0x1000", "0xfc57" ], "out_clusters": [ "0x0003", "0x0004", "0x0006", "0x0008", "0x0019", "0x1000", "0xfc7f" ] } } }, "active_coordinator": false, "entities": [ { "entity_id": "sensor.ikea_of_sweden_symfonisk_sound_remote_gen2_battery", "name": "IKEA of Sweden SYMFONISK sound remote gen2" }, { "entity_id": "button.ikea_of_sweden_symfonisk_sound_remote_gen2_identify", "name": "IKEA of Sweden SYMFONISK sound remote gen2" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "REMOTE_CONTROL" } ], "user_given_name": null, "device_reg_id": "58a64db01f3b9ead5ee6417d4fe2f240", "area_id": "kitchen", "cluster_details": { "1": { "device_type": { "name": "REMOTE_CONTROL", "id": 6 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0004": { "attribute_name": "manufacturer", "value": "IKEA of Sweden" }, "0x0005": { "attribute_name": "model", "value": "SYMFONISK sound remote gen2" } }, "unsupported_attributes": {} }, "0x0001": { "endpoint_attribute": "power", "attributes": {}, "unsupported_attributes": {} }, "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0020": { "endpoint_attribute": "poll_control", "attributes": {}, "unsupported_attributes": {} }, "0x1000": { "endpoint_attribute": "lightlink", "attributes": {}, "unsupported_attributes": {} }, "0xfc57": { "endpoint_attribute": "manufacturer_specific", "attributes": {}, "unsupported_attributes": {} } }, "out_clusters": { "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": {}, "unsupported_attributes": {} }, "0x0008": { "endpoint_attribute": "level", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} }, "0x1000": { "endpoint_attribute": "lightlink", "attributes": {}, "unsupported_attributes": {} }, "0xfc7f": { "endpoint_attribute": "manufacturer_specific", "attributes": {}, "unsupported_attributes": {} } } } } } } ```
Additional logs ``` No Debug logs ```

Additional context image ^This is the device, it has a lot of buttons and would be quite nice to use with homeassistant.

MattWestb commented 1 year ago

Where did you geeing it ?? I hope its being released iin EU very soon then its have being CSA-certified over one year now.

Edit: Its coming next week to Westbahnhoff in Vienna :-(((

MrDodojo commented 1 year ago

Where did you geeing it ?? I hope its being released iin EU very soon then its have being CSA-certified over one year now.

Edit: Its coming next week to Westbahnhoff in Vienna :-(((

Got it at an Ikea in the Netherlands which is in the EU, they didn't have a lot of them so I was sure to grab one. 2 extra buttons for a total of 7 is nice to have

MattWestb commented 1 year ago

Its looks very standard of the cluster and is clean but we must getting all the commands its sending. I think its prity easy getting all Device Automatons (DA) but its needs little work getting it right and testing all is working OK. Its only 2 IKEAs in Austria that having it on the homepage and no inside Vienna but i going and looking tomorrow if its some is having it.

One interesting thing its only Dirigera compatible and TF but shall testing that 2.

MattWestb commented 1 year ago

Do you having ZHA-Toolkit https://github.com/mdeweerd/zha-toolkit installed ??

If yes bind the cluster Id: 0xfc80 on endpoint 2 and 3 to the coordinator (0x0000) and i think you shall getting the commands from the 2 shortcut buttons then its wot IKEA is doing in Dirigera then pairing it.

MrDodojo commented 1 year ago

No I haven't. I just did, don't know how to use it yet. I'd appreciate if you could spoon-feed/help me what to do. Otherwise it will take me a while to understand the docs

Are you asking me to zha_toolkit.bind_ieee like this? I don't know what to set as ieee as the remote only has battery and identify as entities

service: zha_toolkit.bind_ieee
data:
  dst_endpoint: 2
  ieee: ????????
  command_data: 0
  cluster: 0xfc80
SamDeBlock commented 1 year ago

I have the same device, these are the debug logs for the 'dot' buttons:

1 dot button

DEBUG (MainThread) [zigpy_znp.api] Received command: AF.IncomingMsg.Callback(GroupId=0x0000, ClusterId=64639, SrcAddr=0xD1BF, SrcEndpoint=1, DstEndpoint=1, WasBroadcast=<Bool.false: 0>, LQI=63, SecurityUse=<Bool.false: 0>, TimeStamp=1453435, TSN=0, Data=b'\x15\x7C\x11\x42\x01\x01\x01', MacSrcAddr=0xD1BF, MsgResultRadius=29)
DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD1BF), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=0, profile_id=260, cluster_id=64639, data=Serialized[b'\x15|\x11B\x01\x01\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=29, non_member_radius=0, lqi=63, rssi=None)
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Received ZCL frame: b'\x15|\x11B\x01\x01\x01'
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=True, direction=<Direction.Server_to_Client: 0>, disable_default_response=1, reserved=0, *is_cluster=True, *is_general=False, *is_reply=False), manufacturer=4476, tsn=66, command_id=1, *direction=<Direction.Server_to_Client: 0>, *is_reply=False)
WARNING (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Unknown cluster command 1 b'\x01\x01'
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Received command 0x01 (TSN 66): b'\x01\x01'
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] No explicit handler for cluster command 0x01: b'\x01\x01'

2 dot button

DEBUG (MainThread) [zigpy_znp.api] Received command: AF.IncomingMsg.Callback(GroupId=0x0000, ClusterId=64639, SrcAddr=0xD1BF, SrcEndpoint=1, DstEndpoint=1, WasBroadcast=<Bool.false: 0>, LQI=57, SecurityUse=<Bool.false: 0>, TimeStamp=2806217, TSN=0, Data=b'\x15\x7C\x11\x43\x01\x02\x01', MacSrcAddr=0xD1BF, MsgResultRadius=29)
DEBUG (MainThread) [zigpy.application] Received a packet: ZigbeePacket(src=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0xD1BF), src_ep=1, dst=AddrModeAddress(addr_mode=<AddrMode.NWK: 2>, address=0x0000), dst_ep=1, source_route=None, extended_timeout=False, tsn=0, profile_id=260, cluster_id=64639, data=Serialized[b'\x15|\x11C\x01\x02\x01'], tx_options=<TransmitOptions.NONE: 0>, radius=29, non_member_radius=0, lqi=57, rssi=None)
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Received ZCL frame: b'\x15|\x11C\x01\x02\x01'
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Decoded ZCL frame header: ZCLHeader(frame_control=FrameControl(frame_type=<FrameType.CLUSTER_COMMAND: 1>, is_manufacturer_specific=True, direction=<Direction.Server_to_Client: 0>, disable_default_response=1, reserved=0, *is_cluster=True, *is_general=False, *is_reply=False), manufacturer=4476, tsn=67, command_id=1, *direction=<Direction.Server_to_Client: 0>, *is_reply=False)
WARNING (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Unknown cluster command 1 b'\x02\x01'
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] Received command 0x01 (TSN 67): b'\x02\x01'
DEBUG (MainThread) [zigpy.zcl] [0xD1BF:1:0xfc7f] No explicit handler for cluster command 0x01: b'\x02\x01'
MattWestb commented 1 year ago

For both is the remote on original / factory firmware or is it updated by Dirigera ? I have looking little and my have more endpoints and cluster but i was updating it and its now 0x01000032. Signature:

  },
  "data": {
    "ieee": "**REDACTED**",
    "nwk": 43256,
    "manufacturer": "IKEA of Sweden",
    "model": "SYMFONISK sound remote gen2",
    "name": "IKEA of Sweden SYMFONISK sound remote gen2",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "manufacturer_code": 4476,
    "power_source": "Battery or Unknown",
    "lqi": 206,
    "rssi": -54,
    "last_seen": "2023-02-27T13:52:48",
    "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=4476, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, 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": "0x0006",
          "in_clusters": [
            "0x0000",
            "0x0001",
            "0x0003",
            "0x0020",
            "0x1000",
            "0xfc7c"
          ],
          "out_clusters": [
            "0x0003",
            "0x0004",
            "0x0006",
            "0x0008",
            "0x0019",
            "0x1000"
          ]
        },
        "2": {
          "profile_id": 260,
          "device_type": "0x0006",
          "in_clusters": [
            "0x0000",
            "0x0003",
            "0xfc80"
          ],
          "out_clusters": [
            "0x0003",
            "0x0004",
            "0xfc80"
          ]
        },
        "3": {
          "profile_id": 260,
          "device_type": "0x0006",
          "in_clusters": [
            "0x0000",
            "0x0003",
            "0xfc80"
          ],
          "out_clusters": [
            "0x0003",
            "0x0004",
            "0xfc80"
          ]
        }
      }
    },
  }
}
SamDeBlock commented 1 year ago

Mine has definitely been updated to the latest firmware. I first connected it to the dirigera hub and then it automatically started updating.

MrDodojo commented 1 year ago

I did not connect it to a dirigera hub since I don't have one.

MattWestb commented 1 year ago

OK then its logic way @MrDodojo is having different configuration. We must doing 2 device classes so both updated and not updated devices is working OK. @MrDodojo pleas dont updating your device we need it for doing testing then IKEA have locking the chip so its not possible dumping the original firmware (that i normally doing) and flashing it back for doing firmware downgrading.

MattWestb commented 1 year ago

@TheJulianJES I have getting the signature working OK and the quirk is loading for both device classes. The V1 is using only EP1 for all commands and V2 is using EP1 for Light commands and EP2 for button 1 and EP3 for button 2 extra commands from cluster V1 0xFC7F / V20xFC80.

From the V1 log:

[0x0664:1:0xfc7f] Unknown cluster command 1 b'\x01\x01' = B1 Short
[0x0664:1:0xfc7f] Unknown cluster command 1 b'\x02\x01' = B2 Short
[0x0664:1:0xfc7f] Unknown cluster command 1 b'\x01\x02' = B1 Duble
[0x0664:1:0xfc7f] Unknown cluster command 1 b'\x02\x03' = B2 Held

The V2 is not reporting then its need EP 2 and 3 being bound and i dont knowing how to do that.

The next problems is the DA is crazy dim up is not triggered and left and right is triggered for both.

Can you taking one look if you can see what i have doing wrong ?

symfonisk2.zip

Thanks in advance !

MichelBrodatzki commented 1 year ago

I've just opened a pull request with a basic implementation for the E2123 with (probably) V1. Every button should work as a trigger for automations (and some buttons even with long press and double press). Currently I'm struggling a bit with passing arguments as both ARGS and PARAMS (or with passing the at all as PARAMS) with the listener_event(...) approach.

@MattWestb I'm quite new to creating device handlers and I don't have an updated E2123 at hand to test my theories, but maybe the event listener approach might be a way to make implementing V2 easier? (i.e. passing the command actions from EP2 and 3 to the custom LevelControlCluster)

MattWestb commented 1 year ago

Great thanks for working on the quirk for our devices @MichelBrodatzki !!

I have not testing it only looking how you have doing things and im not one code warier so i cant saying it its working or how good / bad it is only testing and see if its working OK or not. First is better puttingVer1 for version in the device class name so not need renaming it later and remaking the tests and then its easy adding the variant 2 and so on. Also put the firmware version in the device class dock string is good.

You is having WWAH_CLUSTER_ID in the signature and replacing it with IKEA_CLUSTER_ID that is not making any sense and can making problem if we need using the cluster in the future.

In the INIT you is making one device class ShortcutCluster for cluster 0xFC7F = OK but the Ver 2 is using cluster 0xfc80 for chortcut function on EP 2 and 3 (i must paring it with Dirigera and sniffing it for verifying it). Its very likely we need making 2 different device classes for getting it working but wait until i have sniffing the functions how Dirigera is doing it.

The device automatons shall working with only putting one of more args / parms but my test DAs is spinning completely but i getting the right events from the device and its likely we is hitting one bug in HA that braking some functions (very deep and not oft seen but its there) so take it with "little salt" for the moment until we have testing it more or finding other versions that working OK.

I starting sniffing Dirigera an then i installing your quirk and testing the V1 functions.

MattWestb commented 1 year ago

Also Also i think the PowerConfiguration2AAACluster is not needed and can using the normal PowerConfiguration.cluster_id like all other V24.X firmware devices. My Sy2 is showing 50 and 82% without doubling and is having unchanged batteries from IKEA that shall need being changed for have 100%. What is your device reporting and is you using alkaline or rehanged batteries ?

MattWestb commented 1 year ago

V2 looks using complete different commends but its having release after long press implanted and is sent from EP 2 and 3 for button 1 and 2. Attaching the frames its sending and have the paring and the button press saved if we need it later. Symfonisk2UpdatedDASniff.txt

It can being the command is the same as V1 then wireshark is little outdated but i dont have the knowledge analyzing that.

MrDodojo commented 1 year ago

Anything for me to test atm? I could test the quirk MichelBrodatzki made but I don't understand how to. I added a custom quirk location to my configuration, do I need to add the whole repo or just the changed files?

MichelBrodatzki commented 1 year ago

@MattWestb

First is better putting Ver1 for version in the device class name so not need renaming it later and remaking the tests and then its easy adding the variant 2 and so on. Also put the firmware version in the device class dock string is good.

Yes, that's a great idea. To be perfectly honest with you, I've only seen this issue and your contribution after working on it for some time, so this wasn't even on my radar!

You is having WWAH_CLUSTER_ID in the signature and replacing it with IKEA_CLUSTER_ID that is not making any sense and can making problem if we need using the cluster in the future.

True, that's just an oversight and was not intended. Will be fixed!

What is your device reporting and is you using alkaline or rehanged batteries ?

I've tried both rechargeable NiMH AAA and Alkaline AAA, all new or fully charged. Both showed a battery state of 50%. Maybe that was just a ZigBee thing and the info hasn't yet truly arrived/updated. I'll first fix the other things and then I'll play around with battery reporting :-)

@MrDodojo If you're still on the old version of the sound remote, my files should work for you. To add them as a custom quirk after enabling the custom quirk location, you can follow these steps:

  1. inside the custom quirk location, create a folder named ikea
  2. to this folder add the zhaquirks/ikea/__init__.py and zhaquirks/ikea/symfonisk.py from my pull request
  3. now you need to modify symfonisk.py to not use the existing symfonisk module. Just change the line starting with from zhaquirks.ikea import ( (currently that's line 49, but may change with further commits) to from . import (. This will use the __init__.pyfrom the same folder and not the "old" from zha-device-handlers
  4. restart Home Assistant and check if the quirk was loaded (should appear under "Zigbee Info" for the device in Home Assistant)
MattWestb commented 1 year ago

I have getting all the V2 commands working from the network but i cant getting them being sent to zha_events. The command part looks like this:

class ShortcutV2Cluster(CustomCluster):
    """Ikea Shortcut Button Cluster Variant 2."""

    name = "ShortcutClusterV2"
    cluster_id = 0xFC80

    server_commands = {
        0x01: foundation.ZCLCommandDef(
            "shortcut",
            {
                "shortcut_mode": t.int8s,
                "param1": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
       ),
       0x02: foundation.ZCLCommandDef(
            "hold",
            {
                "held": t.uint4_t,
                "param1": t.uint4_t,
               },
            False,
            is_manufacturer_specific=True,
        ),
        0x03: foundation.ZCLCommandDef(
            "press",
            {
                "pressed": t.uint4_t,
                "param1": t.uint4_t,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x04: foundation.ZCLCommandDef(
            "releas",
            {
                "released": t.uint4_t,
                "param1": t.uint4_t,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x05: foundation.ZCLCommandDef(
        "unknown",
            {
                "unknown": t.uint4_t,
                "param1": t.uint4_t,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x06: foundation.ZCLCommandDef(
            "double",
            {
                "double_pressd": t.int8s,
                "param1": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
    }

    def handle_cluster_request(
        self,
        hdr: foundation.ZCLHeader,
        args: List[Any],
        *,
        dst_addressing: Optional[
            Union[t.Addressing.Group, t.Addressing.IEEE, t.Addressing.NWK]
        ] = None,
    ):
        """Handle a cluster request."""
        self.debug(
            "%s: handle_cluster_request - Command: %s Data: %s",
            self.name,
            hdr.command_id,
            args,
        )

        if hdr.command_id == 0x01:
            self.debug("%s: sending ZHA event", self.name)

            event_args = {
                "step_mode": (
                    LevelControl.StepMode.Up
                    if args.shortcut_mode == 1
                    else LevelControl.StepMode.Down
                ),
                "step_size": args.param1,
                "transition_time": 0,
            }

            self.endpoint.out_clusters[8].listener_event(
                ZHA_SEND_EVENT, "step_with_on_off", event_args
            )

The last part i have not touched then i dont understanding it.

@javicalle do you knowing how getting the command to events ? The log is now looks like this then pressing the button:

2023-03-05 14:01:07.573 DEBUG (MainThread) [zigpy.zcl] [0xBD72:2:0xfc80] Decoded ZCL frame: ShortcutV2Cluster:press(pressed=0, param1=0)
2023-03-05 14:01:07.576 DEBUG (MainThread) [zigpy.zcl] [0xBD72:2:0xfc80] Received command 0x03 (TSN 85): press(pressed=0, param1=0)
2023-03-05 14:01:07.580 DEBUG (MainThread) [zigpy.zcl] [0xBD72:2:0xfc80] ShortcutClusterV2: handle_cluster_request - Command: 3 Data: press(pressed=0, param1=0)
MattWestb commented 1 year ago

I was looking how we have doing the TS004X implementation that is only sending events and not using strange cluster and also is doing the arg and parms OK and i like that but i cant doing the coding for Symfonisk 2. class TuyaSmartRemoteOnOffCluster(OnOff, EventableCluster):

javicalle commented 1 year ago

@javicalle do you knowing how getting the command to events ?

Maybe implementing the EventableCluster?

class ShortcutV2Cluster(CustomCluster, EventableCluster):

You will need to add the import like:

from zhaquirks import EventableCluster, ...
javicalle commented 1 year ago

Let me take a look at the args part...

MattWestb commented 1 year ago

(CustomCluster, EventableCluster) was not working so i was trying with (EventableCluster) and its loading but i dont knowing if its working.

I have copy from the tuya but i dont know how to do it working:

    def handle_cluster_request(
        self,
        hdr: foundation.ZCLHeader,
        args: List[Any],
        *,
        dst_addressing: Optional[
            Union[t.Addressing.Group, t.Addressing.IEEE, t.Addressing.NWK]
        ] = None,
    ):

        if hdr.command_id == 0x00:
            rotate_type = args[0]
            self.listener_event(
                ZHA_SEND_EVENT, self.rotate_type.get(rotate_type, "unknown"), []
            )
        elif hdr.command_id == 0x03:
            press_type = args[0]
            self.listener_event(
                ZHA_SEND_EVENT, self.press_type.get(press_type, "unknown"), []
            )

I do only having different commands 1-6 and the data looks being not interesting then can see from witch EP its coming.

MichelBrodatzki commented 1 year ago

Sorry for the radio silence, I've been quite busy with university. But I think I just solved the args part! Let me just test one last thing, then I'll push it and comment what I did! Give me 10min :D

MattWestb commented 1 year ago

For V2 commands looks like this:

01 first frame 02 hold 03 short 04 release 05 ? 06 double

Command 0x06 is the data length 2 all other is data length 1. Data is always length 1 but looks being irrelevant for us.

And the device is sending one command 1 and then one command 2-6. If command 2 its sending one command 4 then being released without command 1. So command 1 is only some button is being pressed.

MattWestb commented 1 year ago

Only for information i was reading that IKEA is forcing update of version 1 to new firmware and user and we cant reversing it (chip is debug locked) so V1 is only one "factory delivered version" and not one production version. Also it is missing release after held.

MichelBrodatzki commented 1 year ago

So, after testing I can say that V1 should work now. The solution actually was pretty simple, I just haven't interpreted to underlying code correctly.

So for argument passing to work properly, you just have to create a new class which inherits from CommandSchema (which in turn is a subclass of t.Struct) and then you can pass an object of that class to listener_event. The method handles CommandSchema differently to dicts and lists.

MichelBrodatzki commented 1 year ago

Only for information i was reading that IKEA is forcing update of version 1 to new firmware and user and we cant reversing it (chip is debug locked) so V1 is only one "factory delivered version" and not one production version. Also it is missing release after held.

That's an interesting information, thanks! I'd say that supporting V1 is still pretty important, as OTA is not on by default for ZHA, so there might still be some people on V1 when adding their E2123 to Home Assistant.

MattWestb commented 1 year ago

I think that was one TF user that was not getting the OTA then TF dont have it then Dirigera is forcing update in the background. Also many users dont have one IKEA GW and dont getting updates if not doing it manual in ZHA so V1 its needed !

And i think Vindstyrka is not being supported by TF GW only Dirigera.

javicalle commented 1 year ago

@MattWestb if you remove the handle_cluster_request function from your ShortcutV2Cluster class you will get ZHA_EVENTs with the command_id and args from the device. I would expect that you got events like:

    "event_type": "zha_event",
    "data": {
        "device_ieee": "14:b4:57:ff:fe:70:c4:bb",
        "unique_id": "14:b4:57:ff:fe:70:c4:bb:1:0x0102",
        "device_id": "a9f1979c6084affe5c7d5a1b6bb72463",
        "endpoint_id": 1,
        "cluster_id": 64640,
        "command": "press",
        "args": [],
        "params": {pressed=0, param1=0}
    },

Then I suppose that you could match the event in the device_automation_triggers, doesn't you?

But if you want some kind of translation from your device commands to 'standar' commands (like step, step_with_on_off, etc) that would require some kind of mapping table to translate comands and params. Is that what you want to achieve?

MattWestb commented 1 year ago

Fast test:

event_type: zha_event
data:
  device_ieee: 1c:34:f1:ff:fe:78:57:15
  unique_id: 1c:34:f1:ff:fe:78:57:15:3:0xfc80
  device_id: 7b803a8f3912b70e1949639ccf9e0ad5
  endpoint_id: 3
  cluster_id: 64640
  command: hold
  args:
    - 0
    - 0
  params:
    held: 0
    param1: 0
origin: LOCAL
time_fired: "2023-03-05T20:58:22.078751+00:00"
context:
  id: 01GTSS2ZFYZ342GQ3D8SGDVC6T
  parent_id: null
  user_id: null

event_type: zha_event
data:
  device_ieee: 1c:34:f1:ff:fe:78:57:15
  unique_id: 1c:34:f1:ff:fe:78:57:15:3:0xfc80
  device_id: 7b803a8f3912b70e1949639ccf9e0ad5
  endpoint_id: 3
  cluster_id: 64640
  command: releas
  args:
    - 1
    - 0
  params:
    released: 1
    param1: 0
origin: LOCAL
time_fired: "2023-03-05T20:58:24.264093+00:00"
context:
  id: 01GTSS31M8D72JS443SF19PRK7
  parent_id: null
  user_id: null

Need fixing the commands to Matter standard names and testing all of them but that is for tomorrow. I going for doing the Matter compatible and doing DA for the shortcut cluster and normal for the Light control commands.

Its no use doing other not true commands better using the raw and the user can using them in automatons and doing what they like and its more flexible.

Great thanks @javicalle you have make my week and the next to coming 2 !!

MattWestb commented 1 year ago

@javicalle I is using int8s for parms (taken from IKEA scene cluster implanting) and its working OK for all commands but command 0x06 is 2 bytes and all other is 1. So i is using int16s for it or i getting error in the HA log that data is renaming. But the parm dot look as expected. From wirschark:

ZigBee Cluster Library Frame, Mfr: Ikea of Sweden (0x117c)
    Frame Control Field: Cluster-specific (0x15)
    Manufacturer Code: Ikea of Sweden (0x117c)
    Sequence Number: 66
    Command: Unknown (0x06)
Data (2 bytes)
    Data: 0002
    [Length: 2]

And in HA the parm is:

event_type: zha_event
data:
  device_ieee: 1c:34:f1:ff:fe:78:57:15
  unique_id: 1c:34:f1:ff:fe:78:57:15:2:0xfc80
  device_id: 7b803a8f3912b70e1949639ccf9e0ad5
  endpoint_id: 2
  cluster_id: 64640
  command: MultiPressComplete
  args:
    - 512
  params:
    Data: 512
origin: LOCAL
time_fired: "2023-03-06T07:34:13.957233+00:00"
context:
  id: 01GTTXF9650M39ZZ9MDGPVDVC0
  parent_id: null
  user_id: null

and i think it shall being 0002 (2 pressing of the button its not supporting more but the right shall being using command 5 for every press and one 6 for the last if i have understanding the cluster right). Is i using the wrong type for getting the data then its doing it like 0x02, 0x00 = 512 dec instead 0x00, 0x02 = 0002 dec ? Its no braking thing then we dont need the parm then all commands is unique and is being sent from respective endpoint OK but im sure its wrong and can making problem in the future and its better making it right from the beginning.

All commands is working OK and only need do more testing and making one new quirk for the device and all the DAs.

Thanks in advance for your help :-)))

MattWestb commented 1 year ago

Working Variant 2 with updated firmware. Configure local quirk and make one ikea directory in it and putting the 2 files in it. Shall looking like this:

/config/custom_zha_quirks/ikea/__init__.py
/config/custom_zha_quirks/ikea/symfonisk2.py

symfonisk2.zip

Restarting HA. Your need binding EP 2 and 3 with ZHA-Toolkit until i have implemented it in the quirk. DA for the light commands in implanted for Shortcut not but its coming and you can using zha_events as trigger in automatons.

Battery info fixed and also for upcoming gen 2 2 button devices is implanted.

For our devs:

How do i making ZHA binding EP2 and 3 with the Chortcut cluster in the quirk ? Still not found how doing it :-((

Is its possible using the DAs from the common DAs and adding device specific ones so not need having 2 block with 2/3 the same code ? Fixed and working as expected !!

Thanks in advance !!

javicalle commented 1 year ago

Is i using the wrong type for getting the data then its doing it like 0x02, 0x00 = 512 dec instead 0x00, 0x02 = 0002 dec ? Its no braking thing then we dont need the parm then all commands is unique and is being sent from respective endpoint OK but im sure its wrong and can making problem in the future and its better making it right from the beginning.

Hi @MattWestb, that sounds me to all the big/little endian stuff but I have very little knowledge in this mater. I would suggest to you to try with a int16s_be type and see if that make it work:

        0x06: foundation.ZCLCommandDef(
            "double",
            {
                "double_pressd": t.int8s,
                "param1": t.int16s_be,
            },
            False,
            is_manufacturer_specific=True,
        ),

But it is just a random change to get the expected value.

javicalle commented 1 year ago

Just thinking, could it be that command 0x06 takes 2 int8s params? It seems extrange to me that just one command would take an int16s_be param.

Could it be that command 0x06 would be something like this?:

        0x06: foundation.ZCLCommandDef(
            "double",
            {
                "double_pressd": t.int8s,
                "param1": t.int8s,
                "param2": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
MattWestb commented 1 year ago

With BE is looks great:

event_type: zha_event
data:
  device_ieee: 1c:34:f1:ff:fe:78:57:15
  unique_id: 1c:34:f1:ff:fe:78:57:15:2:0xfc80
  device_id: 7b803a8f3912b70e1949639ccf9e0ad5
  endpoint_id: 2
  cluster_id: 64640
  command: MultiPressComplete
  args:
    - 2
  params:
    Data: 2
origin: LOCAL
time_fired: "2023-03-06T13:00:02.817222+00:00"
context:
  id: 01GTVG3VW1NWQB85JE4MHBA86F
  parent_id: null
  user_id: null

but your thinking its the right !! I can being 2 parms and we dont knowing that. Im planing only using the commands and no arg/parms in the DA then the switch is sending unique commands / EP so if we later changing how we is doing it in INIT the DA shall not being broken only user that is using zha_event can getting broken automatons.

I think making one protocol with 64K possible repeated key pressings is little over shutting so 2 args is more realistic (CSA-IOT have much brain capacity for doing things good).

Thanks for helping !

MattWestb commented 1 year ago

Im going for 2 args and the events then looks like this:

event_type: zha_event
data:
  device_ieee: 1c:34:f1:ff:fe:78:57:15
  unique_id: 1c:34:f1:ff:fe:78:57:15:3:0xfc80
  device_id: 7b803a8f3912b70e1949639ccf9e0ad5
  endpoint_id: 3
  cluster_id: 64640
  command: MultiPressComplete
  args:
    - 0
    - 2
  params:
    Data1: 0
    Data2: 2
origin: LOCAL
time_fired: "2023-03-06T13:14:57.459885+00:00"
context:
  id: 01GTVGZ5HKM9SREKZ6VQ5Z4GHK
  parent_id: null
  user_id: null

If having wrong we is knowing it in next year or so and can fixing it then ;-)

Edit: I think the args is for multi press the number of the key presses and the completed is having that 2 and one check function. So if missing one frame the system can still getting the numbers of pressings in the end.

MattWestb commented 1 year ago

I have founding the ZAP code that current Matter is using and its 2 args for it: https://github.com/project-chip/connectedhomeip/blob/7e7b7afbef6bc89be966ed6cafe543f81fcf1199/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h#L11246-L11253

    uint8_t previousPosition            = static_cast<uint8_t>(0);
    uint8_t totalNumberOfPressesCounted = static_cast<uint8_t>(0);

So i making 2 int8s args :-)))

Edit: The multiple pressed shall also having 2 args but IKEA is only using one for the moment.

MattWestb commented 1 year ago

Im trying reusing DA from the V1 and using them for V2 and adding the extra one and have getting this working so good that ZHA is starting but i only getting the V1 DA.


    device_automation_triggers = IkeaSYMFONISKgen2V1.device_automation_triggers.copy()
    device_automation_triggers.update(
        {
            (SHORT_PRESS, BUTTON_1): {ENDPOINT_ID: 2, COMMAND: COMMAND_BUTTON_SINGLE},
            (DOUBLE_PRESS, BUTTON_1): {ENDPOINT_ID: 2, COMMAND: COMMAND_BUTTON_DOUBLE},
            (LONG_PRESS, BUTTON_1): {ENDPOINT_ID: 2, COMMAND: COMMAND_BUTTON_HOLD},
            (SHORT_PRESS, BUTTON_2): {ENDPOINT_ID: 3, COMMAND: COMMAND_BUTTON_SINGLE},
            (DOUBLE_PRESS, BUTTON_2): {ENDPOINT_ID: 3, COMMAND: COMMAND_BUTTON_DOUBLE},
            (LONG_PRESS, BUTTON_2): {ENDPOINT_ID: 3, COMMAND: COMMAND_BUTTON_HOLD},
        }
    )

Is im doing somthing wrong or is not not possible "importing" DAs and using it on one other class and adding more ADs for the new class ??

Edit: I think its better making one common DA class for both V1 and V2 and then importing it to V1 and V2 DA class and adding the Shurtcut commands DA in respective class DA.

javicalle commented 1 year ago

Is im doing somthing wrong or is not not possible "importing" DAs and using it on one other class and adding more ADs for the new class ??

I don't see any problem in your code. IMO that must work. Have you deleted the __pycache__ folder?

MattWestb commented 1 year ago

The cach i never deleting then it can being god old code storage in it that i can reusing !! ;-))

I trying doing the good old cach deletion and restarting HA and not only reloading ZHA that is the easy to do then having problems with syntax (that i often dont understand).

Thanks for helping J !!

MattWestb commented 1 year ago

Was getting problem with the Zram log is full and the system is not liking it :-((

But i can now doing 20 DA for the device !!!

Do something when…
Device offline
"Toggle" pressed
"Dim up" pressed
"Dim up" continuously pressed
"Dim down" pressed
"Dim down" continuously pressed
"Right" pressed
"Left" pressed
"button_1" InitialPress
"First button" pressed
"First button" double clicked
"First button" continuously pressed
"First button" released after long press
"button_2" InitialPress
"Second button" pressed
"Second button" double clicked
"Second button" continuously pressed
"Second button" released after long press
IKEA SYMFONISK sound remote gen2 Upgraded Identify has been pressed
IKEA SYMFONISK sound remote gen2 Upgraded Battery battery level changes

Dim up and down hold is repeating commands and right and left is only one command sent. All is tested with automatons made from the device DA.

Only left how getting EP 2 and 3 bond to the coordinator from the quirk so the getting the commands.

MattWestb commented 1 year ago

Updated dirty quirk only for V2 (updated fimware) if some like testing. symfonisk2.zip

cooljimy84 commented 1 year ago

I think i'm on the old firmware for the gen2 (i've not enabled zha update or connected it to any thing other than HA) HA reports Firmware: 0x00010012, I'll see if i can work out this custom quirk thing and test (if that would be any help?)

MattWestb commented 1 year ago

Your remote is on factory firmware and using V1 quirk. I have getting the events working on both V1 and V2 but i having problem getting the command parsing working OK for using them in DA in one not to dirty way. I have sniffing Dirigera updating one V1 remote and don working file that is signed and OK and the device is accepting it = OK for updating to V2 but is for the moment not recommended upgrading it then we need both V1 and V2 for testing but you can do it if you like.

cooljimy84 commented 1 year ago

I'll stick on v1 firmware for now, as it does all i need for now. I've just tested the posted zip and i no longer get the "unknown cluster command" for the single dot and double dot buttons. But they also don't show any presses in the device log in Home Assistant. Sorry if that was expected, but thought I'd report back

MattWestb commented 1 year ago

The above posted quirk is only working with V2 and an can getting it doing events for V1 but i cant getting the DA working with V1. Working but not so nice implanted is here https://github.com/zigpy/zha-device-handlers/pull/2236.

MattWestb commented 1 year ago

@javicalle I can getting nice zha_events from both version with this code in INIT:

class ShortcutV1Cluster(EventableCluster):
    """Ikea Shortcut Button Cluster Variant 1."""

    name = "ShortcutClusterV1"
    cluster_id = IKEA_SHORTC_CLUSTER_ID

    server_commands = {
        0x01: foundation.ZCLCommandDef(
            "ShortCut",
            {
                "shortcut_mode": t.int8s,
                "Data": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
    }

class ShortcutV2Cluster(EventableCluster):
    """Ikea Shortcut Button Cluster Variant 2."""

    name = "ShortcutClusterV2"
    cluster_id = IKEA_MSWITCH_CLUSTER_ID

    server_commands = {
        0x00: foundation.ZCLCommandDef(
            "SwitchLatched",
            {
                "NewPosition": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x01: foundation.ZCLCommandDef(
            "InitialPress",
            {
                "NewPosition": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x02: foundation.ZCLCommandDef(
            "LongPress",
            {
                "PreviousPosition": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x03: foundation.ZCLCommandDef(
            "ShortRelease",
            {
                "PreviousPosition": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x04: foundation.ZCLCommandDef(
            "LongRelease",
            {
                "PreviousPosition": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x05: foundation.ZCLCommandDef(
            "MultiPressOngoing",
            {
                "NewPosition": t.int8s,
#                 "CurrentNumberOfPressesCounted": t.int8s, # Not implanted by IKEA for the moment.
            },
            False,
            is_manufacturer_specific=True,
        ),
        0x06: foundation.ZCLCommandDef(
            "MultiPressComplete",
            {
                "PreviousPosition": t.int8s,
                "TotalNumberOfPressesCounted": t.int8s,
            },
            False,
            is_manufacturer_specific=True,
        ),
    }

And events for V2:

  endpoint_id: 2
  cluster_id: 64640
  command: LongRelease
  args:
    - 0
  params:
    PreviousPosition: 0  

  endpoint_id: 2
  cluster_id: 64640
  command: ShortRelease
  args:
    - 0
  params:
    PreviousPosition: 0

And V1:

  endpoint_id: 1
  cluster_id: 64639
  command: ShortCut
  args:
    - 1
    - 1
  params:
    shortcut_mode: 1
    Data: 1

  endpoint_id: 1
  cluster_id: 64639
  command: ShortCut
  args:
    - 1
    - 3
  params:
    shortcut_mode: 1
    Data: 3

How shall i doing the V1 DA triggering on the command: ShortCut and using the args ? Also for V2 is doing different commands and i cont need the args but how do i getting them in the DA ??

I have patching commends from constants in the quirk so the names is matching and its working but its wrong way.

MattWestb commented 1 year ago

I was defining one new command in the quirk like this COMMAND_SHORTCUT = "ShortCut" and doing the DA like this:

    device_automation_triggers = (
        Symfonisk2CommonTriggers.device_automation_triggers.copy()
    )
    device_automation_triggers.update(
        {
            (SHORT_PRESS, BUTTON_1): {
                COMMAND: COMMAND_SHORTCUT,
                PARAMS: {"shortcut_mode": 1, "Data": 1},
            },
            (DOUBLE_PRESS, BUTTON_1): {
                COMMAND: COMMAND_SHORTCUT,
                PARAMS: {"shortcut_mode": 1, "Data": 2},
            },
            (LONG_PRESS, BUTTON_1): {
                COMMAND: COMMAND_SHORTCUT,
                PARAMS: {"shortcut_mode": 1, "Data": 3},
            },
            (SHORT_PRESS, BUTTON_2): {
                COMMAND: COMMAND_SHORTCUT,
                PARAMS: {"shortcut_mode": 2, "Data": 1},
            },
            (DOUBLE_PRESS, BUTTON_2): {
                COMMAND: COMMAND_SHORTCUT,
                PARAMS: {"shortcut_mode": 2, "Data": 2},
            },
            (LONG_PRESS, BUTTON_2): {
                COMMAND: COMMAND_SHORTCUT,
                PARAMS: {"shortcut_mode": 2, "Data": 3},
            },
        },
    )

And the DA for button 1 and 2 is working OK.

Is this the right way to do it ? Can i getting V2 parsing command type 1 - 6 to one command with type as args with the same COMMAND_SHORTCUT so i only need one new command implanted (i have 5 done now and its feel wrong) ?

If getting one handle from this in the quirk DA its being much easier only using command and args from it: https://github.com/zigpy/zha-device-handlers/blob/9c1bc72ecfbba6aa947cca553777d75c9bfd7574/zhaquirks/__init__.py#L104

Edit: I was thinking somthing like we have don with the TS004F here and getting more functions from manufacture cluster commands: https://github.com/zigpy/zha-device-handlers/blob/9c1bc72ecfbba6aa947cca553777d75c9bfd7574/zhaquirks/tuya/__init__.py#L934.

MattWestb commented 1 year ago

@cooljimy84 Can you testing the posted quirk then i have updating it and its looks working OK for my devices ? https://github.com/zigpy/zha-device-handlers/issues/2223#issuecomment-1455965893

MrDodojo commented 1 year ago

@MattWestb is the quirk you just mentioned for the V1(not updated) or the V2(updated with ikea hub). You mentioned "variant 2", i thought cooljimy had a V1(not updated) remote