Why I went down this road
I started with Zigbee sprinkler controllers. They were fine… until they weren't. One by one they died, and the real kicker: when they failed they wouldn't re-pair — no amount of reset/exclude/include dancing brought them back. Replacing them on the same platform felt like signing up for the same disappointment on a timer.
So I set two hard requirements:
- Fully local / cloud-independent. No vendor cloud, no account, no "the service is down so my lawn cooks." Everything decided on the hub.
- A hardware-level failsafe. Even if the hub itself hangs or reboots mid-watering, a valve must not stay open indefinitely.
That combination is what pushed me to Zooz Z-Wave multi-relays and, eventually, to writing my own app to run the whole thing.
The hardware
- Zooz ZEN16 (3 relays) and ZEN17 (2 relays) multi-relays driving standard 24VAC sprinkler valves.
- Mains-powered, so they're always awake and act as solid Z-Wave repeaters — no dead batteries, no sleepy-device weirdness.
- Running @jtp10181's excellent Advanced ZEN16/ZEN17 drivers (I keep a lightly modified fork — added a
ping()for an active reachability check and removed the upstream telemetry call; all the hard driver work is his). - The reason for Zooz specifically: each relay has a hardware Auto-Off timer (params P6/P8/P10). The app pushes those, so if Hubitat ever falls over mid-cycle, the relay closes itself at the device level. That's the piece that lets me actually trust it.
What the app does — "Zooz Sprinkler Scheduler"
The app is a Hubitat user app called Zooz Sprinkler Scheduler. It's a full irrigation controller, hardware-agnostic (any device with the Switch capability works; Zooz relays are just the reliable local muscle):
- Zones — dynamic count, per-zone runtimes either fixed minutes or Spruce-style weekly-target (minutes/week ÷ days/week), cycle & soak to control runoff, per-zone weekly budget caps.
- Scheduling — days-of-week or every-N-days, up to three start windows per run, with a Simple/Advanced UI toggle so it isn't overwhelming for a basic setup.
- Weather (Open-Meteo, no API key) — rain-delay by forecast/recent rainfall, seasonal runtime scaling, smart-skips for frost/cold/wind. Unit-aware — follows the hub's °F/°C setting for everything (temps, wind, rainfall).
- Sensors — binary rain sensors (incl. a ZEN16/ZEN17 Sw input wired as a water/contact sensor), and optional soil-moisture-aware watering with skip / early-stop / adaptive / learning modes.
- Pause & resume (my favorite feature) — point any contact or switch at it and an in-progress run pauses mid-zone and then resumes from exactly where it left off when the trigger clears. It remembers the remaining seconds for the current zone — it does not restart the zone or the whole schedule. Perfect for a back-door/gate contact (kids and pets don't get blasted), a "guests are over" override switch, etc., with a configurable resume delay and an option to fully stop instead of pause.
- Safety — quiet hours, mode/HSM restrictions, pump/master-valve control, a software per-zone max-run cap, and the hardware Auto-Off failsafe described above. The hardware push now verifies the timers actually landed on the device instead of just claiming success.
- Control & visibility — exposes each zone (and a "Run schedule now") as a child switch for HomeKit / dashboards / Rule Machine, granular templated notifications (incl. Pushover), run history, a next-7-days preview, an iCalendar feed, a JSON API, and backup/restore.
The complexity (and a couple of hard-won lessons)
Watering "well" turns out to be a lot of little decisions stacked on top of each other — should it run today, how long given the forecast, split into soak cycles, skip if a sensor says wet, don't run while a door's open, don't overshoot the weekly budget, and always have a hardware backstop. Wiring all of that together so it stays predictable is most of the work.
Two findings worth saving anyone else the hours I spent:
- ZEN16/ZEN17 sensor inputs (Water/Contact/etc.) won't report until you exclude + re-include the device. Setting the input-type parameter takes effect (it'll even drive a relay), but the unsolicited sensor-report association is only established during the Z-Wave inclusion interview — Save/Configure/power-cycle did nothing. After a re-pair, the input reported instantly. The parameter was never the problem; the report path was.
- "setParameter sent OK" ≠ "the device accepted it." Hubitat hides command argument names from apps, so auto-detecting the ZEN16's
setParameterargument order (size/value vs value/size) isn't reliable. Get it wrong and the device silently ignores the write — my hardware Auto-Off timers sat at 0 for ages while the push cheerfully reported success. Lesson: for anything safety-related, read the value back and verify, don't trust the send.
Install / what's in the repo
Everything is on Hubitat — no cloud anything.
Easiest — Hubitat Package Manager (HPM): search Zooz Sprinkler Scheduler in HPM and install. It pulls in the app and offers the ZEN16/ZEN17 fork drivers as optional add-ons. (If it doesn't show up, add my repo in HPM → Settings → Add a custom repository: https://raw.githubusercontent.com/RamSet/hubitat/main/repository.json)
Manual install — paste these as Import URLs in the Apps Code / Drivers Code editors:
- App — Zooz Sprinkler Scheduler:
https://raw.githubusercontent.com/RamSet/hubitat/main/zooz-sprinkler-scheduler.groovy - Driver — Zooz ZEN16 MultiRelay Advanced (my fork of @jtp10181's — adds
ping(), removes the telemetry check-in):
https://raw.githubusercontent.com/RamSet/hubitat/main/zooz-zen16-multirelay.groovy - Driver — Zooz ZEN17 Universal Relay (same fork treatment):
https://raw.githubusercontent.com/RamSet/hubitat/main/zooz-zen17-universal-relay.groovy
Either way: install the driver(s) first, pair the ZEN16/ZEN17, then add the app and point each zone at a relay child. Repo (code + readme): github.com/RamSet/hubitat
Driver credit: the ZEN16/ZEN17 Advanced drivers are @jtp10181's work — I'm just running a lightly-modified fork. If you only need the drivers, use his originals; mine exist so the app's reachability ping and a no-telemetry build are guaranteed present.
Happy to answer questions or take feedback — this scratched a very specific itch and I'd love to make it useful for others.
Curious if anyone else has gone fully-local for irrigation, and how you're handling the "hub dies mid-run" failsafe — the per-relay hardware timer has been the thing that finally let me stop worrying about it.
