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
721 stars 670 forks source link

[Device Support Request] TS0601 by _TZE204_xlppj4f5 (Immax NEO Smart irrigation valve with solar panel) #3321

Open jindrab01 opened 3 weeks ago

jindrab01 commented 3 weeks ago

Problem description

This device is not supported ZHA by default. Tried to implement my own quirk based on TUYA water valves and despite i managed quirk to be loaded and assigned to the valve, the switch turning on/off valve does not react. Thanks for the support

Solution description

I would like to get On/OFF switch working. Attached is my modified quirk based on ts0601_valve.py which is loading, but no HA control.

Screenshots/Video

valve picture

Device signature

Device signature ```json { "node_descriptor": { "logical_type": 2, "complex_descriptor_available": 0, "user_descriptor_available": 0, "reserved": 0, "aps_flags": 0, "frequency_band": 8, "mac_capability_flags": 128, "manufacturer_code": 4417, "maximum_buffer_size": 66, "maximum_incoming_transfer_size": 66, "server_mask": 10752, "maximum_outgoing_transfer_size": 66, "descriptor_capability_field": 0 }, "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0051", "input_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0006", "0x0702", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE204_xlppj4f5", "model": "TS0601", "class": "ts0601_valve.TuyaValve" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2024.8.3", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.12.4", "docker": true, "arch": "aarch64", "timezone": "Europe/Prague", "os_name": "Linux", "os_version": "6.6.31-haos-raspi", "supervisor": "2024.08.0", "host_os": "Home Assistant OS 13.1", "docker_version": "26.1.4", "chassis": "embedded", "run_as_root": true }, "custom_components": { "jablotron100": { "documentation": "https://github.com/kukulich/home-assistant-jablotron100", "version": "3.26.0", "requirements": [] }, "zha_toolkit": { "documentation": "https://github.com/mdeweerd/zha-toolkit", "version": "v1.1.19", "requirements": [ "aiofiles>=0.4.0", "pytz>=2016.10" ] }, "philips_android_tv": { "documentation": "https://github.com/nstrelow/ha_philips_2016", "version": "1.0.0", "requirements": [ "wakeonlan" ] }, "hacs": { "documentation": "https://hacs.xyz/docs/configuration/start", "version": "2.0.0", "requirements": [ "aiogithubapi>=22.10.1" ] } }, "integration_manifest": { "domain": "zha", "name": "Zigbee Home Automation", "after_dependencies": [ "onboarding", "usb" ], "codeowners": [ "dmulcahey", "adminiuga", "puddly", "TheJulianJES" ], "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", "zha", "universal_silabs_flasher" ], "requirements": [ "universal-silabs-flasher==0.0.22", "zha==0.0.31" ], "usb": [ { "vid": "10C4", "pid": "EA60", "description": "*2652*", "known_devices": [ "slae.sh cc2652rb stick" ] }, { "vid": "10C4", "pid": "EA60", "description": "*slzb-07*", "known_devices": [ "smlight slzb-07" ] }, { "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": "0403", "pid": "6015", "description": "*conbee*", "known_devices": [ "Conbee III" ] }, { "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*" }, { "type": "_xzg._tcp.local.", "name": "xzg*" }, { "type": "_czc._tcp.local.", "name": "czc*" } ], "is_built_in": true }, "setup_times": { "null": { "setup": 0.00017116498202085495 }, "0fe5eccf2300d9792b540578b2ca23d3": { "wait_import_platforms": -0.044172860973048955, "wait_base_component": -0.00234933098545298, "config_entry_setup": 13.990707289951388 } }, "data": { "ieee": "**REDACTED**", "nwk": 4279, "manufacturer": "_TZE204_xlppj4f5", "model": "TS0601", "name": "_TZE204_xlppj4f5 TS0601", "quirk_applied": true, "quirk_class": "ts0601_valve.TuyaValve", "quirk_id": null, "manufacturer_code": 4417, "power_source": "Battery or Unknown", "lqi": 188, "rssi": -53, "last_seen": "2024-08-26T22:27:43", "available": true, "device_type": "EndDevice", "signature": { "node_descriptor": { "logical_type": 2, "complex_descriptor_available": 0, "user_descriptor_available": 0, "reserved": 0, "aps_flags": 0, "frequency_band": 8, "mac_capability_flags": 128, "manufacturer_code": 4417, "maximum_buffer_size": 66, "maximum_incoming_transfer_size": 66, "server_mask": 10752, "maximum_outgoing_transfer_size": 66, "descriptor_capability_field": 0 }, "endpoints": { "1": { "profile_id": "0x0104", "device_type": "0x0051", "input_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0006", "0x0702", "0xef00" ], "output_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE204_xlppj4f5", "model": "TS0601" }, "active_coordinator": false, "entities": [ { "entity_id": "sensor.tze204_xlppj4f5_ts0601_battery", "name": "_TZE204_xlppj4f5 TS0601" }, { "entity_id": "sensor.tze204_xlppj4f5_ts0601_rssi", "name": "_TZE204_xlppj4f5 TS0601" }, { "entity_id": "sensor.tze204_xlppj4f5_ts0601_lqi", "name": "_TZE204_xlppj4f5 TS0601" }, { "entity_id": "sensor.tze204_xlppj4f5_ts0601_instantaneous_demand", "name": "_TZE204_xlppj4f5 TS0601" }, { "entity_id": "sensor.tze204_xlppj4f5_ts0601_summation_delivered", "name": "_TZE204_xlppj4f5 TS0601" }, { "entity_id": "switch.tze204_xlppj4f5_ts0601_switch", "name": "_TZE204_xlppj4f5 TS0601" }, { "entity_id": "update.tze204_xlppj4f5_ts0601_firmware", "name": "_TZE204_xlppj4f5 TS0601" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "SMART_PLUG" } ], "user_given_name": null, "device_reg_id": "e5db265275e8184a6974cc6628c68b11", "area_id": "garden", "cluster_details": { "1": { "device_type": { "name": "SMART_PLUG", "id": 81 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 74 }, "0x0004": { "attribute_name": "manufacturer", "value": "_TZE204_xlppj4f5" }, "0x0005": { "attribute_name": "model", "value": "TS0601" } }, "unsupported_attributes": {} }, "0x0004": { "endpoint_attribute": "groups", "attributes": {}, "unsupported_attributes": {} }, "0x0005": { "endpoint_attribute": "scenes", "attributes": {}, "unsupported_attributes": {} }, "0x0006": { "endpoint_attribute": "on_off", "attributes": { "0x0000": { "attribute_name": "on_off", "value": false } }, "unsupported_attributes": {} }, "0x0702": { "endpoint_attribute": "smartenergy_metering", "attributes": { "0x0300": { "attribute_name": "unit_of_measure", "value": 7 } }, "unsupported_attributes": {} }, "0x0001": { "endpoint_attribute": "power", "attributes": {}, "unsupported_attributes": {} }, "0xef00": { "endpoint_attribute": "tuya_manufacturer", "attributes": { "0xef00": { "attribute_name": "mcu_version", "value": "1.0.0" } }, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": { "0x0002": { "attribute_name": "current_file_version", "value": 74 } }, "unsupported_attributes": {} } } } } } } ```

Logs

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

Custom quirk

Custom quirk ```python """Collection of Tuya Valve devices e.g. water valves, gas valve etc.""" 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, Groups, Identify, OnOff, Ota, Scenes, Time from zigpy.zcl.clusters.smartenergy import Metering from zhaquirks import DoublingPowerConfigurationCluster 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 ( DPToAttributeMapping, EnchantedDevice, TuyaMCUCluster, TuyaOnOff, TuyaPowerConfigurationCluster, ) class TuyaValveWaterConsumed(Metering, TuyaLocalCluster): """Tuya Valve Water consumed cluster.""" VOLUME_LITERS = 0x0007 """Setting unit of measurement.""" _CONSTANT_ATTRIBUTES = {0x0300: VOLUME_LITERS} class TuyaValveManufCluster(TuyaMCUCluster): """On/Off Tuya cluster with extra device attributes.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update( { 0xEF01: ("time_left", t.uint32_t, True), 0xEF02: ("state", t.enum8, True), 0xEF03: ("last_valve_open_duration", t.uint32_t, True), 0xEF04: ("dp_6", t.uint32_t, True), } ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaOnOff.ep_attribute, "on_off", ), 5: DPToAttributeMapping( TuyaValveWaterConsumed.ep_attribute, "current_summ_delivered", ), 6: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "dp_6", ), 7: DPToAttributeMapping( DoublingPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", ), 11: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "time_left", ), 12: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "state", ), 15: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "last_valve_open_duration", ), } data_point_handlers = { 1: "_dp_2_attr_update", 5: "_dp_2_attr_update", 6: "_dp_2_attr_update", 7: "_dp_2_attr_update", 11: "_dp_2_attr_update", 12: "_dp_2_attr_update", 15: "_dp_2_attr_update", } class TuyaValve(CustomDevice): """Tuya valve device.""" signature = { MODELS_INFO: [("_TZE200_81isopgh", "TS0601"),("_TZE204_xlppj4f5", "TS0601")], # SizePrefixedSimpleDescriptor(endpoint=1, profile=260, device_type=81, device_version=1, # input_clusters=[0, 4, 5, 61184], output_clusters=[25, 10]) ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaValveManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], } }, } replacement = { ENDPOINTS: { 1: { DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaOnOff, TuyaValveWaterConsumed, DoublingPowerConfigurationCluster, TuyaValveManufCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], } } } class ParksideTuyaValveManufCluster(TuyaMCUCluster): """Manufacturer Specific Cluster for the _TZE200_htnnfasr water valve sold as PARKSIDE.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update( { 0xEF11: ("timer_duration", t.uint32_t, True), 0xEF12: ("timer_time_left", t.uint32_t, True), 0xEF13: ("frost_lock", t.Bool, True), 0xEF14: ("frost_lock_reset", t.Bool, True), # 0 resets frost lock } ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaOnOff.ep_attribute, "on_off", ), 5: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "timer_duration", ), 6: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "timer_time_left", ), 11: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", ), 108: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "frost_lock", lambda x: not x, # invert for lock entity ), 109: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "frost_lock_reset", ), } data_point_handlers = { 1: "_dp_2_attr_update", 5: "_dp_2_attr_update", 6: "_dp_2_attr_update", 11: "_dp_2_attr_update", 108: "_dp_2_attr_update", 109: "_dp_2_attr_update", } async def bind(self): """ Bind cluster. When adding this device tuya gateway issues factory reset, we just need to reset the frost lock, because its state is unknown to us. """ result = await super().bind() await self.write_attributes({self.attributes_by_name["frost_lock_reset"].id: 0}) return result class ParksidePSBZS(EnchantedDevice): """LIDL Parkside water without implemented scheduler.""" signature = { MODELS_INFO: [("_TZE200_htnnfasr", "TS0601")], # HG06875 ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, OnOff.cluster_id, ParksideTuyaValveManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, }, } replacement = { ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ Basic.cluster_id, Identify.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaOnOff, TuyaPowerConfigurationCluster, ParksideTuyaValveManufCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, }, } ```

Additional information

No response

deb0ro commented 2 weeks ago

Hi, I have the same valve! 😄 I also pair it with HA using custom quirk found here https://github.com/zigpy/zha-device-handlers/blob/dev/zhaquirks/tuya/ts0601_valve.py. For me the switch in UI react when I press the button on the valve - it correctly detect the state. But not working other way around, from HA 😞

When I bought it few reviews on the shop said its working fine with HA, only one mentioned its not working. Maybe they have been using Z2M?