Request for help for Tuya Water valve - DTH and EDGE driver


I am looking for some help developing a DTH for a Tuya Water valve (which screws on to a tap for irrigation control).

  • application: 55
  • endpointId: 01
  • manufacturer: _TZE200_akjefhj5
  • model: TS0601

01 0104 0051 01 04 0000 0004 0005 EF00 02 0019 000A

I started making progress with this some time ago, trying to build on a Moes TS0601 switch DTH from Kotsos, and with the help of @ygerlovin - but I stopped progressing it.

The basics work -
The on/off button on the valve sends the appropriate message to ST.
And the ST on/off tile does indeed turn the valve on and off.

But I’m looking to improve it. The key features I would like to add to the DTH are:

  • Reporting on the battery level
  • Adding a preference to choose an auto-off time period (on/off)
  • Cleaning up the preference for setting the auto off time (greying it out if the above option is off)

(And also help to clean up unused code)

This is what I’ve got so far -

 *  Moes ZigBee Switch // HACK
 *  Copyright 2020 SmartThings
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not
 *  use this file except in compliance with the License. You may obtain a copy
 *  of the License at:
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  License for the specific language governing permissions and limitations
 *  under the License.
public static String version() { return "v0.0.1.20210727" }

private getMODEL_MAP() { 
        'TS0601' : 3

metadata {
    definition(name: "C Smartfoss Tester", namespace: "CSF Test", author: "Kotsos", ocfDeviceType: "oic.d.watervalve", vid: "generic-valve") {
        capability "Actuator"
        capability "Configuration"
        capability "Refresh"
        capability "Health Check"
        capability "Valve"
  		capability "Battery"
  		capability "Switch"

		fingerprint profileId: "0104", model: "TS0601", manufacturer: "_TZE200_akjefhj5", endpointId: "01", inClusters: "0000,0004,0005,EF00", outClusters: "0019,000A", application: "55", deviceJoinName: "Smartfoss"

        preferences {
       // input name: "autoOff", type: "bool", title: "Turn off automatically", defaultValue: false, required: false
		input name: "autoofftimeinput", type: "number", title: "Auto off time period (minutes)", defaultValue: 10, required: false

      tiles(scale: 2) {  

      main(["battery"])	//main(["mode","button"])

// Parse incoming device messages to generate events
def parse(String description) {
    Map map = [:]
    //def event = zigbee.getEvent(description)

    if (description?.startsWith('catchall:')) {
  		log.debug "description is $description"      
        log.debug description
        // call parseCatchAllMessage to parse the catchall message received
        map = parseCatchAllMessage(description)
        if (map != [:]) {
            log.debug "ok send event: $ : $map.value"
            sendEvent(name:, value: map.value)
    else {
        log.warn "DID NOT PARSE MESSAGE for description : $description"

// Commands from Smartthings
def close() {
    log.debug "called closed"
    zigbee.command(0xEF00, 0x0, "00010101000100")

def autoofftime(value) {
	String autoofftime = "00010B020004" + zigbee.convertToHexString((value * 60) as Integer, 8)

def open() {
    log.debug "called open"
//    sendHubCommand(zigbee.command(0xEF00, 0x0, "00010B02000400015180"))
   def AOTI = autoofftimeinput
   log.debug "AOTI = ${AOTI}"        
    sendHubCommand(zigbee.command(0xEF00, 0x0, autoofftime(AOTI)))    
    zigbee.command(0xEF00, 0x0, "00010101000101")

def off() {

def on() {

def refresh() {
    log.debug "called refresh"
    zigbee.command(0xEF00, 0x0, "00020100")


def update() {

def configure() {
    log.debug "Configuring Reporting and Bindings."
    zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()

// Commands from the device

private Map parseCatchAllMessage(String description) {
    // Create a map from the raw zigbee message to make parsing more intuitive
    def msg = zigbee.parse(description)
    Map result = [:]
    switch(msg.clusterId) {
        case 0xEF00: 
            def attribute = getAttribute(
            def value = getAttributeValue(
         //   def AOTS = autoofftimeinput
            def autoofftimeFD = autoofftime(autoofftimeinput)
            switch (attribute) {      
                case "valve": 
                    switch(value) {
                        case 0:
                            result = [
                                name: 'valve',
                                value: 'closed',
                                data: [buttonNumber: 1],
                                descriptionText: "$device.displayName button was pressed",
                                isStateChange: true
                        sendEvent(name: 'switch',  value:"off", displayed: true )

                        case 1:
                            result = [
                                name: 'valve',
                                value: 'open',
                                data: [buttonNumber: 1],
                                descriptionText: "$device.displayName button was pressed",
                                isStateChange: true
						sendEvent(name: 'switch',  value:"on", displayed: true )
                        sendHubCommand(zigbee.command(0xEF00, 0x0, autoofftimeFD))
    return result

private String getAttribute(ArrayList _data) {
    String retValue = ""
    if (_data.size() >= 5) {
        if (_data[2] == 1 && _data[3] == 1 && _data[4] == 0) {
            retValue = "valve"
        else if (_data[2] == 2 && _data[3] == 2 && _data[4] == 0) {
            retValue = "level"
    return retValue

private int getAttributeValue(ArrayList _data) {
    int retValue = 0
    if (_data.size() >= 6) {
        int dataLength = _data[5] as Integer
        int power = 1;
        for (i in dataLength..1) {
            retValue = retValue + power * _data[i+5]
            power = power * 256
    return retValue

I appreciate this is hard to help with if you don’t have the device, but I’m happy to provide any logs required. I’m not a developer and kind of fumbling my way around this.

Also - looking to get an Edge driver developed as well if possible, since I know the Groovy DTH’s are on their way out.

Any help would be sincerely appreciated.



Hi! Interested in this here. I will load this up and see if there’s anything I can help with


Hi @brgavino - thanks for this.
Obviously since I posted this in June, the Groovy to Edge timeline has sped up a bit, so not keen to go down the DTH path anymore - unless it helps develop an Edge driver beyond that.

I’m more than happy to test anything you can throw my way.

I’ve taken some time to look into the Driver path and considering the heavy lift to get this working, it would be a better effort of time to migrate fully to HomeAssistant or the like - which has a more engaged dev community from the authorship side. Which is too bad, but the proprietary model has a strong pull.

ok - many thanks for taking a look anyway.

@Hendo25 Did you get anything working here? Is this the tap timer you have ?

ZIGBEE Sprinkler Timer with Gateway Work for Tuya/Smart Life,Smart Water Timer for Garden Hose,Up to 20 Separate Watering Schedules,Water Flow Meter, for Yard Lawn Watering,Rain Delay, Gray : Garden