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:
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.