Beware of Slithering Creatures - They Hiss

The Goal Behind These Snippets

•••••••••••

Watering

•••••••••••

I wound up getting a Tuya Watering Valve

Scripted getting the status out of it and issuing commands. Requires the Tiny Tuya Library, and applies to Frankever DN20 Valve

This script will print the status like this, or if TRIGGER_VALVE is True, make it spray water for a moment:


Melon
status.py

# filepath: status.py
import tinytuya
import time
from config import status_map

TRIGGER_VALVE = False

def show_status(data):
    print("\n" + "="*55)
    print("       SMART IRRIGATION CONTROLLER STATUS")
    print("="*55)

    dps = data.get('dps', {})

    for dp_id, raw_value in sorted(dps.items(), key=lambda x: int(x[0])):
        if dp_id not in status_map:
            print(f"  DPS {dp_id:>3}: {raw_value}  (unknown)")
            continue

        info = status_map[dp_id]
        code = info["code"]
        dtype = info["type"]
        vals = info["values"]

        # ----------  format the value ----------
        if dtype == "Boolean":
            formatted = "ON" if raw_value else "OFF"

        elif dtype == "Enum" and "range" in vals:
            range_list = vals["range"]
            # accept either an index (int) or the literal string
            if isinstance(raw_value, int) and 0 <= raw_value < len(range_list):
                formatted = range_list[raw_value]
            elif isinstance(raw_value, str) and raw_value in range_list:
                formatted = raw_value
            else:
                formatted = str(raw_value)

        elif "unit" in vals:
            unit = vals["unit"]
            if "scale" in vals and vals["scale"] > 0:
                formatted = f"{raw_value / (10 ** vals['scale']):.{vals['scale']}f}{unit}"
            else:
                formatted = f"{raw_value}{unit}"
        else:
            formatted = str(raw_value)

        print(f"  {code:25} | DPS {dp_id:>3} | {formatted}")

    print("="*55)


# Connect to Device
d = tinytuya.Device(
    dev_id='HAHAHAHA',
    address='YARITE',
    local_key='YOUWISH',
    version=3.5,
    persist=True)
d.set_version(3.5)

show_status(d.status())


if TRIGGER_VALVE:
    print("\nTriggering valve ON for 4ish seconds...")
    result = d.set_multiple_values({
        1: True,     # Turn ON
        2: 10,       # 10% open
    }, nowait=False)
    print("Set result:", result)
    time.sleep(1)
    print("\nChecking status after 1 second...")
    show_status(d.status())
    time.sleep(3)
    
    # 4. Final check
    print("\nFinal status check")
    final = d.status()
    show_status(final)

    dps = final.get('dps', {})
    still_on = dps.get('1') is True

    if still_on:
        print("Shutting down valve manually...")
        final_result = d.set_multiple_values({1: False}, nowait=False)
        print("Set result:", final_result)
        print("\nStatus after forced shutoff, waiting 3 seconds to poll:")
        time.sleep(3)
        show_status(d.status())
    else:
        print("Valve shut off automatically - all good.")
  
config.py

status_map = {
    "1": {
        "code": "switch",
        "type": "Boolean",
        "values": {}
    },
    "2": {
        "code": "percent_control",
        "type": "Integer",
        "values": {
            "unit": "%",
            "min": 0,
            "max": 100,
            "scale": 0,
            "step": 10
        }
    },
    "3": {
        "code": "percent_state",
        "type": "Integer",
        "values": {
            "unit": "%",
            "min": 0,
            "max": 100,
            "scale": 0,
            "step": 1
        }
    },
    "5": {
        "code": "water_once",
        "type": "Integer",
        "values": {
            "unit": "L",
            "min": 0,
            "max": 10000,
            "scale": 1,
            "step": 1
        }
    },
    "6": {
        "code": "water_total",
        "type": "Integer",
        "values": {
            "unit": "L",
            "min": 0,
            "max": 999999999,
            "scale": 0,
            "step": 1
        }
    },
    "10": {
        "code": "weather_delay",
        "type": "Enum",
        "values": {
            "range": [
                "cancel",
                "24h",
                "48h",
                "72h"
            ]
        }
    },
    "11": {
        "code": "countdown",
        "type": "Integer",
        "values": {
            "unit": "s",
            "min": 0,
            "max": 86400,
            "scale": 0,
            "step": 1
        }
    },
    "22": {
        "code": "sensor_temperature",
        "type": "Integer",
        "values": {
            "unit": "°C",
            "min": 0,
            "max": 100,
            "scale": 0,
            "step": 1
        }
    },
    # Additional common DPS codes often seen in smart irrigation devices
    "102": {
        "code": "auto_mode",
        "type": "Boolean",
        "values": {}
    },
    "103": {
        "code": "manual_watering_time",
        "type": "Integer",
        "values": {
            "unit": "s",
            "min": 0,
            "max": 3600,
            "scale": 0,
            "step": 1
        }
    },
    "104": {
        "code": "child_lock",
        "type": "Boolean",
        "values": {}
    },
    "105": {
        "code": "cycle_watering",
        "type": "Integer",
        "values": {
            "unit": "times",
            "min": 0,
            "max": 10,
            "scale": 0,
            "step": 1
        }
    },
    "106": {
        "code": "cycle_interval",
        "type": "Integer",
        "values": {
            "unit": "min",
            "min": 0,
            "max": 1440,
            "scale": 0,
            "step": 1
        }
    },
    "107": {
        "code": "cycle_duration",
        "type": "Integer",
        "values": {
            "unit": "s",
            "min": 0,
            "max": 3600,
            "scale": 0,
            "step": 1
        }
    },
    "108": {
        "code": "frost_protection",
        "type": "Boolean",
        "values": {}
    },
    "109": {
        "code": "remaining_watering_time",
        "type": "Integer",
        "values": {
            "unit": "s",
            "min": 0,
            "max": 86400,
            "scale": 0,
            "step": 1
        }
    },
    "110": {
        "code": "mode",
        "type": "Enum",
        "values": {
            "range": ["off", "manual", "auto", "delay"]
        }
    },
    "112": {
        "code": "pump_status",
        "type": "Boolean",
        "values": {}
    },
    "113": {
        "code": "fault_code",
        "type": "Integer",
        "values": {
            "min": 0,
            "max": 255,
            "scale": 0,
            "step": 1
        }
    }
}
  

Sharing is caring

My next logical steps, are going to be having this triggered either Node Red, or ole fashioned cron - I am still pondering.