Multi Zone Irrigation - Multi-valve, sensor driven

Universal Smart Irrigation

This hardware-agnostic Home Assistant Blueprint is a professional-grade solution for garden irrigation. It is designed to handle everything from simple flower bed setups to complex multi-zone systems with precision and intelligence.


:sparkles: Key Features

1. Intelligent Queue Management (Mutex-Lock)

  • Feature: Prevents multiple irrigation zones from running at the same time.
  • Benefit: Ensures stable water pressure and protects pumps from overload.
  • Prioritization: Use a configurable “Start Delay” to determine which zone wins the race (e.g., prioritize your vegetable patch over the lawn).

2. Dual-Layer Weather Safety

  • Preventive (Forecast): Checks the rain forecast (mm) before starting. If the predicted rainfall exceeds your threshold, the irrigation is skipped.
  • Reactive (Live Veto): A physical rain sensor (e.g., Netatmo) can immediately abort an active irrigation cycle if it starts raining unexpectedly.

3. Flexible Operation Modes

  • Sequential: Valves in a single zone are processed one after the other (ideal for low water pressure).
  • Parallel: All valves in a zone open simultaneously (ideal for large areas with high-capacity pumps).

4. Advanced Sensor Logic

  • Soil Moisture: Controlled by minimum and maximum thresholds. Irrigation only stops once the target moisture level is reached.
  • Temperature Protection: Optional frost protection—irrigation will not start if the outside temperature is below X °C.

5. Hardware & Pump Support

  • Pump Control: Configurable delay between valve opening and pump start to prevent pressure spikes (water hammer).
  • Hardware Timers: Support for devices with internal safety timers (e.g., Gardena Bluetooth valves).

:hammer_and_wrench: Prerequisites & Setup

To unlock the full potential of this blueprint, the following preparations are required in Home Assistant:

Required (Basic)

  • Global Irrigation Lock: A manually created helper (input_boolean.irrigation_lock) that must be selected in all instances of this blueprint.
  • Zone Switch: One helper (input_boolean.zone_x_active) per zone to enable/disable the automation.
  • Soil Moisture Sensor: An entity providing moisture levels in percentage (%).

Optional (Advanced Features)

  • Weather Forecast: A sensor or helper providing the expected rainfall for the next 24 hours in mm.
    • Note: For services like DWD or Met.no, you may need a small template helper or a helper automation to fetch the forecast data (as modern HA weather entities require a service call).
  • Mobile App: The Home Assistant app installed on your smartphone for push notifications.
  • Physical Rain Sensor: A binary sensor or measurement sensor for the “Live Veto” function.

:warning: Limitations & Important Notes

  1. One Sensor per Zone: The logic is based on one central soil moisture sensor per blueprint instance. If you have 5 valves in one zone, they all react to this single sensor.
  2. Manual Helper Creation: Home Assistant cannot automatically create the required input_boolean (Lock). This must be done manually once.
  3. Weather Data Fetching: This blueprint does not perform an active weather service call. it reads existing sensor data. Users must ensure their rain forecast sensor is updated regularly.
  4. Pump Logic: The pump is defined per zone. If multiple zones share one physical pump, the “Global Lock” is mandatory for the system to function correctly.

:rocket: Installation & Quick Start

  1. Import the blueprint into Home Assistant.
  2. Create your global input_boolean (Lock) for the main water line.
  3. Create a new automation from the blueprint for each zone.
  4. Assign your sensors and valves.
  5. Pro-Tip: Use different Start Delays (0s, 5s, 10s) for your zones to establish a clear hierarchy in the queue.

Notification Preview: Push messages are formatted as: [Zone Name]: [Custom Message] (Soil Moisture: X%). You’ll always know exactly what’s happening in your garden.


Disclosure: This blueprint has been AI generated. I have reviewed the code to the best of my abilities and I deem the code to be good.

Hello TheFreaker68,

sounds promising for my garden irrigation (16 Sonoff ZBV, 2 deep well pumps, maybe Gardena BT stuff later).

I failed importing the blueprint. The message is:

while parsing a block mapping in "", line 12, column 5: zone_name: ^ expected , but found '' in "", line 70, column 7: name: Max humidity (%) ^

Maybe you can help here?

best regards Holger

Hello,
A small error has crept in, but it can be fixed quickly.

The description for the minimum soil moisture ended up under the maximum soil moisture section.
The keyword description: is missing before it.

Here is the corrected YAML file:

# This blueprint is provided as it is. Use at your own risk!
# for full description see https://community.home-assistant.io/t/multi-zone-irrigation-multi-valve-sensor-driven/1006378
blueprint:
  name: "Universal Smart Irrigation"
  description: >
    Contains: Multi-zone, Mutex-Lock, Priority handling, 
    Forecast-Check (DWD), Live-Rain-Veto, valve Parallel/Sequential-Mode, 
    Sun-Trigger for irrigation start time with offset and adaptable notifications.
  domain: automation
  input:
    # --- BASIS & IDENTIFIKATION ---
    zone_name:
      name: Name of the irrigation zone
      default: Garden
    irrigation_switch:
      name: Main switch (Helper)
      description: "A switch to enable or disable the automation. You need to create a switch helper and select it here."
      selector: { entity: { domain: input_boolean } }
    water_source_lock:
      name: Mutual exclusion helper (Lock)
      description: "this switch ensures that no other instance of the blueprint runs in parallel in order to keep the water pressure up."
      selector: { entity: { domain: input_boolean } }
    start_delay:
      name: Start-delay (Priority)
      description: "This helps to set priority to the blueprint instance if more than one runs in parallel. The least delayed runs before the other"
      default: 0
      selector: { number: { min: 0, max: 60, unit_of_measurement: s } }

    # --- BETRIEBSMODUS ---
    valve_mode:
      name: Valve-Mode
      description: "Open the valves in parallel or sequentially?"
      default: "sequential"
      selector:
        select:
          options:
            - label: "Sequential"
              value: "sequential"
            - label: "Parallel"
              value: "parallel"

    # --- Start Mode ---
    start_mode:
      name: Start-Mode
      default: "time"
      selector:
        select:
          options:
            - { label: "Fixed Time", value: "time" }
            - { label: "Sunrise", value: "sunrise" }
            - { label: "Sunset", value: "sunset" }
    fixed_time:
      name: Start time
      default: "06:00:00"
      selector: { time: {} }
    sun_offset:
      name: Sun-Offset (Minutes)
      default: 0
      selector: { number: { min: -120, max: 120, unit_of_measurement: min } }

    # --- Soil humidity ---
    moisture_sensor:
      name: Soil humidity sensor
      selector: { entity: { domain: sensor } }
    min_moisture:
      name: Min humidity (%)
      description: "When the soil reaches the defined percentage the irrigation starts"
      default: 30
      selector: { number: { min: 0, max: 100, unit_of_measurement: "%" } }
    max_moisture: 
      name: Max humidity (%)
      description: "When the soil reaches the defined percentage the irrigation stops"
      default: 80
      selector: { number: { min: 0, max: 100, unit_of_measurement: "%" } }

    # --- Rain Forecast ---
    rain_forecast_sensor:
      name: Rain forecast sensor
      default: []
      selector: { entity: { domain: [sensor, input_number] } }
    rain_threshold:
      name: Rain threshold Forecast (mm)
      default: 2
      selector: { number: { min: 0, max: 50, unit_of_measurement: mm } }

    # --- RAIN-VETO (PHYSICAL SENSOR) ---
    live_rain_sensor:
      name: Live-Rainsensor (Veto)
      description: "Physical Sensor (e.g. Netatmo), which stops the irrigation in case of rain."
      default: []
      selector: { entity: { domain: sensor } }
    live_rain_threshold:
      name: Veto-threshold (mm)
      description: "When the rain sensor value reaches the threshold the irrigation stops."
      default: 0.5
      selector: { number: { min: 0.1, max: 10, step: 0.1, unit_of_measurement: mm } }

    # --- DEVICES ---
    valve_entities:
      name: Ventile
      selector: { entity: { domain: [switch, valve], multiple: true } }
    pump_entity:
      name: Pumpe (Optional)
      default: []
      selector: { entity: { domain: [switch, plug] } }
    max_duration:
      name: Max. Duration (Minutes)
      default: 15
      selector: { number: { min: 1, max: 60, unit_of_measurement: min } }

    # --- NOTIFICATIONS ---
    notification_device:
      name: Notification devices
      default: []
      selector: { device: { filter: [{ integration: mobile_app }] } }
    notify_on_start:
      name: Start Notification
      default: false
      selector: { boolean: {} }
    notify_on_stop:
      name: End Notification
      default: false
      selector: { boolean: {} }
    msg_start:
      name: Start-Message
      default: "Irrigation started."
      selector: { text: {} }
    msg_stop:
      name: End-Message
      default: "Bewässerung beendet."
      selector: { text: {} }

variables:
  valves: !input valve_entities
  v_mode: !input valve_mode
  lock_ent: !input water_source_lock
  pump_ent: !input pump_entity
  moisture_ent: !input moisture_sensor
  max_m: !input max_moisture
  min_m: !input min_moisture
  max_d: !input max_duration
  forecast_ent: !input rain_forecast_sensor
  forecast_thresh: !input rain_threshold
  live_rain_ent: !input live_rain_sensor
  live_threshold: !input live_rain_threshold
  zone: !input zone_name
  notif_device: !input notification_device
  msg_s: !input msg_start
  msg_e: !input msg_stop
  start_mode: !input start_mode
  delay_seconds: !input start_delay

trigger:
  - platform: time
    at: !input fixed_time
    id: "time_trigger"
  - platform: sun
    event: sunrise
    offset: !input sun_offset
    id: "sunrise_trigger"
  - platform: sun
    event: sunset
    offset: !input sun_offset
    id: "sunset_trigger"

condition:
  - condition: state
    entity_id: !input irrigation_switch
    state: "on"
  - condition: template
    value_template: >
      {% if start_mode == 'time' and trigger.id == 'time_trigger' %} true
      {% elif start_mode == 'sunrise' and trigger.id == 'sunrise_trigger' %} true
      {% elif start_mode == 'sunset' and trigger.id == 'sunset_trigger' %} true
      {% else %} false
      {% endif %}
  - condition: template
    value_template: "{{ states(moisture_ent) | float < min_m | float }}"

action:
  - delay: { seconds: "{{ delay_seconds }}" }
  - wait_template: "{{ is_state(lock_ent, 'off') }}"
    # original value 06:00:00
    timeout: "00:15:00" 
  
  - service: input_boolean.turn_on
    target: { entity_id: !input water_source_lock }

  # Start-Meldung
  - if:
      - condition: template
        value_template: "{{ notify_on_start and notif_device != [] }}"
    then:
      - domain: mobile_app
        type: notify
        device_id: !input notification_device
        title: "{{ zone }}"
        message: "{{ msg_s }} (Feuchte: {{ states(moisture_ent) }}%)"

  # IRRIGATION STARTS (If Forecast OK)
  - if:
      - condition: template
        value_template: >
          {% set forecast_val = states(forecast_ent) | float(0) if forecast_ent != [] else 0 %}
          {{ (forecast_ent == [] or forecast_val < forecast_thresh | float) }}
    then:
      - choose:
          # --- MODE: PARALLEL ---
          - conditions: "{{ v_mode == 'parallel' }}"
            sequence:
              - service: homeassistant.turn_on
                target: { entity_id: "{{ valves }}" }
              - if:
                  - condition: template
                    value_template: "{{ pump_ent != [] }}"
                then:
                  - delay: "00:00:03"
                  - service: homeassistant.turn_on
                    target: { entity_id: !input pump_entity }
              
              - wait_for_trigger:
                  - platform: template
                    value_template: "{{ states(moisture_ent) | float >= max_m | float }}"
                    id: "moisture_reached"
                  - platform: template
                    value_template: "{{ live_rain_ent != [] and states(live_rain_ent) | float(0) >= live_threshold | float }}"
                    id: "rain_veto"
                timeout: { minutes: "{{ max_d }}" }
              
              - if:
                  - condition: template
                    value_template: "{{ pump_ent != [] }}"
                then:
                  - service: homeassistant.turn_off
                    target: { entity_id: !input pump_entity }
                  - delay: "00:00:02"
              - service: homeassistant.turn_off
                target: { entity_id: "{{ valves }}" }
              
              - if:
                  - condition: template
                    value_template: "{{ wait.trigger != none and wait.trigger.id == 'rain_veto' }}"
                then: { stop: "Live-Regen Veto" }

          # --- MODE: SEQUENTIAL ---
          - conditions: "{{ v_mode == 'sequential' }}"
            sequence:
              - repeat:
                  for_each: "{{ valves }}"
                  sequence:
                    - service: homeassistant.turn_on
                      target: { entity_id: "{{ repeat.item }}" }
                    - if:
                        - condition: template
                          value_template: "{{ pump_ent != [] }}"
                      then:
                        - delay: "00:00:03"
                        - service: homeassistant.turn_on
                          target: { entity_id: !input pump_entity }
                    
                    - wait_for_trigger:
                        - platform: template
                          value_template: "{{ states(moisture_ent) | float >= max_m | float }}"
                          id: "moisture_reached"
                        - platform: template
                          value_template: "{{ live_rain_ent != [] and states(live_rain_ent) | float(0) >= live_threshold | float }}"
                          id: "rain_veto"
                      timeout: { minutes: "{{ max_d }}" }
                    
                    - if:
                        - condition: template
                          value_template: "{{ pump_ent != [] }}"
                      then:
                        - service: homeassistant.turn_off
                          target: { entity_id: !input pump_entity }
                    - service: homeassistant.turn_off
                      target: { entity_id: "{{ repeat.item }}" }
                    
                    - if:
                        - condition: template
                          value_template: "{{ wait.trigger != none and wait.trigger.id == 'rain_veto' }}"
                      then: { stop: "Live-Regen Veto" }

  # FINISH
  - service: input_boolean.turn_off
    target: { entity_id: !input water_source_lock }

  # Ende-Meldung
  - if:
      - condition: template
        value_template: "{{ notify_on_stop and notif_device != [] }}"
    then:
      - domain: mobile_app
        type: notify
        device_id: !input notification_device
        title: "{{ zone }}"
        message: "{{ msg_e }} (Feuchte: {{ states(moisture_ent) }}%)"


Thx for this Blueprint TheFreaker86. It works perfect.