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

Curtain Switch TS130F Modell SM-SW101-CZ no quirk loaded, no calibration, no Motor inversion. #2623

Closed jopeyyyy closed 1 year ago

jopeyyyy commented 1 year ago

Problem description

no quirk loaded, calibration attribute missing, no calibration possible, reverse output with HA command (physicaly OK)

Solution description

correct quirk to load?

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device 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": "0x0202", "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0x0102" ], "output_clusters": [ "0x000a" ] } }, "manufacturer": "_TZ3000_ltiqubue", "model": "TS130F", "class": "zigpy.device.Device" }

Diagnostic information

{ "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.9.3", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.11.5", "docker": true, "arch": "x86_64", "timezone": "Europe/Paris", "os_name": "Linux", "os_version": "6.1.45", "supervisor": "2023.10.0", "host_os": "Home Assistant OS 10.5", "docker_version": "23.0.6", "chassis": "embedded", "run_as_root": true }, "custom_components": { "zha_toolkit": { "version": "v1.0.0", "requirements": [ "pytz" ] }, "localtuya": { "version": "5.2.1", "requirements": [] }, "dreame_vacuum": { "version": "v2.0.0b6", "requirements": [ "pillow", "numpy", "pybase64", "requests", "pycryptodome", "python-miio", "py-mini-racer", "tzlocal" ] }, "hacs": { "version": "1.33.0", "requirements": [ "aiogithubapi>=22.10.1" ] } }, "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.4", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.103", "zigpy-deconz==0.21.1", "zigpy==0.57.1", "zigpy-xbee==0.18.2", "zigpy-zigate==0.11.0", "zigpy-znp==0.11.4", "universal-silabs-flasher==0.0.14" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "2652", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "1A86", "pid": "55D4", "description": "sonoffplus", "known_devices": [ "sonoff zigbee dongle plus v2" ] }, { "vid": "10C4", "pid": "EA60", "description": "sonoffplus", "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": 38566, "manufacturer": "_TZ3000_ltiqubue", "model": "TS130F", "name": "_TZ3000_ltiqubue TS130F", "quirk_applied": false, "quirk_class": "zigpy.device.Device", "manufacturer_code": 4417, "power_source": "Mains", "lqi": 105, "rssi": null, "last_seen": "2023-10-04T08:36:09", "available": false, "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": "0x0202", "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0x0102" ], "output_clusters": [ "0x000a" ] } }, "manufacturer": "_TZ3000_ltiqubue", "model": "TS130F" }, "active_coordinator": false, "entities": [ { "entity_id": "button.tz3000_ltiqubue_ts130f_identify_2", "name": "_TZ3000_ltiqubue TS130F" }, { "entity_id": "cover.tz3000_ltiqubue_ts130f_cover_2", "name": "_TZ3000_ltiqubue TS130F" }, { "entity_id": "switch.tz3000_ltiqubue_ts130f_switch_2", "name": "_TZ3000_ltiqubue TS130F" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "WINDOW_COVERING_DEVICE" } ], "user_given_name": null, "device_reg_id": "30360e6233b981192e7ce8f839270c9e", "area_id": "buro", "cluster_details": { "1": { "device_type": { "name": "WINDOW_COVERING_DEVICE", "id": 514 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0004": { "attribute_name": "manufacturer", "value": "_TZ3000_ltiqubue" }, "0x0005": { "attribute_name": "model", "value": "TS130F" } }, "unsupported_attributes": {} }, "0x0003": { "endpoint_attribute": "identify", "attributes": {}, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": {}, "unsupported_attributes": { "0x0000": { "attribute_name": "on_off" }, "0x4003": { "attribute_name": "start_up_on_off" } } }, "0x0102": { "endpoint_attribute": "window_covering", "attributes": { "0x0008": { "attribute_name": "current_position_lift_percentage", "value": 0 } }, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} } } } } } }

Logs

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

Custom quirk

"""Device handler for loratap TS130F smart curtain switch.""" from zigpy.profiles import zha from zigpy.quirks import CustomCluster, CustomDevice import zigpy.types as t from zigpy.zcl.clusters.closures import WindowCovering from zigpy.zcl.clusters.general import Basic, Groups, OnOff, Ota, Scenes, Time

from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, )

ATTR_CURRENT_POSITION_LIFT_PERCENTAGE = 0x0008 CMD_GO_TO_LIFT_PERCENTAGE = 0x0005

class TuyaWithBacklightOnOffCluster(CustomCluster): """TuyaSmartCurtainOnOffCluster: fire events corresponding to press type."""

cluster_id = OnOff.cluster_id

LIGHT_MODE_1 = {0x8001: 0}
LIGHT_MODE_2 = {0x8001: 1}
LIGHT_MODE_3 = {0x8001: 2}

attributes = {0x8001: ("backlight_mode", t.enum8)}

class TuyaCoveringCluster(CustomCluster, WindowCovering): """TuyaSmartCurtainWindowCoveringCluster: Allow to setup Window covering tuya devices."""

attributes = WindowCovering.attributes.copy()
attributes.update({0xF000: ("tuya_moving_state", t.enum8)})
attributes.update({0xF001: ("calibration", t.enum8)})
attributes.update({0xF002: ("motor_reversal", t.enum8)})

def _update_attribute(self, attrid, value):
    if attrid == ATTR_CURRENT_POSITION_LIFT_PERCENTAGE:
        # Invert the percentage value (cf https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757)
        value = 100 - value
    super()._update_attribute(attrid, value)

async def command(
    self, command_id, *args, manufacturer=None, expect_reply=True, tsn=None
):
    """Override default command to invert percent lift value."""
    if command_id == CMD_GO_TO_LIFT_PERCENTAGE:
        percent = args[0]
        # Invert the percentage value (cf https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757)
        percent = 100 - percent
        v = (percent,)
        return await super().command(command_id, *v)
    return await super().command(
        command_id,
        *args,
        manufacturer=manufacturer,
        expect_reply=expect_reply,
        tsn=tsn
    )

class TuyaTS130F(CustomDevice): """Tuya smart curtain roller shutter."""

signature = {
    # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0, 4, 5, 6, 10, 0x0102], output_clusters=[25]))
    MODELS_INFO: [
        ("_TZ3000_8kzqqzu4", "TS130F"),
        ("_TZ3000_egq7y6pr", "TS130F"),
    ],
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                Time.cluster_id,
                OnOff.cluster_id,
                WindowCovering.cluster_id,
            ],
            OUTPUT_CLUSTERS: [Ota.cluster_id],
        },
    },
}
replacement = {
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                Time.cluster_id,
                TuyaWithBacklightOnOffCluster,
                TuyaCoveringCluster,
            ],
            OUTPUT_CLUSTERS: [Ota.cluster_id],
        },
    },
}

class TuyaZemismartTS130F(CustomDevice): """Tuya ZemiSmart smart curtain roller shutter."""

signature = {
    # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0x0000, 0x0004, 0x0005, 0x0006, 0x0102], output_clusters=[0x000a, 0x0019]))
    MODELS_INFO: [("_TZ3000_ltiqubue", "TS130F")],
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                OnOff.cluster_id,
                WindowCovering.cluster_id,
            ],
            OUTPUT_CLUSTERS: [
                Time.cluster_id,
                Ota.cluster_id,
            ],
        },
    },
}
replacement = {
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                TuyaWithBacklightOnOffCluster,
                TuyaCoveringCluster,
            ],
            OUTPUT_CLUSTERS: [
                Time.cluster_id,
                Ota.cluster_id,
            ],
        },
    },
}

class TuyaTS130F_Module(CustomDevice): """Tuya smart curtain roller shutter."""

signature = {
    # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=0x0202, device_version=1, input_clusters=[0, 4, 5, 6, 10, 0x0102], output_clusters=[25]))
    MODELS_INFO: [("_TZ3000_vd43bbfq", "TS130F")],
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                WindowCovering.cluster_id,
            ],
            OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
        },
    },
}
replacement = {
    ENDPOINTS: {
        1: {
            PROFILE_ID: zha.PROFILE_ID,
            DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE,
            INPUT_CLUSTERS: [
                Basic.cluster_id,
                Groups.cluster_id,
                Scenes.cluster_id,
                TuyaCoveringCluster,
            ],
            OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
        },
    },
}

Additional information

switches bought on amazon: https://www.amazon.de/dp/B0CCXZ96Z7/ref=pe_27091401_487027711_TE_SCE_dp_2

ZHA Toolkit installed and activated, custom quirk file and directory added to configuration.yaml

´´´ zha_toolkit:

zha: enable_quirks: true custom_quirks_path: /config/zha_quirks/ ´´´

I'm new to Home Assistant and this is one of my first devices ... Thanks for your help!

javicalle commented 1 year ago

Remove any local quirk for that device and try this one:

TuyaTS130F_var10 ```python """Device handler TS130F smart curtain switch.""" from zigpy.profiles import zgp, zha from zigpy.quirks import CustomCluster, CustomDevice import zigpy.types as t from zigpy.zcl.clusters.closures import WindowCovering from zigpy.zcl.clusters.general import ( Basic, GreenPowerProxy, Groups, Identify, OnOff, Ota, Scenes, Time, ) from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODEL, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import SwitchBackLight, TuyaZBExternalSwitchTypeCluster ATTR_CURRENT_POSITION_LIFT_PERCENTAGE = 0x0008 CMD_GO_TO_LIFT_PERCENTAGE = 0x0005 class TuyaWithBacklightOnOffCluster(CustomCluster, OnOff): """Tuya Zigbee On Off cluster with extra attributes.""" attributes = OnOff.attributes.copy() attributes.update({0x8001: ("backlight_mode", SwitchBackLight)}) class MotorMode(t.enum8): """Tuya motor mode enum.""" STRONG_MOTOR = 0x00 WEAK_MOTOR = 0x01 class TuyaCoveringCluster(CustomCluster, WindowCovering): """TuyaSmartCurtainWindowCoveringCluster: Allow to setup Window covering tuya devices.""" attributes = WindowCovering.attributes.copy() attributes.update({0x8000: ("motor_mode", MotorMode)}) attributes.update({0xF000: ("tuya_moving_state", t.enum8)}) attributes.update({0xF001: ("calibration", t.enum8)}) attributes.update({0xF002: ("motor_reversal", t.enum8)}) attributes.update({0xF003: ("calibration_time", t.uint16_t)}) def _update_attribute(self, attrid, value): if attrid == ATTR_CURRENT_POSITION_LIFT_PERCENTAGE: # Invert the percentage value (cf https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757) value = 100 - value super()._update_attribute(attrid, value) async def command( self, command_id, *args, manufacturer=None, expect_reply=True, tsn=None ): """Override default command to invert percent lift value.""" if command_id == CMD_GO_TO_LIFT_PERCENTAGE: percent = args[0] # Invert the percentage value (cf https://github.com/dresden-elektronik/deconz-rest-plugin/issues/3757) percent = 100 - percent v = (percent,) return await super().command(command_id, *v) return await super().command( command_id, *args, manufacturer=manufacturer, expect_reply=expect_reply, tsn=tsn, ) class TuyaTS130F_var10(CustomDevice): """Tuya smart curtain roller shutter Time Out (variation 10).""" signature = { MODEL: "TS130F", ENDPOINTS: { 1: { # "profile_id": "0x0104", "device_type": "0x0202", # "input_clusters": [ "0x0000", "0x0003", "0x0004", "0x0005", "0x0006", "0x0102" ], # "output_clusters": [ "0x000a" ] PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, WindowCovering.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.WINDOW_COVERING_DEVICE, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaCoveringCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id], }, }, } ```
jopeyyyy commented 1 year ago

Thanks for the feedback!

So I removed everything in my TS130F.py file and replaced it by that what you gave me. The result is unchanged, no quirk loaded.

I'm wondering if everything is correctly configured in my Home Assistant so the ZHA Toolkit and Custom quirks can even be loaded as it should? Once again, I'm a complete newbie to Home Assistant...

javicalle commented 1 year ago

After changing and saving the file you need to restart HA. Then remove the device, wait a minuto or so, and pair the device again.

Check for any error in your logs, on HA start up or while pairing the device.

You can enable the debug logs from the HA integration view and try to pair the device. After pairing, stop the debug logs from HA and attach the logs here.

MattWestb commented 1 year ago

ZHA Toolkit is very likely (i have 3 loading it and 3 systems not) broke but its reported to the devevs but customer quirks is working well if its OK made.

jopeyyyy commented 1 year ago

OK I made that (restart, removing, pairing again), I didn't wait a minute after removing the device....

Now I removed, restarted HA, waited 2 min and paired again. Here is the log:

HA logfile.txt

Can you find something wrong regarding to the switch/quirk?

jopeyyyy commented 1 year ago

After changing and saving the file you need to restart HA. Then remove the device, wait a minuto or so, and pair the device again.

Check for any error in your logs, on HA start up or while pairing the device.

You can enable the debug logs from the HA integration view and try to pair the device. After pairing, stop the debug logs from HA and attach the logs here.

Could you find something in my log?

jopeyyyy commented 1 year ago

Hi, I need to send the switched back if I don't get them working properly in time...

Would be fine if someone could help me, or give me feedback that it will not work , or if more Information are needed.

Thanks guys.

jopeyyyy commented 1 year ago

Hello, just wanted to inform that I bought another curtain switch (looks the same but with curtain pictures as button). That version has been identified by a Quirk directly and works properly. Calibration was possible!

As I sent the other version back, I cannot test anymore and close that issue.