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
720 stars 668 forks source link

[Device Support Request] - ZigbeeModel: TS0601, Zigbee Manufacturer: _TZE204_nklqjk62 #2635

Open tinygalaxyltd opened 11 months ago

tinygalaxyltd commented 11 months ago

Problem description

Zigbee Manufacturer: _TZE204_nklqjk62 is different to the one currently supported - Zigbee Manufacturer: _TZE200_nklqjk62. Device doesn't recognise any of the entity attributes. Binding is successful, but reporting is blank using https://github.com/zigpy/zha-device-handlers/blob/dev/zhaquirks/tuya/ts0601_garage.py.

It is the same device mentioned here - https://github.com/Koenkk/zigbee2mqtt/issues/18633

Solution description

Device is currently unusable and without showing the attributes. You can't open the gate or see if the gate sensors are working.

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device signature

Device signature ```json [Paste the device signature here] ```

Diagnostic information

Diagnostic information ```json [Paste the diagnostic information here] ```

Logs

Logs ```python [Paste the logs here] ```

Custom quirk

Custom quirk ```python [Paste your custom quirk here] ```

Additional information

None

Danieldiazi commented 11 months ago

I friend of mine had just bought this model and he has used this quirk file:

    from typing import Dict
    from zigpy.profiles import zha
    from zigpy.quirks import CustomDevice
    import zigpy.types as t
    from zigpy.zcl.clusters.general import Basic, GreenPowerProxy, Groups, Ota, Scenes, Time
    from zigpy.zcl.clusters.security import IasZone

    from zhaquirks.const import (
        DEVICE_TYPE,
        ENDPOINTS,
        INPUT_CLUSTERS,
        MODELS_INFO,
        OUTPUT_CLUSTERS,
        PROFILE_ID,
    )
    from zhaquirks.tuya import TuyaLocalCluster
    from zhaquirks.tuya.mcu import (
        TuyaMCUCluster,
        TuyaOnOff,
        DPToAttributeMapping

    )

    #    TuyaDPType,

    from zhaquirks.tuya.mcu import ( TuyaMCUCluster
    )

    from zhaquirks.tuya.ts0601_dimmer import TuyaOnOffNM

    ZONE_TYPE = 0x0001

    class ContactSwitchCluster(TuyaLocalCluster, IasZone):
        """Tuya ContactSwitch Sensor."""

        _CONSTANT_ATTRIBUTES = {ZONE_TYPE: IasZone.ZoneType.Contact_Switch}

        def _update_attribute(self, attrid, value):
            self.debug("_update_attribute '%s': %s", attrid, value)
            super()._update_attribute(attrid, value)

    class TuyaGarageManufCluster(TuyaMCUCluster):
        """Tuya garage door opener."""

        attributes = TuyaMCUCluster.attributes.copy()
        attributes.update(
            {
                # ramdom attribute IDs
                0xEF02: ("dp_2", t.uint32_t, True),
                0xEF04: ("dp_4", t.uint32_t, True),
                0xEF05: ("dp_5", t.uint32_t, True),
                0xEF0B: ("dp_11", t.Bool, True),
                0xEF0C: ("dp_12", t.enum8, True),
            }
        )

        dp_to_attribute: Dict[int, DPToAttributeMapping] = {
            # garage door trigger ¿on movement, on open, on closed?
            1: DPToAttributeMapping(
                TuyaOnOffNM.ep_attribute,
                "on_off",
                #dp_type=TuyaDPType.BOOL,
            ),
            2: DPToAttributeMapping(
                TuyaMCUCluster.ep_attribute,
                "dp_2",
                #dp_type=TuyaDPType.VALUE,
            ),
            3: DPToAttributeMapping(
                ContactSwitchCluster.ep_attribute,
                "zone_status",
                #dp_type=TuyaDPType.BOOL,
                converter=lambda x: IasZone.ZoneStatus.Alarm_1 if x else 0,
                endpoint_id=2,
            ),
            4: DPToAttributeMapping(
                TuyaMCUCluster.ep_attribute,
                "dp_4",
                #dp_type=TuyaDPType.VALUE,
            ),
            5: DPToAttributeMapping(
                TuyaMCUCluster.ep_attribute,
                "dp_5",
                #dp_type=TuyaDPType.VALUE,
            ),
            11: DPToAttributeMapping(
                TuyaMCUCluster.ep_attribute,
                "dp_11",
                #dp_type=TuyaDPType.BOOL,
            ),
            # garage door status (open, closed, ...)
            12: DPToAttributeMapping(
                TuyaMCUCluster.ep_attribute,
                "dp_12",
                #dp_type=TuyaDPType.ENUM,
            ),
        }

        data_point_handlers = {
            1: "_dp_2_attr_update",
            2: "_dp_2_attr_update",
            3: "_dp_2_attr_update",
            4: "_dp_2_attr_update",
            5: "_dp_2_attr_update",
            11: "_dp_2_attr_update",
            12: "_dp_2_attr_update",
        }

    class TuyaGarageSwitchTO(CustomDevice):
        """Tuya Garage switch."""

        signature = {
            MODELS_INFO: [
                ("_TZE204_nklqjk62", "TS0601"),
            ],
            ENDPOINTS: {
                # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
                # input_clusters=[0, 4, 5, 61184]
                # output_clusters=[10, 25]>
                1: {
                    PROFILE_ID: zha.PROFILE_ID,
                    DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
                    INPUT_CLUSTERS: [
                        Basic.cluster_id,
                        Groups.cluster_id,
                        Scenes.cluster_id,
                        TuyaGarageManufCluster.cluster_id,
                    ],
                    OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
                },
                # <SimpleDescriptor endpoint=242 profile=41440 device_type=97
                # input_clusters=[]
                # output_clusters=[33]
                242: {
                    PROFILE_ID: 41440,
                    DEVICE_TYPE: 97,
                    INPUT_CLUSTERS: [],
                    OUTPUT_CLUSTERS: [GreenPowerProxy.cluster_id],
                },
            },
        }

        replacement = {
            ENDPOINTS: {
                1: {
                    DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,
                    INPUT_CLUSTERS: [
                        Basic.cluster_id,
                        Groups.cluster_id,
                        Scenes.cluster_id,
                        TuyaGarageManufCluster,
                        TuyaOnOffNM,
                    ],
                    OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
                },
                2: {
                    PROFILE_ID: zha.PROFILE_ID,
                    DEVICE_TYPE: zha.DeviceType.IAS_ZONE,
                    INPUT_CLUSTERS: [
                        ContactSwitchCluster
                    ],
                    OUTPUT_CLUSTERS: [],
                },
                242: {
                    PROFILE_ID: 0xA1E0,
                    DEVICE_TYPE: 0x0061,
                    INPUT_CLUSTERS: [],
                    OUTPUT_CLUSTERS: [0x0021],
                },
            },
        }

It appears a light switch and a open/close sensor. He hasn't been able to test it on the garage door yet. If someone has the same model and wish to test it in a real environment maybe we can have some interesting feedback

zdeneksvarc commented 10 months ago

Thank you for the quirk @Danieldiazi

Please remove the ` before and after the code in your post as these characters seem to break the indentation.

Quirk works well, my friend said in return, but try to consider:

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,

for

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,
tengotengo commented 10 months ago

Same issue. Entities don't get created. Tried the .py file as /config/zhaquirks/ts0601_garage.py from the above comment. Including the DEVICE_TYPE: zha.DeviceType.SMART_PLUG instead of ON_OFF_LIGHT change

After .py file changes, I delete the device, restart HA and pair the device again.

Also added

zha:
  custom_quirks_path: /config/zhaquirks

to /config/configuration.yaml

image

My diagnostics file:

{
  "home_assistant": {
    "installation_type": "Home Assistant Supervised",
    "version": "2023.10.3",
    "dev": false,
    "hassio": true,
    "virtualenv": false,
    "python_version": "3.11.5",
    "docker": true,
    "arch": "x86_64",
    "timezone": "America/Chicago",
    "os_name": "Linux",
    "os_version": "5.10.0-21-amd64",
    "supervisor": "2023.10.1",
    "host_os": "Debian GNU/Linux 11 (bullseye)",
    "docker_version": "24.0.6",
    "chassis": "laptop",
    "run_as_root": true
  },
  "custom_components": {
    "govee_lan": {
      "version": "1.0.0",
      "requirements": [
        "govee-led-wez>=0.0.14"
      ]
    },
    "meross_lan": {
      "version": "3.0.3",
      "requirements": []
    },
    "hacs": {
      "version": "1.33.0",
      "requirements": [
        "aiogithubapi>=22.10.1"
      ]
    },
    "govee": {
      "version": "0.2.2",
      "requirements": [
        "govee-api-laggat==0.2.2",
        "dacite==1.6.0"
      ]
    },
    "localtuya": {
      "version": "5.0.0",
      "requirements": []
    }
  },
  "integration_manifest": {
    "domain": "zha",
    "name": "Zigbee Home Automation",
    "after_dependencies": [
      "onboarding",
      "usb"
    ],
    "codeowners": [
      "@dmulcahey",
      "@adminiuga",
      "@puddly"
    ],
    "config_flow": true,
    "dependencies": [
      "file_upload"
    ],
    "documentation": "https://www.home-assistant.io/integrations/zha",
    "iot_class": "local_polling",
    "loggers": [
      "aiosqlite",
      "bellows",
      "crccheck",
      "pure_pcapy3",
      "zhaquirks",
      "zigpy",
      "zigpy_deconz",
      "zigpy_xbee",
      "zigpy_zigate",
      "zigpy_znp",
      "universal_silabs_flasher"
    ],
    "requirements": [
      "bellows==0.36.5",
      "pyserial==3.5",
      "pyserial-asyncio==0.6",
      "zha-quirks==0.0.104",
      "zigpy-deconz==0.21.1",
      "zigpy==0.57.2",
      "zigpy-xbee==0.18.3",
      "zigpy-zigate==0.11.0",
      "zigpy-znp==0.11.5",
      "universal-silabs-flasher==0.0.14",
      "pyserial-asyncio-fast==0.11"
    ],
    "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"
        ]
      }
    ],
    "zeroconf": [
      {
        "type": "_esphomelib._tcp.local.",
        "name": "tube*"
      },
      {
        "type": "_zigate-zigbee-gateway._tcp.local.",
        "name": "*zigate*"
      },
      {
        "type": "_zigstar_gw._tcp.local.",
        "name": "*zigstar*"
      },
      {
        "type": "_uzg-01._tcp.local.",
        "name": "uzg-01*"
      },
      {
        "type": "_slzb-06._tcp.local.",
        "name": "slzb-06*"
      }
    ],
    "is_built_in": true
  },
  "data": {
    "ieee": "**REDACTED**",
    "nwk": 34059,
    "manufacturer": "_TZE204_nklqjk62",
    "model": "TS0601",
    "name": "_TZE204_nklqjk62 TS0601",
    "quirk_applied": false,
    "quirk_class": "zigpy.device.Device",
    "manufacturer_code": 4417,
    "power_source": "Mains",
    "lqi": 186,
    "rssi": null,
    "last_seen": "2023-10-28T21:44:30",
    "available": true,
    "device_type": "Router",
    "signature": {
      "node_descriptor": "NodeDescriptor(logical_type=<LogicalType.Router: 1>, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=<FrequencyBand.Freq2400MHz: 8>, mac_capability_flags=<MACCapabilityFlags.FullFunctionDevice|MainsPowered|RxOnWhenIdle|AllocateAddress: 142>, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, descriptor_capability_field=<DescriptorCapability.NONE: 0>, *allocate_address=True, *is_alternate_pan_coordinator=False, *is_coordinator=False, *is_end_device=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": "0x0051",
          "input_clusters": [
            "0x0000",
            "0x0004",
            "0x0005",
            "0xef00"
          ],
          "output_clusters": [
            "0x000a",
            "0x0019"
          ]
        },
        "242": {
          "profile_id": "0xa1e0",
          "device_type": "0x0061",
          "input_clusters": [],
          "output_clusters": [
            "0x0021"
          ]
        }
      },
      "manufacturer": "_TZE204_nklqjk62",
      "model": "TS0601"
    },
    "active_coordinator": false,
    "entities": [],
    "neighbors": [],
    "routes": [],
    "endpoint_names": [
      {
        "name": "SMART_PLUG"
      },
      {
        "name": "PROXY_BASIC"
      }
    ],
    "user_given_name": null,
    "device_reg_id": "7bdb9111c2242cbfbd1a138aab455d43",
    "area_id": "garage",
    "cluster_details": {
      "1": {
        "device_type": {
          "name": "SMART_PLUG",
          "id": 81
        },
        "profile_id": 260,
        "in_clusters": {
          "0x0004": {
            "endpoint_attribute": "groups",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0005": {
            "endpoint_attribute": "scenes",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0xef00": {
            "endpoint_attribute": null,
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x0000": {
            "endpoint_attribute": "basic",
            "attributes": {
              "0x0001": {
                "attribute_name": "app_version",
                "value": 74
              },
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "_TZE204_nklqjk62"
              },
              "0x0005": {
                "attribute_name": "model",
                "value": "TS0601"
              }
            },
            "unsupported_attributes": {}
          }
        },
        "out_clusters": {
          "0x0019": {
            "endpoint_attribute": "ota",
            "attributes": {},
            "unsupported_attributes": {}
          },
          "0x000a": {
            "endpoint_attribute": "time",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      },
      "242": {
        "device_type": {
          "name": "PROXY_BASIC",
          "id": 97
        },
        "profile_id": 41440,
        "in_clusters": {},
        "out_clusters": {
          "0x0021": {
            "endpoint_attribute": "green_power",
            "attributes": {},
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}
tengotengo commented 10 months ago

Update: script posted here: https://github.com/zigpy/zha-device-handlers/issues/2533
worked!

Danieldiazi commented 10 months ago

Thank you for the quirk @Danieldiazi

Please remove the ` before and after the code in your post as these characters seem to break the indentation.

Quirk works well, my friend said in return, but try to consider:

* Add automation to reset the switch after 1 s, since the event is more like touching a button
id: '...'
alias: Garage Door Switch Reset
description: ''
trigger:
  - platform: state
    entity_id:
      - switch.garage_door_controller_switch
    to: 'on'
    for:
      hours: 0
      minutes: 0
      seconds: 1
condition: []
action:
  - service: switch.turn_off
    data: {}
    target:
      entity_id: switch.garage_door_controller_switch
mode: single
* In the quirk code change
    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.ON_OFF_LIGHT,

for

    replacement = {
        ENDPOINTS: {
            1: {
                DEVICE_TYPE: zha.DeviceType.SMART_PLUG,

I've removed ' before and after the code! Thanks for the suggestions!

zdeneksvarc commented 10 months ago

You are welcome @Danieldiazi. One more point, I like these icons for dashboard garage card:

https://i.imgur.com/giZOHtf.png

zdeneksvarc commented 10 months ago

Same issue. Entities don't get created.

I believe it is the incorrect formatting of the quirk code mentioned above. Python code must start without indentation. HA will point this out in the log when it tries to load the quirk file.

liouma commented 5 months ago

Thanks for the quirk, it's working fine. Turning on the switch activate the physical connection for a second, the it is immediately interrupted. This is probably the expected behaviour for a garage door but not for the device I use. I need a normal on/off switch.

Is this behaviour built in the hardware or is it programmed in the quirk? Do you know if there is a software way to modify it?? Thanks a lot!