Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Device Support Request] Add Tuya Giex GX02 water valve support TS0601 _TZE200_7ytb3h8u #3209

Closed
dethegeek opened this issue Jun 16, 2024 · 11 comments · Fixed by #3394
Closed
Labels
Tuya Request/PR regarding a Tuya device

Comments

@dethegeek
Copy link

dethegeek commented Jun 16, 2024

Problem description

Similar device aded to solve the following issue #3124

My device is detected as TS0601 _TZE200_7ytb3h8u

Bought here: https://fr.aliexpress.com/item/1005006548852233.html

Solution description

I succesfully aded the device by adding the above model in

MODELS_INFO: [("_TZE200_sh1btabb", "TS0601"), ("_TZE200_a7sghmms", "TS0601"), ( "_TZE204_7ytb3h8u", "TS0601")],

I'm rather new with domotic and Home Assistant, I'm not skilled enough to get a zigbee to MQTT dump for now, then I did a try after roughly understanding how the ZHA quirks works.

Screenshots/Video

Screenshots/Video

[Paste/upload your media here]

Device signature

Device 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=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=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": "0x0104",
      "device_type": "0x0000",
      "input_clusters": [
        "0x0000",
        "0x0001",
        "0x0004",
        "0x0005",
        "0x0006",
        "0x0702",
        "0xef00"
      ],
      "output_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZE200_7ytb3h8u",
  "model": "TS0601",
  "class": "ts0601_valve.GiexValve"
}

Diagnostic information

Diagnostic information
{
  "home_assistant": {
    "installation_type": "Home Assistant Container",
    "version": "2024.6.2",
    "dev": false,
    "hassio": false,
    "virtualenv": false,
    "python_version": "3.12.2",
    "docker": true,
    "arch": "aarch64",
    "timezone": "Europe/Paris",
    "os_name": "Linux",
    "os_version": "6.2.0-1018-raspi",
    "run_as_root": true
  },
  "custom_components": {},
  "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",
      "universal_silabs_flasher"
    ],
    "requirements": [
      "bellows==0.39.0",
      "pyserial==3.5",
      "zha-quirks==0.0.116",
      "zigpy-deconz==0.23.1",
      "zigpy==0.64.0",
      "zigpy-xbee==0.20.1",
      "zigpy-zigate==0.12.0",
      "zigpy-znp==0.12.1",
      "universal-silabs-flasher==0.0.20",
      "pyserial-asyncio-fast==0.11"
    ],
    "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*"
      }
    ],
    "is_built_in": true
  },
  "setup_times": {
    "null": {
      "setup": 0.00021338602527976036
    },
    "0ffe77e8ceb946efe67b9ae06341980c": {
      "wait_import_platforms": -0.0004412168636918068,
      "wait_base_component": -0.001689257100224495,
      "config_entry_setup": 13.71825297595933
    }
  },
  "data": {
    "ieee": "**REDACTED**",
    "nwk": 47845,
    "manufacturer": "_TZE200_7ytb3h8u",
    "model": "TS0601",
    "name": "_TZE200_7ytb3h8u TS0601",
    "quirk_applied": true,
    "quirk_class": "ts0601_valve.GiexValve",
    "quirk_id": null,
    "manufacturer_code": 4417,
    "power_source": "Battery or Unknown",
    "lqi": null,
    "rssi": null,
    "last_seen": "2024-06-16T16:15:49",
    "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=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=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": "0x0104",
          "device_type": "0x0000",
          "input_clusters": [
            "0x0000",
            "0x0001",
            "0x0004",
            "0x0005",
            "0x0006",
            "0x0702",
            "0xef00"
          ],
          "output_clusters": [
            "0x000a",
            "0x0019"
          ]
        }
      },
      "manufacturer": "_TZE200_7ytb3h8u",
      "model": "TS0601"
    },
    "active_coordinator": false,
    "entities": [
      {
        "entity_id": "sensor.electrovanne_batterie",
        "name": "_TZE200_7ytb3h8u TS0601"
      },
      {
        "entity_id": "sensor.electrovanne_demande_instantanee",
        "name": "_TZE200_7ytb3h8u TS0601"
      },
      {
        "entity_id": "sensor.electrovanne_summation_delivered",
        "name": "_TZE200_7ytb3h8u TS0601"
      },
      {
        "entity_id": "switch.electrovanne_commutateur",
        "name": "_TZE200_7ytb3h8u TS0601"
      },
      {
        "entity_id": "update.electrovanne_micrologiciel",
        "name": "_TZE200_7ytb3h8u TS0601"
      }
    ],
    "neighbors": [],
    "routes": [],
    "endpoint_names": [
      {
        "name": "ON_OFF_SWITCH"
      }
    ],
    "user_given_name": "electrovanne",
    "device_reg_id": "c8a695ef6f9e50c5dda150243ce08909",
    "area_id": "arbres_fruitiers",
    "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
              },
              "0x0004": {
                "attribute_name": "manufacturer",
                "value": "_TZE200_7ytb3h8u"
              },
              "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": 0
              }
            },
            "unsupported_attributes": {}
          },
          "0x0001": {
            "endpoint_attribute": "power",
            "attributes": {
              "0x0021": {
                "attribute_name": "battery_percentage_remaining",
                "value": 400
              }
            },
            "unsupported_attributes": {}
          },
          "0x0702": {
            "endpoint_attribute": "smartenergy_metering",
            "attributes": {
              "0x0000": {
                "attribute_name": "current_summ_delivered",
                "value": 0
              },
              "0x0306": {
                "attribute_name": "metering_device_type",
                "value": 2
              },
              "0x0300": {
                "attribute_name": "unit_of_measure",
                "value": 7
              }
            },
            "unsupported_attributes": {}
          },
          "0xef00": {
            "endpoint_attribute": "tuya_manufacturer",
            "attributes": {
              "0xef72": {
                "attribute_name": "irrigation_duration",
                "value": "00:00:00,0"
              },
              "0xef66": {
                "attribute_name": "irrigation_end_time",
                "value": "05:36:38"
              },
              "0xef69": {
                "attribute_name": "irrigation_interval",
                "value": 0
              },
              "0xef01": {
                "attribute_name": "irrigation_mode",
                "value": 0
              },
              "0xef67": {
                "attribute_name": "irrigation_num_times",
                "value": 0
              },
              "0xef65": {
                "attribute_name": "irrigation_start_time",
                "value": "05:36:38"
              },
              "0xef68": {
                "attribute_name": "irrigation_target",
                "value": 0
              },
              "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": 72
              }
            },
            "unsupported_attributes": {}
          }
        }
      }
    }
  }
}

Logs

Logs
[Paste the logs here]

Custom quirk

ts0601_valve.py
"""Collection of Tuya Valve devices e.g. water valves, gas valve etc."""

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 EnchantedDevice, TuyaLocalCluster
from zhaquirks.tuya.mcu import (
    DPToAttributeMapping,
    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),
            0xEF05: ("valve_position", 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",
        ),
        102: DPToAttributeMapping(
            TuyaMCUCluster.ep_attribute,
            "valve_position",
        ),
    }

    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",
        102: "_dp_2_attr_update",
    }


class TuyaValve(CustomDevice):
    """Tuya valve device."""

    signature = {
        MODELS_INFO: [("_TZE200_81isopgh", "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 BasicTuyaValve(CustomDevice):
    """Basic Tuya valve device."""

    signature = {
        MODELS_INFO: [("_TZE200_1n2zev06", "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,
                    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: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0
            # device_version=1
            # input_clusters=[0, 1, 3, 4, 5, 6, 61184]
            # output_clusters=[10, 25]>
            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],
            },
        },
    }


# 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 sec 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_sh1btabb", "TS0601"), ("_TZE200_a7sghmms", "TS0601"), ( "_TZE204_7ytb3h8u", "TS0601"),  ("_TZE200_7ytb3h8u", "TS0601")],
        ENDPOINTS: {
            # <SimpleDescriptor endpoint=1 profile=260 device_type=0x0051
            # input_clusters=[0x0000, 0x0004, 0x0005, 0xef00]
            # output_clusters=[0x000a, 0x0019]>
            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,
                    TuyaOnOffNM,
                    TuyaPowerConfigurationCluster,
                    TuyaValveWaterConsumed,
                    GiexValveManufCluster,
                ],
                OUTPUT_CLUSTERS: [Time.cluster_id, Ota.cluster_id],
            }
        }
    }

Additional information

Enable / disable test done

valve

No response

@dethegeek dethegeek changed the title Add Tuya Giex GX02 water valve support Add Tuya Giex GX02 water valve support TS0601 _TZE200_7ytb3h8u Jun 18, 2024
@dethegeek dethegeek changed the title Add Tuya Giex GX02 water valve support TS0601 _TZE200_7ytb3h8u [Device Support Request] Add Tuya Giex GX02 water valve support TS0601 _TZE200_7ytb3h8u Jun 18, 2024
@TheJulianJES TheJulianJES added the Tuya Request/PR regarding a Tuya device label Jun 21, 2024
@jaggaer-alocatel
Copy link

Hi @dethegeek I bought the same valve but unfortunately I cannot integrate it successfully as suggestedabove. I copied the quirk file adding the model and I see it in HA with only switch e battery sensor. the problem is that the switch doesn't work (like the valve ignores it) an battery is unknown.
same behaviour with 2 identical valves.
have I missed anything?

@liksvette
Copy link

I added _TZE200_7ytb3h8u to line 384. No idea how to report this, so I'll just comment it here, and hope a grown-up can assist. It's for this unit: https://www.aliexpress.com/item/1005006547328147.html?spm=a2g0o.order_list.order_list_main.4.787f18021Qrnq9

MODELS_INFO: [("_TZE200_sh1btabb", "TS0601"), ("_TZE200_a7sghmms", "TS0601"), ( "_TZE200_7ytb3h8u", "TS0601"), ( "_TZE204_7ytb3h8u", "TS0601")],

@dethegeek
Copy link
Author

dethegeek commented Aug 5, 2024

Hi @jaggaer-alocatel

On my side I can switch the state of my device.

However I noticed some issues

  • Battery level is often reported 200% or sometimes 400%. It usually drops to 100% when I switch on the device, and since a few days it dropped to 90% (seems legit, depite the battery is drained quite fast).
  • Instant water delivery is empty
  • Summation of delivered water is reset to zero when I switch on the device again.

@dethegeek
Copy link
Author

Hi @liksvette

My device looks the same as yours.

@jaggaer-alocatel
Copy link

Hi @jaggaer-alocatel

On my side I can switch the state of my device.

However I noticed some issues

  • Battery level is often reported 200% or sometimes 400%. It usually drops to 100% when I switch on the device, and since a few days it dropped to 90% (seems legit, depite the battery is drained quite fast).
  • Instant water delivery is empty
  • Summation of delivered water is reset to zero when I switch on the device again.

sorry, it was by bad... wrong change in the file. Now my devices look as ours. Thanks a lot

@liksvette
Copy link

Hi @liksvette

My device looks the same as yours.

Your code snippet had _TZE204 as opposed to _TZE200, so it didn't work on mine.

@dethegeek
Copy link
Author

@liksvette the code snippet is the original line (this is a reference to the line in the repository). You just need to add the device in it.

The result should look like this

MODELS_INFO: [("_TZE200_sh1btabb", "TS0601"), ("_TZE200_a7sghmms", "TS0601"), ( "_TZE204_7ytb3h8u", "TS0601"), ( "_TZE200_7ytb3h8u", "TS0601")],

@nicktrigger
Copy link

nicktrigger commented Nov 3, 2024

Problem description

Similar device aded to solve the following issue #3124

My device is detected as TS0601 _TZE200_7ytb3h8u

Bought here: https://fr.aliexpress.com/item/1005006548852233.html

Solution description

I succesfully aded the device by adding the above model in

MODELS_INFO: [("_TZE200_sh1btabb", "TS0601"), ("_TZE200_a7sghmms", "TS0601"), ( "_TZE204_7ytb3h8u", "TS0601")],

I'm rather new with domotic and Home Assistant, I'm not skilled enough to get a zigbee to MQTT dump for now, then I did a try after roughly understanding how the ZHA quirks works.

Screenshots/Video

Screenshots/Video

Device signature

Device signature

Diagnostic information

Diagnostic information

Logs

Logs

Custom quirk

ts0601_valve.py

Additional information

Enable / disable test done

valve valve

No response

I'm sure this is a dumb question but where and how can I access this file on my home assistant instance to add the code?
I tried to add as a custom quirk but it had errors and didn't load.
There seems to be no documenting or explanation anywhere about how to actually do this!

@dethegeek
Copy link
Author

dethegeek commented Nov 3, 2024 via email

@nicktrigger
Copy link

Many thanks my devices have different names but are basically the same. I just want to test to see if they work by inserting the product codes into this quirk but i have no idea where to start. There seems to be something missing in my setup. Here is the latest error:

Logger: zhaquirks
Source: /usr/local/lib/python3.12/site-packages/zhaquirks/init.py:464
First occurred: 23:07:39 (1 occurrences)
Last logged: 23:07:39

Unexpected exception importing custom quirk 'Valves'
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/zhaquirks/init.py", line 462, in setup
spec.loader.exec_module(module)
File "", line 995, in exec_module
File "", line 488, in _call_with_frames_removed
File "/config/config/custom_zha_quirks/Valves.py", line 27, in
from zhaquirks.tuya.builder import TuyaQuirkBuilder
ModuleNotFoundError: No module named 'zhaquirks.tuya.builder'

@nicktrigger
Copy link

My devices are ts0601_TZE200_akjefhj5 and ts0601_TZE204_qtnjuoae

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Tuya Request/PR regarding a Tuya device
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants