Royal Gardineer garden water valve

I bought a zigbee-compatible water-valve called Royal Gardineer (_TZE200_2wg5qrjy) and added it to my HA-installation. Sadly, I’ve just 2 sensors (somewhat “LQI” and somewhat “RSSI”), but there are no entities for enabling or disabling the device /water.

In HA the device-signature is:

{
  "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=4098, maximum_buffer_size=82, maximum_incoming_transfer_size=82, server_mask=11264, maximum_outgoing_transfer_size=82, 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": 260,
      "device_type": "0x0051",
      "in_clusters": [
        "0x0000",
        "0x0004",
        "0x0005",
        "0xef00"
      ],
      "out_clusters": [
        "0x000a",
        "0x0019"
      ]
    }
  },
  "manufacturer": "_TZE200_2wg5qrjy",
  "model": "TS0601",
  "class": "zigpy.device.Device"
}

Does anyone know, how to get access to the switches of this device? At the moment it isn’t useable.

Best regards
EnzephaloN

1 Like

I feel you. Same problem here though brand name is different.

Since I am running Tasmota on my zigbee bridge and I don’t have an unflashed one, I’m stuck.

Either I solve this within HA or I guess I’ll need to buy another hub.

Hi

started my HA journey this year and trapped into these as well. Did some research the last days an think I got quite some helpful materials to fix it. Only being in lack of time and practice with the HA programming model. What I got so far:
It seems one need to extend these tuya quirks: zha-device-handlers/ts0601_valve.py at 71c95b76751ada487314eda2bc3bde5246f7dea0 · zigpy/zha-device-handlers · GitHub

Capabilities of the valve we find here: RTX ZVG1 control via MQTT | Zigbee2MQTT

From the logs (unfortunately having no access to my HA) I noticed commands with Ids 1 and 36 being unknown so far.
My guess is, we miss a device mapping in ZHA. Command 1 might be already available in the code base (on_off) but for 36 I’m clueless so far.
I’ll try to setup a dev env to create custom quirk based on the class mentioned above and see how far I get. Any assistance on reverse engineering the tuya data point API would be appreciated as I’ve no experience with it so far.

Let’s see how far that gets… :wink:

And deconz community is working on the same, maybe there are synergies: Royal Gardineer ZigBee Water Valve · Issue #6143 · dresden-elektronik/deconz-rest-plugin · GitHub

And there’s a PR for zigbee-herdsman https://github.com/Koenkk/zigbee-herdsman-converters/pull/4190

( Not allowed to post more links with the prev post…)

Thank you all for your replies.

I ended up with another zigbee-controller (ZigStarLAN) which connects to a repeater (IKEA Tradfri plug) which connects to the water-valve. I also added docker-container for mqtt and zigbee2mqtt, configured everything and TaDa! it works!

But, I’m frustrated about “this device only works with z2m and this device only with ZHA”.
I tried to completely switch to z2m, but my plugs (silvercrest/lidl) only work with ZHA (not pairing with z2m). On the other hand my IKEA shortcut-switches (E1812) only work with z2m (missing entities in ZHA).

Also I recognized some other plugs I use, do not work as repeater when connected to z2m, but do repeat when connected to ZHA - so I needed to buy the Tradfri, for beeing repeater from z2m to the water-valve.

It’s a frustrating journey…

hacked together a custom quirk tonite based on the tuya valve code. At least on/off worked immediately with ZHA, rest not. Will investigate further…

It would be nice if you would share your code.

I’m still looking for a better solution. yesterday the water-value activated as expected :partying_face: , but never closed :frowning: today it doesn’n act on any command :frowning:

Hi

I just got one of these (from a different manufacturer but same device). Please share how you got it working with ZHA?

Thanks!

@der-burn, I´m also interested on this could. Could you please share?

Into the configuration.yaml I added the following lines

zha:
  custom_quirks_path: /config/custom_zha_quirks/

And in that path I copied this file: zha-device-handlers/ts0601_valve.py at 72daeee636d876d6ee2a390153ec7973583f27bc · zigpy/zha-device-handlers · GitHub and changed the linked line to the following:

MODELS_INFO: [("_TZE200_81isopgh", "TS0601"), ("_TZE200_2wg5qrjy", "TS0601")],

You’ve to check for the correct device id to be appended.
Hope that helps.

2 Likes

Hi, any irrigation device that you recommend? Zigbee preferably

Thanks a million!
it does the trick for my Royal Gardineer valve to switch it on/off!

Small remark:
the custom quirk wasn’t compatible with newer HA versions, the issue got logged and solved here:

It is about: TuyaDPType became obsolete and has to be removed from the quirk.

Water consumption is a bit out of range though: several hundreds of liters in some minutes

2 Likes

Thank you both of you !
Only battery % is missing !

@nicknol thanks for highlighting. I just found out myself as well, didn’t see your comment.
After commenting out all the TuyaDPType occurrence it works well again.

But another question. How did you make the water consumption work? Which datapoint are you using. I never got it running at all (even not with hihg amount of liters) and gave up eventually.

Cheers!

@der-burn I have to admit I didn’t change anything of your code except removing the TuyaDPType stuff.
Your code retrieved the data…

This is the quirk I am using, I think it is completely yours @der-burn

"""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")], ("_TZE200_2wg5qrjy", "TS0601")],
        MODELS_INFO: [("_TZE200_2wg5qrjy", "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: {
            # <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],
            },
        },
    }

@der-burn
could we join forces to get battery and consumption working?

1 Like

Hi, I have basically the same device, but from a different vendor. Using the quirk posted above, I got the on/off switch to work, but not the consumption nor battery. I’ll try to spent some time on this over the weekend. But I’m wondering where the 695 litres come from in the above screenshot. Did the device count these? My counter remains at zero.

Hi @nilsreiter
yes, the data comes from the device. See the history graphs below:


that one shows a value different from zero as actual value.


this one shows a zero as actual value.

and: I didn’t modify the quirk at all.

Thanks!

Out of curiosity: Are you certain that this measurement is correct? Hundreds of litres sounds a lot for normal garden.