View on GitHub

Irrigation Plus

Evapotranspiration-based irrigation scheduling for Home Assistant — a maintained community fork of Smart Irrigation, renamed to Irrigation Plus

My Zones

Main page: Configuration
Previous: Weather & Location
Next: When to Water

Specify one or more irrigation zones here. The integration calculates irrigation duration per zone, depending on size, throughput, state, module and sensor group. A zone can be:

When entering any values in the configuration of this integration, take notice of the labels provided so you enter values in the correct units.

Where zones live: dashboard vs. settings

Zones appear in two places:

Multi-zone support

For irrigation systems that have multiple zones which you want to run in series or independent you need to create multiple zones. The configuration should be done for each zone, including the area the zone covers and the corresponding settings.

Adding a zone

Zones are added and configured under Setup → My Zones. Click the + button and provide:

After clicking Add, the new zone appears in the list (and as a device with entities in Home Assistant), with its settings shown directly underneath so you can finish configuring it.

Actions on all automatic Zones

These bulk actions are split across the two surfaces:

Configuring a zone

Open Setup → My Zones. Each zone's settings are shown directly under its name (no need to expand anything), and you can change:

Watering mode {#watering-mode}

Each zone chooses how the integration actuates it:

Set the controller's water level to 100%. OpenSprinkler applies its own weather-based water level percentage to every run it is given. Irrigation Plus has already scaled the duration by its own calculation, so leaving the controller's adjustment on applies a weather correction twice and the zone is under-watered. Set the water level to 100%, or turn the controller's weather adjustment off. Master and pump control stays with the controller, which drives its own master station with its own on/off delays.

What counts as self-closing hardware? Anything that runs its own timer once told a duration — for example Zigbee2MQTT valves with a built-in countdown (Tuya countdown_l1, SONOFF cyclic_timed_irrigation), or a DIY / ESPHome controller that closes its own valve after a received runtime. The script is only an adapter so the integration can drive all of them the same way — it is not what closes the valve. A plain switch with no hardware timer is not self-closing; use Classic mode for that.

To make self-closing setup painless, three script blueprints ship with the integration and are copied into config/blueprints/script/irrigation_plus/ automatically on setup (existing files are never overwritten):

Blueprint For Duration unit
Tuya TS0601 dual water valve (Z2M) Tuya TS0601_water_switch — countdown_l1 + state_l1 (custom converters may use valve_l1) Minutes
SONOFF Smart Water Valve SWV (Z2M) SONOFF Zigbee Smart Water Valve (SWV) — cyclic_timed_irrigation Seconds
Self-closing valve (entity based) ZHA / non-MQTT valves with a hardware countdown number entity match the entity

Setup: create a script from the blueprint under Settings → Automations & Scenes → Blueprints (fill in your valve's MQTT topic or entities), then in the zone set Watering mode = Self-closing service and pick that script as the Run service. Each blueprint's script opens on duration > 0 and closes on duration = 0, so the same script also works as the Stop service.

Blueprints only cover common cases. Any script that accepts a duration field works — the device specifics live in the script, so the same mechanism drives Z2M, ZHA, ESPHome or anything else.

The stop service must also clear the queue. On a real controller, stopping one zone stops the whole cycle — but the queue survives it. Zones that were queued and never started stay queued, and will water on the next manual start, with Irrigation Plus having already closed its books on them. That is why the stop script has to do both, and why stopping any one batch zone settles every batch run in flight rather than pretending it is a per-zone action. The same applies when the integration is disabled or removed, or Home Assistant shuts down: each of those stops the controller and clears the queue too. On an ESPHome controller the script is sprinkler.shutdown followed by sprinkler.clear_queued_valves.

Clear the queue at the start of the run script too. The stop path above is the one that matters for a cycle Irrigation Plus ended itself, but a queue can also survive something Irrigation Plus never saw — a cycle started by hand on the controller, or one left behind by a controller reboot. Making clear_queued_valves the first action of the run script means every plan starts from a known state, and a leftover zone cannot run alongside the plan that was just handed over. The worked example below does this.

If your controller switches its own pump, do not configure a master switch here. ESPHome's sprinkler component has its own pump handling, which switches the pump along with the valves. A master switch configured in Irrigation Plus as well would give one pump two independent owners, each deciding when it runs — exactly the failure the master model exists to prevent. Use one or the other. (Note that the pump interaction is unverified in the field: the only person able to test this mode has no pump.)

Leave the controller's multiplier and repeat out. If your controller can scale run times or repeat a cycle, those would rescale durations Irrigation Plus has already calculated. They are optional in the ESPHome configuration — omit them, or set them to neutral values (multiplier 1, repeat 0). Irrigation Plus cannot detect them, so this is a precondition rather than something it can enforce.

Pausing {#batch-pause}

Some controllers can pause an irrigation: the valve switch turns off while the controller keeps the remaining time, ready to resume. Without being told about it, Irrigation Plus would read that as the controller cutting the run short — settling the run as partial, reversing the part of the moisture credit it believed had not been delivered, and then watching the controller resume a zone it had already closed the books on.

Without a paused indicator there is nothing to tell the two apart, and that is exactly what happens: the first pause settles the run, reverses part of the credit, and the zone reads as finished while the controller is still holding water for it. If your controller has a pause button, configure one.

Configure a paused indicator and that is handled: while it reads on, a valve going off is a pause rather than a finish, and the run simply stops accumulating watering time. The panel stops counting down too — a paused run shows its Stop control without a remaining time, because only the controller knows when it will hand the rest back. Run time in this mode is the sum of the watering segments rather than one stretch from the start, so a twenty-minute pause costs nothing — and because the total is persisted, a restart in the middle of a pause still adds up correctly.

An indicator that stops reporting — the usual case being a controller still reconnecting after a Home Assistant restart — counts as paused, not as stopped. Irrigation Plus never reads "cannot see the controller" as "the run ended", so a restart in the middle of a pause keeps the run and picks the water back up when the controller resumes.

A pause holds the whole queue, and the zones behind it keep their place. Irrigation Plus gives every queued zone a deadline by which its turn should have come, so a zone the controller silently drops does not sit claimed forever. That deadline is measured against time the controller spends watering, so it stops while the controller is paused and picks up with the time it had left when the cycle resumes — a long pause cannot write off the zones still waiting behind it. On the dashboard those zones read Queued…, distinct from the paused one, which reads Paused….

A pause is always bounded. Leave the pause timeout at 0 and a generous default backstop applies. This is deliberate: an unbounded pause is harmless to the controller, but a run that never ends holds its zone against any future run, holds the pump, and keeps a moisture credit for water that never fell — so the zone reads as watered while it is dry, and stays that way until someone notices. If you set an On pause timeout script it is called when the bound expires, so you decide what giving up means on your hardware (resume, shut down, clear the queue); the run is settled for what it actually delivered either way.

A worked example (ESPHome) {#batch-example}

The run script receives the plan and queues it. zones is a list, so it is walked with a repeat:

alias: Irrigation - run batch
mode: single
fields:
  zones:
    description: Ordered list of {zone_id, zone_name, duration} from Irrigation Plus.
    required: true
sequence:
  # Start from a known state, or a leftover queue would run alongside this plan.
  - action: esphome.sprinkler_clear_queued_valves
  - repeat:
      for_each: ""
      sequence:
        - action: esphome.sprinkler_queue_valve
          data:
            # Map the Irrigation Plus zone id to the controller's valve number.
            valve_number: ""
            run_duration: ""
  - action: esphome.sprinkler_start_from_queue

The stop script must do both halves:

alias: Irrigation - stop batch
mode: single
sequence:
  - action: esphome.sprinkler_shutdown
  - action: esphome.sprinkler_clear_queued_valves

The optional paused indicator, as an ESPHome template binary sensor:

binary_sensor:
  - platform: template
    name: "Controller paused"
    lambda: |-
      return id(sprinkler_controller).paused_valve().has_value();

Then, per zone: Watering mode = Batch / queue controller, and Confirm entity = that zone's valve switch (switch.<zone>_valve). Durations are sent in each zone's own Duration unit, so set that to whatever your controller expects.

None of this requires ESPHome. Any script that accepts a zones list works, and the paused indicator can be an input_boolean your own automation sets. The mode describes what the entities must mean, not where they come from.

Linked entity {#linked-entity}

Optionally link a Home Assistant switch, valve or input_boolean (helper) entity to a zone (the Classic watering mode). When irrigation fires, the integration will:

  1. Call turn_on on the entity
  2. Wait for the calculated duration (in seconds)
  3. Call turn_off on the entity

This means no automation is needed to control your valve — the integration does it directly. The zone sequencing setting on the When to Water tab controls whether multiple linked zones run in parallel or one after another.

OpenSprinkler stations are the exception. In OpenSprinkler station mode this field holds the station's enabled switch, and the integration never calls turn_on on it — that switch rewrites the station's enabled flag in the controller's configuration and opens no valve. The run is sent as a station run instead, and the three steps above do not apply. A zone left in Classic mode with a station linked has its run refused rather than performed, so the station's configuration is never rewritten by accident.

Tip: Start typing switch., valve. or input_boolean. in the field and all matching entities in your HA instance will appear as autocomplete suggestions. Linking an input_boolean helper is handy when you drive the actual valve from your own automation but still want Irrigation Plus to start/stop it.

If you prefer to keep using automations, simply leave this field empty and listen for the irrigation_plus_start_irrigation_all_zones event, which fires whenever an irrigate schedule runs.

Soil-moisture veto

Two optional per-zone fields let a soil-moisture sensor skip a zone that is already wet enough — without touching the ET calculation.

On an automatic (scheduled) run, if the sensor reads strictly above the threshold, HASI skips that zone and resets its bucket to 0 (re-anchoring the water balance to field capacity). Manual Irrigate Now runs always water. If the sensor is unavailable or non-numeric, the zone waters normally (fail-open) — a dead sensor never silently stops irrigation.

Each skip is recorded in the zone's run history ("Recent runs", persisted across restarts) as a skipped entry, and also fires the irrigation_plus_zone_skipped event for your own logging.

Why reset the bucket instead of carrying the deficit? The bucket is a signed water balance (negative = deficit). A measured-wet zone is at field capacity, i.e. no deficit — so the balance is re-anchored to 0. Carrying an old modeled deficit that the measurement contradicts would make the controller over-water to "catch up" once the veto lifts. This matches FAO-56 (root-zone depletion is bounded at 0 at field capacity), the Extension "checkbook" scheduling method (overwrite the book with the field measurement), and every model-based commercial controller (Rachio, RainMachine, Spruce all re-anchor toward field capacity on a wet signal).

Available actions per zone

On the Zones dashboard, each zone card shows an at-a-glance verdict, a one-line status (bucket and when it was last checked), and the everyday action buttons:

The remaining per-zone tools live under Setup → My Zones:

Weather records, the forecast and the 12-month seasonal outlook are no longer shown per zone — they apply to your whole location, so they now live once on the Weather & Location tab.

Main page: Configuration
Previous: Weather & Location
Next: When to Water