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
759 stars 697 forks source link

[Device Support Request] Giex/Tuya QT06 / ts0601 - Modelnr. _TZE200_anv5ujhv #2541

Closed dustinbvh closed 8 months ago

dustinbvh commented 1 year ago

Problem description

There is already a script which works (kind of). The script creates a on/off switch and a battery percentage. The battery only returns 0% (solar+battery powered product) & the switch only 'switches' when u physically push the button on the device. If you use the switch in home assistant, the log updates but the device does nothing.

image

Log for switching within home assistant: <- device does nothing

image

Log for switching by clicking the device button: <- home assistant switch does change state active/inactive

image

Used script (added the modelnumber): https://github.com/zigpy/zha-device-handlers/blob/dev/zhaquirks/tuya/ts0601_valve.py

Solution description

  1. I would like to see the on/off switch working in Home Assistant (for automations).
  2. I hope the battery state can be fixed but this is a nice-to-have.

Screenshots/Video

Screenshots/Video [Paste/upload your media here]

Device signature

Device signature ```json { "node_descriptor": "NodeDescriptor(logical_type=, complex_descriptor_available=0, user_descriptor_available=0, reserved=0, aps_flags=0, frequency_band=, mac_capability_flags=, manufacturer_code=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, 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": "0x0000", "in_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0006", "0x0702", "0xef00" ], "out_clusters": [ "0x000a", "0x0019" ] } }, "manufacturer": "_TZE200_anv5ujhv", "model": "TS0601", "class": "ts0601_valve.GiexValve" } ```

Diagnostic information

Diagnostic information ```json { "home_assistant": { "installation_type": "Home Assistant OS", "version": "2023.4.5", "dev": false, "hassio": true, "virtualenv": false, "python_version": "3.10.10", "docker": true, "arch": "aarch64", "timezone": "Europe/Amsterdam", "os_name": "Linux", "os_version": "6.1.21-v8", "supervisor": "2023.08.1", "host_os": "Home Assistant OS 10.0", "docker_version": "23.0.3", "chassis": "embedded", "run_as_root": true }, "custom_components": {}, "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" ], "requirements": [ "bellows==0.35.1", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.97", "zigpy-deconz==0.20.0", "zigpy==0.54.1", "zigpy-xbee==0.17.0", "zigpy-zigate==0.10.3", "zigpy-znp==0.10.0" ], "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": "_slzb-06._tcp.local.", "name": "slzb-06*" } ], "is_built_in": true }, "data": { "ieee": "**REDACTED**", "nwk": 63795, "manufacturer": "_TZE200_anv5ujhv", "model": "TS0601", "name": "_TZE200_anv5ujhv TS0601", "quirk_applied": true, "quirk_class": "ts0601_valve.GiexValve", "manufacturer_code": 4417, "power_source": "Battery or Unknown", "lqi": 255, "rssi": -64, "last_seen": "2023-08-23T14:19:40", "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=4417, maximum_buffer_size=66, maximum_incoming_transfer_size=66, server_mask=10752, maximum_outgoing_transfer_size=66, 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": "0x0000", "in_clusters": [ "0x0000", "0x0001", "0x0004", "0x0005", "0x0006", "0x0702", "0xef00" ], "out_clusters": [ "0x000a", "0x0019" ] } } }, "active_coordinator": false, "entities": [ { "entity_id": "sensor.tze200_anv5ujhv_ts0601_battery", "name": "_TZE200_anv5ujhv TS0601" }, { "entity_id": "switch.tze200_anv5ujhv_ts0601_switch", "name": "_TZE200_anv5ujhv TS0601" } ], "neighbors": [], "routes": [], "endpoint_names": [ { "name": "ON_OFF_SWITCH" } ], "user_given_name": null, "device_reg_id": "4f75a2ef10f8d6fbf232898de9d43ea2", "area_id": null, "cluster_details": { "1": { "device_type": { "name": "ON_OFF_SWITCH", "id": 0 }, "profile_id": 260, "in_clusters": { "0x0000": { "endpoint_attribute": "basic", "attributes": { "0x0001": { "attribute_name": "app_version", "value": 72 } }, "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": { "0x0000": { "attribute_name": "on_off" }, "0x4003": { "attribute_name": "start_up_on_off" } } }, "0x0001": { "endpoint_attribute": "power", "attributes": { "0x0021": { "attribute_name": "battery_percentage_remaining", "value": 0 } }, "unsupported_attributes": { "0x0020": { "attribute_name": "battery_voltage" }, "0x0021": { "attribute_name": "battery_percentage_remaining" }, "0x0033": { "attribute_name": "battery_quantity" }, "0x0031": { "attribute_name": "battery_size" } } }, "0x0702": { "endpoint_attribute": "smartenergy_metering", "attributes": { "0x0300": { "attribute_name": "unit_of_measure", "value": 7 }, "0x0306": { "attribute_name": "metering_device_type", "value": 2 } }, "unsupported_attributes": { "0x0400": { "attribute_name": "instantaneous_demand" }, "0x0301": { "attribute_name": "multiplier" }, "0x0302": { "attribute_name": "divisor" }, "0x0303": { "attribute_name": "summation_formatting" }, "0x0304": { "attribute_name": "demand_formatting" }, "0x0000": { "attribute_name": "current_summ_delivered" }, "0x0100": { "attribute_name": "current_tier1_summ_delivered" }, "0x0102": { "attribute_name": "current_tier2_summ_delivered" }, "0x0104": { "attribute_name": "current_tier3_summ_delivered" }, "0x0106": { "attribute_name": "current_tier4_summ_delivered" }, "0x0108": { "attribute_name": "current_tier5_summ_delivered" }, "0x010a": { "attribute_name": "current_tier6_summ_delivered" }, "0x0200": { "attribute_name": "status" } } }, "0xef00": { "endpoint_attribute": "tuya_manufacturer", "attributes": { "0xef00": { "attribute_name": "mcu_version", "value": "1.0.0" }, "0xef65": { "attribute_name": "irrigation_start_time", "value": 0 }, "0xef68": { "attribute_name": "irrigation_target", "value": 0 } }, "unsupported_attributes": {} } }, "out_clusters": { "0x000a": { "endpoint_attribute": "time", "attributes": {}, "unsupported_attributes": {} }, "0x0019": { "endpoint_attribute": "ota", "attributes": {}, "unsupported_attributes": {} } } } } } } ```

Logs

Logs ```python _TZE200_anv5ujhv TS0601 Switch is ingeschakeld getriggerd door service switch.turn_on 14:24:12 - 11 minuten geleden - dustinbvh _TZE200_anv5ujhv TS0601 Switch is uitgeschakeld getriggerd door service switch.turn_off 14:24:10 - 11 minuten geleden - dustinbvh _TZE200_anv5ujhv TS0601 Switch is ingeschakeld 14:23:57 - 11 minuten geleden _TZE200_anv5ujhv TS0601 Switch is uitgeschakeld 14:23:44 - 12 minuten geleden _TZE200_anv5ujhv TS0601 Switch is ingeschakeld getriggerd door service switch.turn_off 14:23:34 - 12 minuten geleden - dustinbvh _TZE200_anv5ujhv TS0601 Switch is uitgeschakeld getriggerd door service switch.turn_off 14:23:30 - 12 minuten geleden - dustinbvh _TZE200_anv5ujhv TS0601 Switch is ingeschakeld getriggerd door service switch.turn_on ```

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 import foundation 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, TuyaOnOffNM, TuyaPowerConfigurationCluster, ) class TuyaValveWaterConsumed(Metering, TuyaLocalCluster): """Tuya Valve Water consumed cluster.""" VOLUME_LITERS = 0x0007 WATER_METERING = 0x02 """Setting unit of measurement.""" _CONSTANT_ATTRIBUTES = { 0x0300: VOLUME_LITERS, 0x0306: WATER_METERING, } 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_anv5ujhv", "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, TuyaOnOffNM, TuyaValveWaterConsumed, TuyaPowerConfigurationCluster, 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_anv5ujhv", "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, TuyaOnOffNM, TuyaPowerConfigurationCluster, ParksideTuyaValveManufCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], }, }, } # info from https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/devices/giex.js GIEX_MODE_ATTR = 0xEF01 # Mode [0] duration [1] capacity GIEX_START_TIME_ATTR = 0xEF65 # Last irrigation start time (GMT) GIEX_END_TIME_ATTR = 0xEF66 # Last irrigation end time (GMT) GIEX_NUM_TIMES_ATTR = 0xEF67 # Number of cycle irrigation times min=0 max=100 GIEX_TARGET_ATTR = 0xEF68 # Irrigation target, duration in seconds or capacity in litres (depending on mode) min=0 max=3600 GIEX_INTERVAL_ATTR = 0xEF69 # Cycle irrigation interval in seconds min=0 max=3600 GIEX_DURATION_ATTR = 0xEF72 # Last irrigation duration class GiexValveManufCluster(TuyaMCUCluster): """GiEX valve manufacturer cluster.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update( { GIEX_MODE_ATTR: ("irrigation_mode", t.Bool, True), GIEX_START_TIME_ATTR: ("irrigation_start_time", t.uint32_t, True), GIEX_END_TIME_ATTR: ("irrigation_end_time", t.uint32_t, True), GIEX_NUM_TIMES_ATTR: ("irrigation_num_times", t.uint32_t, True), GIEX_TARGET_ATTR: ("irrigation_target", t.uint32_t, True), GIEX_INTERVAL_ATTR: ("irrigation_interval", t.uint32_t, True), GIEX_DURATION_ATTR: ("irrigation_duration", t.uint32_t, True), } ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { 1: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_mode", ), 2: DPToAttributeMapping( TuyaOnOffNM.ep_attribute, "on_off", ), 101: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_start_time", ), 102: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_end_time", ), 103: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_num_times", ), 104: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_target", ), 105: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_interval", ), 108: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", ), 111: DPToAttributeMapping( TuyaValveWaterConsumed.ep_attribute, "current_summ_delivered", ), 114: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "irrigation_duration", ), } data_point_handlers = { 1: "_dp_2_attr_update", 2: "_dp_2_attr_update", 101: "_dp_2_attr_update", 102: "_dp_2_attr_update", 103: "_dp_2_attr_update", 104: "_dp_2_attr_update", 105: "_dp_2_attr_update", 108: "_dp_2_attr_update", 111: "_dp_2_attr_update", 114: "_dp_2_attr_update", } async def write_attributes(self, attributes, manufacturer=None): """Overwrite to force manufacturer code.""" return await super().write_attributes( attributes, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID ) class GiexValve(CustomDevice): """GiEX valve device.""" signature = { MODELS_INFO: [("_TZE200_anv5ujhv", "TS0601")], ENDPOINTS: { # 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, GiexValveManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], } }, } replacement = { ENDPOINTS: { 1: { DEVICE_TYPE: zha.DeviceType.ON_OFF_SWITCH, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, TuyaOnOff, TuyaOnOffNM, TuyaPowerConfigurationCluster, TuyaValveWaterConsumed, GiexValveManufCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], } } } ```

Additional information

I have tried multiple instances (found online) but this is the only one with actual visual results.

danielcaceresm commented 1 year ago

I'm using this custom quirk that I wrote:

Custom quirk ```python """Collection of Tuya Valve devices e.g. water valves, gas valve etc.""" from typing import Dict, Optional from zigpy.profiles import zha from zigpy.quirks import CustomDevice import zigpy.types as t from zigpy.zcl import foundation from zigpy.zcl.clusters.general import Basic, Groups, Ota, Scenes, Time from zhaquirks.const import ( DEVICE_TYPE, ENDPOINTS, INPUT_CLUSTERS, MODELS_INFO, OUTPUT_CLUSTERS, PROFILE_ID, ) from zhaquirks.tuya import ( TuyaDatapointData, TuyaCommand ) from zhaquirks.tuya.mcu import ( DPToAttributeMapping, TuyaClusterData, TuyaMCUCluster, TuyaOnOffNM, TuyaPowerConfigurationCluster, ) # info from https://github.com/Koenkk/zigbee-herdsman-converters/blob/master/src/devices/qoto.ts QOTO_VALVE_STATE_AUTO_SHUTDOWN_ATTR = 0xEF06 # Set valve to % with auto shutdown. QOTO_WATER_FLOW_ATTR = 0xEF65 # Current water flow in %. QOTO_SHUTDOWN_TIMER_ATTR = 0xEF66 # Auto shutdown in seconds. QOTO_REMAINING_WATERING_TIME_ATTR = 0xEF67 # Remaning watering time (for auto shutdown). Updates every minute, and every 10s in the last minute. QOTO_VALVE_STATE_ATTR = 0xEF68 # Set valve to %. QOTO_LAST_WATERING_DURATION_ATTR = 0xEF69 # Last watering duration in seconds. class QotoValveManufCluster(TuyaMCUCluster): """QOTO valve manufacturer cluster.""" attributes = TuyaMCUCluster.attributes.copy() attributes.update( { #QOTO_VALVE_STATE_AUTO_SHUTDOWN_ATTR: ("valve_state_auto_shutdown", t.uint32_t, True), QOTO_WATER_FLOW_ATTR: ("water_flow", t.uint32_t, True), QOTO_SHUTDOWN_TIMER_ATTR: ("shutdown_timer", t.uint32_t, True), QOTO_REMAINING_WATERING_TIME_ATTR: ("remaining_watering_time", t.uint32_t, True), QOTO_VALVE_STATE_ATTR: ("valve_state", t.uint32_t, True), QOTO_LAST_WATERING_DURATION_ATTR: ("last_watering_duration", t.uint32_t, True), } ) dp_to_attribute: Dict[int, DPToAttributeMapping] = { #2: DPToAttributeMapping( # TuyaMCUCluster.ep_attribute, # "valve_state_auto_shutdown", #), 2: DPToAttributeMapping( TuyaOnOffNM.ep_attribute, "on_off", converter = lambda x: True if x > 0 else False, dp_converter = lambda x: 100 if x else 0, ), 3: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "water_flow", ), 11: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "shutdown_timer", ), 101: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "remaining_watering_time", ), 102: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "valve_state", ), 107: DPToAttributeMapping( TuyaMCUCluster.ep_attribute, "last_watering_duration", ), 110: DPToAttributeMapping( TuyaPowerConfigurationCluster.ep_attribute, "battery_percentage_remaining", ), } data_point_handlers = { 2: "_dp_2_attr_update", 3: "_dp_2_attr_update", 11: "_dp_2_attr_update", 101: "_dp_2_attr_update", 102: "_dp_2_attr_update", 107: "_dp_2_attr_update", 110: "_dp_2_attr_update", } async def write_attributes(self, attributes, manufacturer=None): """Overwrite to force manufacturer code.""" return await super().write_attributes( attributes, manufacturer=foundation.ZCLHeader.NO_MANUFACTURER_ID ) def from_cluster_data(self, data: TuyaClusterData) -> Optional[TuyaCommand]: """Adds shutdown_timer datapoint when setting valve_state_auto_shutdown.""" result = super().from_cluster_data(data) for cmd_payload in result: if (len(list(filter (lambda x : x.dp == 2, cmd_payload.datapoints))) > 0): cmd_payload.datapoints.append(TuyaDatapointData(11, self.get(QOTO_SHUTDOWN_TIMER_ATTR))) return result class QotoValve(CustomDevice): """QOTO valve device.""" signature = { MODELS_INFO: [("_TZE200_anv5ujhv", "TS0601")], ENDPOINTS: { 1: { PROFILE_ID: zha.PROFILE_ID, DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Basic.cluster_id, Groups.cluster_id, Scenes.cluster_id, QotoValveManufCluster.cluster_id, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], } }, } replacement = { ENDPOINTS: { 1: { DEVICE_TYPE: zha.DeviceType.SMART_PLUG, INPUT_CLUSTERS: [ Groups.cluster_id, Scenes.cluster_id, TuyaPowerConfigurationCluster, TuyaOnOffNM, QotoValveManufCluster, ], OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id], } } } ```

Notes:

github-actions[bot] commented 8 months ago

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates. Please make sure to update to the latest version and check if that solves the issue. Let us know if that works for you by adding a comment 👍 This issue has now been marked as stale and will be closed if no further activity occurs. Thank you for your contributions.