ESPHome Sprinkler controller

Hi!
I am testing the new “Sprinkler Controller” component.
However, I would like to be able to set the timer for each individual valve (zone). Currently I can set the single timer for all 3 zones.
Can you help me please? Thanks.

My config:

sprinkler:
  - id: controller_irrigazione
    main_switch: "Irrigatori"
    auto_advance_switch: "Irrigatori Auto Advance"
    reverse_switch: "Irrigatori Reverse"
    valve_open_delay: 0s
    valves:
      - valve_switch: "Irrigatore 1"
        enable_switch: "Enable Irrigatore 1"
        pump_switch_id: sprinkler_pump_sw
        run_duration: 1s
        valve_switch_id: sprinkler_valve_sw1
      - valve_switch: "Irrigatore 2"
        enable_switch: "Enable Irrigatore 2"
        pump_switch_id: sprinkler_pump_sw
        run_duration: 1s
        valve_switch_id: sprinkler_valve_sw2
      - valve_switch: "Irrigatore 3"
        enable_switch: "Enable Irrigatore 3"
        pump_switch_id: sprinkler_pump_sw
        run_duration: 1s
        valve_switch_id: sprinkler_valve_sw3

switch:
  - platform: gpio
    id: sprinkler_pump_sw
    pin: 12
    inverted: true
    icon: mdi:water-pump
  - platform: gpio
    id: sprinkler_valve_sw1
    pin: 0
    inverted: true
    icon: mdi:sprinkler-variant
  - platform: gpio
    id: sprinkler_valve_sw2
    pin: 2
    inverted: true
    icon: mdi:sprinkler-variant
  - platform: gpio
    id: sprinkler_valve_sw3
    pin: 4
    inverted: true
    icon: mdi:sprinkler-variant
    
number:
  - platform: template
    id: sprinkler_ctrlr_multiplier
    name: "Sprinkler Controller Multiplier"
    icon: mdi:timer-cog-outline
    min_value: 5
    max_value: 120
    step: 5
    mode: slider
    unit_of_measurement: s
    lambda: "return id(controller_irrigazione).multiplier();"
    set_action:
      - sprinkler.set_multiplier:
          id: controller_irrigazione
          multiplier: !lambda 'return x;'    

For each sprinkler, you’ll want to add something like the following to your number: section;

number:
  - platform: template
    id: valve_0
    name: Rear Lawn
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: Min
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id(irrigation_valve_controller_unit_a).valve_run_duration(0);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_valve_controller_unit_a
          valve_number: 0
          run_duration: !lambda 'return x;'

Change valve_0, Rear Lawn, unit_of_measure and devicename (In my case this is irrigation_valve_controller_unit_a) to match corresponding names used in your code.

For refernce, my code is;

# Based on ESPHome Sprinkler Controller - https://esphome.io/components/sprinkler.html

# Change Log
# 2022 09 20 v06
  # - Corrected YAML spacing. Replaced four spaces with two.
  # - Corrected valve number sequenceing in number: template: section.
# 2022 10 03 v07
  # - Added code for Sprinkler Status, Time Remaining and Percentage Complete
  # - Revised numbering of zone_x_name: substitution - 1 to 4 instead of 0 - 3
  # - Renamed zone_x_id: substitution to zone_x_valve_id:
  # - Revised numbering of zone_x_valve_id: substitution - 1 to 4 instead of 0 - 3
  # - Rename lawn_sprinkler_ctrlr_status to valve_status
  # - Add on_boot function to set value of valve_status
  # - Remove the word "Unit" from substitutions
  # - Add "Time Remaining" attribute for all zones
  # - Remove Mulitpier adjustment. as it defaults to 1 at boot
  # - Remove Global for setting run time. Set valve run_duration to 1200
  # - Add "Progress %" attribute for all zones
  # - Add sensor_update_frequency as a substitution for update_interval
  # - Replace occurances of ${devicename_unit_id} with $devicename_unit_id
  # - Replace occurances of ${unit_id} with $unit_id
  # - Replace occurances of ${software_version} with $software_version
  # - Rename Start/Resume to Start/Stop/Resume
  # - Removed Cancel button
  # - Add Timer icons to durations
  # - Add Pause icon to Pause button
# 2022 10 04 v08
  # - Add icons to Time Remaining, Progress and Status sensors
  # - Convert Time Remaining sensor to text_sensor
  # - Convert Percentage Remaining sensor to text_sensor
# 2022 10 09 v09
  # - Convert Percentage Remaining sensor to text_sensor
  # - Add UOM for Number sensors - Set to "Sec"
  # - Remove " Duration (Seconds)" from number template name:
  # - Convert seconds input to minutes
# 2022 10 14 v10
  # - Change update_interval for valve_status_$devicename_unit_id to 'never'. text_sensor updates automatically when data is passed to it through text_sensor.template.publish.
# 2022 10 18 v11
  # - Change update_interval to 120s - test to see if 1s update resolves problem of slow Google response

# Establish Substitutions
substitutions:
### Modify only the following eight lines.
  unit_id: B
  devicename_unit_id: b
  zone_1_name: Rear Lawn
  zone_2_name: North Garden
  zone_3_name: Drip Line A
  zone_4_name: Drip Line B
  software_version: 2022 10 18  v11
  sensor_update_frequency: 120s
# DO NOT CHANGE ANYTHING BELOW THIS LINE ###
  zone_1_valve_id: valve_0
  zone_2_valve_id: valve_1
  zone_3_valve_id: valve_2
  zone_4_valve_id: valve_3
  esphome_name: irrigation-valve-ctrl-unit-$devicename_unit_id
  esphome_platform: ESP8266
  esphome_board: d1_mini
  esphome_comment: Four Valve Irrigation Control - $unit_id
  esphome_project_name: Robert.Four Valve Irrigation Control - $unit_id
  esphome_project_version: Four Valve Irrigation Ctrl - Unit $unit_id, $software_version
  devicename: irrigation_valve_controller_unit_$devicename_unit_id
  upper_devicename: Four Valve Irrigation Ctrl - $unit_id
  uom: Min

#Define Project Deatils and ESP Board Type
esphome:
  name: $esphome_name
  platform: $esphome_platform
  board: $esphome_board
  comment: $esphome_comment
  project:
    name: $esphome_project_name
    version: $esphome_project_version
  on_boot:
    priority: -100
    then:
      # Set default state for Valve Status
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "Idle"
      # Set multiplier to 60, convert seconds to minutes
      - sprinkler.set_multiplier:
          id: $devicename
          multiplier: 60

# WiFi connection, replace these with values for your WiFi.
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# Enable logging
logger:
  
# Enable over-the-air updates.
ota:
  password: !secret ota_password

# Enable Web server.
web_server:
  port: 80

# Sync time with Home Assistant.
time:
  - platform: homeassistant
    id: homeassistant_time

# Text sensors with general information.
text_sensor:
  # Expose ESPHome version as sensor.
  - platform: version
    name: Version

  # Expose WiFi information as sensor.
  - platform: wifi_info
    ip_address:
      name: $devicename IP
    ssid:
      name: $devicename SSID
    bssid:
      name: $devicename BSSID

  # Expose Time Remaining as a sensor.
  - platform: template
    id: time_remaining_$devicename_unit_id
    name: "Time Remaining ($unit_id)"
    update_interval: $sensor_update_frequency
    icon: "mdi:timer-sand"
    lambda: |-
      int seconds = round(id($devicename).time_remaining().value_or(0));
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
        return {
          ((days ? String(days) + "d " : "") +
          (hours ? String(hours) + "h " : "") +
          (minutes ? String(minutes) + "m " : "") +
          (String(seconds) + "s")
          ).c_str()};

  # Expose Progress Percent as a sensor.
  - platform: template
    id: progress_percent_$devicename_unit_id
    name: "Progress % ($unit_id)"
    update_interval: $sensor_update_frequency
    icon: "mdi:progress-clock"
    lambda: |-
      int progress_percent = round(((id($devicename).valve_run_duration_adjusted(id($devicename).active_valve().value_or(0)) - id($devicename).time_remaining().value_or(0)) * 100 / id($devicename).valve_run_duration_adjusted(id($devicename).active_valve().value_or(0))));
      std::string progress_percent_as_string = std::to_string(progress_percent);
      return progress_percent_as_string;

  # Expose Valve Status as a sensor.
  - platform: template
    id: valve_status_$devicename_unit_id
    name: "Status ($unit_id)"
    update_interval: never
    icon: "mdi:information-variant"

# Enable On-Board Status LED.
status_led:
  pin:
    # Pin D4
    number: GPIO2
    inverted: true

sensor:
  # Uptime sensor.
  - platform: uptime
    name: $upper_devicename Uptime

  # WiFi Signal sensor.
  - platform: wifi_signal
    name: $upper_devicename WiFi Signal
    update_interval: 60s

# Enable Home Assistant APIs
api:
  encryption:
    key: !secret api_encryption
  password: !secret api_password

number:
  - platform: template
    id: $zone_1_valve_id
    name: $zone_1_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(0);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 0
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_2_valve_id
    name: $zone_2_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(1);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 1
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_3_valve_id
    name: $zone_3_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(2);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 2
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_4_valve_id
    name: $zone_4_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(3);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 3
          run_duration: !lambda 'return x;'

sprinkler:
  - id: $devicename
    main_switch:
      name: "Start/Stop/Resume ($unit_id)"
      id: main_switch
          
    auto_advance_switch: "Auto Advance ($unit_id)"
    valve_open_delay: 2s
    valves:
      - valve_switch: $zone_1_name
        enable_switch: Enable $zone_1_name
        run_duration: 20s
        valve_switch_id: ${devicename}_1
      - valve_switch: $zone_2_name
        enable_switch: Enable $zone_2_name
        run_duration: 20s
        valve_switch_id: ${devicename}_2
      - valve_switch: $zone_3_name
        enable_switch: Enable $zone_3_name
        run_duration: 20s
        valve_switch_id: ${devicename}_3
      - valve_switch: $zone_4_name
        enable_switch: Enable $zone_4_name
        run_duration: 20s
        valve_switch_id: ${devicename}_4

button:

  - platform: template
    id: sprinkler_pause
    name: "Pause ($unit_id)"
    icon: "mdi:pause"
    on_press:
      then:
        - text_sensor.template.publish:
            id: valve_status_$devicename_unit_id
            state: "Paused"
        - sprinkler.pause: $devicename
        
switch:

# Hidden switches.
# Switches that control sprinkler valve relays
  - platform: gpio
    name: Relay Board Pin IN1
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_1
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "$zone_1_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "Idle"
    pin:
      # GPIO Pin 12
      number: D6
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN2
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_2
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "$zone_2_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "Idle"
    pin:
      # GPIO Pin 13
      number: D7
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN3
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_3
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "$zone_3_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "Idle"
    pin:
      # GPIO Pin 14
      number: D5
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN4
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_4
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "$zone_4_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status_$devicename_unit_id
          state: "Idle"
    pin:
      # GPIO Pin 4
      number: D2
      inverted: true

Perfect thanks I did it!
Can you show me graphically as you see it on lovelace? Thanks

Sure!

…and here’s the YAML if you want to use it.

title: Household Maintenance
views:
  - theme: Backend-selected
    title: Irrigation System
    path: irrigation-system
    type: panel
    badges: []
    cards:
      - type: horizontal-stack
        cards:
          - type: vertical-stack
            cards:
              - type: entities
                entities:
                  - entity: number.front_lawn
                  - entity: number.front_garden
                  - entity: number.south_garden
                  - entity: number.rear_garden
                  - entity: sensor.status_a
                  - entity: sensor.progress_a
                  - entity: sensor.time_remaining_a
                title: Duration - Unit A
                state_color: true
              - type: entities
                entities:
                  - entity: number.rear_lawn
                  - entity: number.north_garden
                  - entity: number.drip_line_a
                  - entity: number.drip_line_b
                  - entity: sensor.status_b
                  - entity: sensor.progress_b
                  - entity: sensor.time_remaining_b
                title: Duration - Unit B
                state_color: true
          - type: vertical-stack
            cards:
              - square: true
                columns: 4
                type: grid
                cards:
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.front_lawn
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.front_garden
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.south_garden
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.rear_garden
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.auto_advance_a
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.start_stop_resume_a
                    show_state: true
                    name: Start / Stop / Resume
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: button.pause_a
                    show_state: false
              - type: markdown
                content: _________
              - square: true
                columns: 4
                type: grid
                cards:
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.rear_lawn
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.north_garden
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.drip_line_a
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.drip_line_b
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.auto_advance_b
                    show_state: true
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: switch.start_stop_resume_b
                    show_state: true
                    name: Start / Stop / Resume
                  - show_name: true
                    show_icon: true
                    type: button
                    tap_action:
                      action: toggle
                    entity: button.pause_b
                    show_state: false
          - type: vertical-stack
            cards:
              - type: entities
                entities:
                  - switch.enable_drip_line_a
                  - switch.enable_drip_line_b
                  - switch.enable_front_garden
                  - switch.enable_front_lawn
                  - switch.enable_north_garden
                  - switch.enable_rear_garden
                  - switch.enable_rear_lawn
                  - switch.enable_south_garden
              - type: entity-filter
                entities:
                  - switch.front_lawn
                  - switch.front_garden
                  - switch.south_garden
                  - switch.rear_garden
                  - switch.rear_lawn
                  - switch.north_garden
                  - switch.drip_line_a
                  - switch.drip_line_b
                state_filter:
                  - 'on'

Many thanks!

I’m just trying to sort out individual sprinkler times myself. Would you mind posting your end result ESPhome code?

EDIT: I have it working but since the default for the valve_run_time is in seconds and the number entity to change it via the UI is in minutes there is a bit of a bug on startup of the ESP. It tries to use the valve run time I have set to 20s but displays in the UI as 20 Mins due to the number entity… Testing will see how this pans out.

Hello @sparkydave, Substitutions are included at the head of the file, one of which is UOM:. In my case, I’ve set this to ‘Min’ as I don’t see value in using seconds. This change only impacts the UI. It doesn’t convert seconds to minutes. There’s a section of code under “esphome:” that executes at startup. In that section I set the multiplier to 60. It’s default is 1.

Here is the section of code that performs the conversion;

  on_boot:
    priority: -100
    then:
      # Set default state for Valve Status
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
      # Set multiplier to 60, convert seconds to minutes
      - sprinkler.set_multiplier:
          multiplier: 60

here is the entire code;

# Based on ESPHome Sprinkler Controller - https://esphome.io/components/sprinkler.html

# Change Log
# 2022 10 24 v01
  # - Initial Build
# 2022 10 26 v02
  # - Added Status Led
  # - Add Previous Button
  # - Add Next Button

# Establish Substitutions
substitutions:
# Modify only the following eight lines.
  software_version: 2022 10 26 v02
  zone_1_name: Front Lawn
  zone_2_name: Front Garden
  zone_3_name: South Garden
  zone_4_name: Rear Garden
  zone_5_name: Rear Lawn
  zone_6_name: North Garden
  zone_7_name: Drip Line A
  zone_8_name: Drip Line B
# DO NOT CHANGE ANYTHING BELOW THIS LINE ###
  zone_1_valve_id: valve_0
  zone_2_valve_id: valve_1
  zone_3_valve_id: valve_2
  zone_4_valve_id: valve_3
  zone_5_valve_id: valve_4
  zone_6_valve_id: valve_5
  zone_7_valve_id: valve_6
  zone_8_valve_id: valve_7
  esphome_name: irrigation-valve-ctrl-unit
  esphome_platform: ESP32
  esphome_board: nodemcu-32s
  esphome_comment: Eight Valve Irrigation Control
  esphome_project_name: Robert.Eight Valve Irrigation Control
  esphome_project_version: Eight Valve Irrigation Ctrl, $software_version
  devicename: irrigation_valve_controller
  upper_devicename: Eight Valve Irrigation Ctrl
  sensor_update_frequency: 10s
  uom: Min

#Define Project Details and ESP Board Type
esphome:
  name: $esphome_name
  platform: $esphome_platform
  board: $esphome_board
  comment: $esphome_comment
  project:
    name: $esphome_project_name
    version: $esphome_project_version
  on_boot:
    priority: -100
    then:
      # Set default state for Valve Status
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
      # Set multiplier to 60, convert seconds to minutes
      - sprinkler.set_multiplier:
          multiplier: 60

# WiFi connection, replace these with values for your WiFi.
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

# Enable logging
logger:
  
# Enable over-the-air updates.
ota:
  password: !secret ota_password

# Enable Web server.
web_server:
  port: 80

# Sync time with Home Assistant.
time:
  - platform: homeassistant
    id: homeassistant_time

# Text sensors with general information.
text_sensor:
  # Expose ESPHome version as sensor.
  - platform: version
    name: Version

  # Expose WiFi information as sensor.
  - platform: wifi_info
    ip_address:
      name: $devicename IP
    ssid:
      name: $devicename SSID
    bssid:
      name: $devicename BSSID

  # Expose Time Remaining as a sensor.
  - platform: template
    id: time_remaining
    name: "Time Remaining"
    update_interval: $sensor_update_frequency
    icon: "mdi:timer-sand"
    lambda: |-
      int seconds = round(id($devicename).time_remaining().value_or(0));
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
        return {
          ((days ? String(days) + "d " : "") +
          (hours ? String(hours) + "h " : "") +
          (minutes ? String(minutes) + "m " : "") +
          (String(seconds) + "s")
          ).c_str()};

  # Expose Progress Percent as a sensor.
  - platform: template
    id: progress_percent
    name: "Progress %"
    update_interval: $sensor_update_frequency
    icon: "mdi:progress-clock"
    lambda: |-
      int progress_percent = round(((id($devicename).valve_run_duration_adjusted(id($devicename).active_valve().value_or(0)) - id($devicename).time_remaining().value_or(0)) * 100 / id($devicename).valve_run_duration_adjusted(id($devicename).active_valve().value_or(0))));
      std::string progress_percent_as_string = esphome::to_string(progress_percent);
      return progress_percent_as_string;

  # Expose Valve Status as a sensor.
  - platform: template
    id: valve_status
    name: "Status"
    update_interval: never
    icon: "mdi:information-variant"

# Enable On-Board Status LED.
light:
  - platform: status_led
    pin: GPIO2
    id: led_status_light
    restore_mode: ALWAYS_ON
    internal: true
    effects:
    - strobe:
        name: "Slow Blink" # 0.1s on, 2s off
        colors:
          - state: true
            brightness: 100%
            duration: 100ms
          - state: false
            duration: 2s
    on_turn_on:
      - light.turn_on:
          id: led_status_light
          effect: "Slow Blink"

sensor:
  # Uptime sensor.
  - platform: uptime
    name: $upper_devicename Uptime

  # WiFi Signal sensor.
  - platform: wifi_signal
    name: $upper_devicename WiFi Signal
    update_interval: 60s

# Enable Home Assistant APIs
api:
#  encryption:
#    key: !secret api_encryption
#  password: !secret api_password

number:
  - platform: template
    id: $zone_1_valve_id
    name: $zone_1_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(0);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 0
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_2_valve_id
    name: $zone_2_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(1);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 1
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_3_valve_id
    name: $zone_3_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(2);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 2
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_4_valve_id
    name: $zone_4_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(3);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 3
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_5_valve_id
    name: $zone_5_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(3);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 4
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_6_valve_id
    name: $zone_6_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(3);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 5
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_7_valve_id
    name: $zone_7_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(3);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 6
          run_duration: !lambda 'return x;'
  - platform: template
    id: $zone_8_valve_id
    name: $zone_8_name
    min_value: 1
    max_value: 60
    step: 1
    unit_of_measurement: $uom
    icon: "mdi:timer-outline"
    mode: box # Defines how the number should be displayed in the UI
    lambda: "return id($devicename).valve_run_duration(3);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: $devicename
          valve_number: 7
          run_duration: !lambda 'return x;'

sprinkler:
  - id: $devicename
    main_switch:
      name: "Start/Stop/Resume"
      id: main_switch
          
    auto_advance_switch: "Auto Advance"
    valve_open_delay: 2s
    valves:
      - valve_switch: $zone_1_name
        enable_switch: Enable $zone_1_name
        run_duration: 20s
        valve_switch_id: ${devicename}_1
      - valve_switch: $zone_2_name
        enable_switch: Enable $zone_2_name
        run_duration: 20s
        valve_switch_id: ${devicename}_2
      - valve_switch: $zone_3_name
        enable_switch: Enable $zone_3_name
        run_duration: 20s
        valve_switch_id: ${devicename}_3
      - valve_switch: $zone_4_name
        enable_switch: Enable $zone_4_name
        run_duration: 20s
        valve_switch_id: ${devicename}_4
      - valve_switch: $zone_5_name
        enable_switch: Enable $zone_5_name
        run_duration: 20s
        valve_switch_id: ${devicename}_5
      - valve_switch: $zone_6_name
        enable_switch: Enable $zone_6_name
        run_duration: 20s
        valve_switch_id: ${devicename}_6
      - valve_switch: $zone_7_name
        enable_switch: Enable $zone_7_name
        run_duration: 20s
        valve_switch_id: ${devicename}_7
      - valve_switch: $zone_8_name
        enable_switch: Enable $zone_8_name
        run_duration: 20s
        valve_switch_id: ${devicename}_8

button:

  - platform: template
    id: sprinkler_pause
    name: "Pause"
    icon: "mdi:pause"
    on_press:
      then:
        - text_sensor.template.publish:
            id: valve_status
            state: "Paused"
        - sprinkler.pause: $devicename

  - platform: template
    id: sprinkler_next
    name: "Next"
    icon: mdi:skip-next
    on_press:
      then:
        - sprinkler.next_valve: $devicename

  - platform: template
    id: sprinkler_previous
    name: "Previous"
    icon: mdi:skip-previous
    on_press:
      then:
        - sprinkler.previous_valve: $devicename

switch:

# Hidden switches.
# Switches that control sprinkler valve relays
  - platform: gpio
    name: Relay Board Pin IN1 - Relay Board A
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_1
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_1_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 19
      number: 19
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN2 - Relay Board A
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_2
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_2_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 21
      number: 21
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN3 - Relay Board A
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_3
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_3_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 22
      number: 22
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN4 - Relay Board A
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_4
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_4_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 23
      number: 23
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN1 - Relay Board B
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_5
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_5_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 27
      number: 27
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN2 - Relay Board B
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_6
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_6_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 26
      number: 26
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN3 - Relay Board B
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_7
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_7_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 33
      number: 33
      inverted: true
  - platform: gpio
    name: Relay Board Pin IN4 - Relay Board B
    restore_mode: RESTORE_DEFAULT_OFF # Prevents GPIO pin from going high during boot
    internal: true # Prevents GPIO switch NAME from showing up in Home Assistant
    id: ${devicename}_8
    on_turn_on:
      - text_sensor.template.publish:
          id: valve_status
          state: "$zone_8_name Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
    pin:
      # GPIO 32
      number: 32
      inverted: true

On a slightly different note; I use a number of “substitutions:” in my code. One of which is “sensor_update_frequency: 10s”. The value reflects how often the display is updated. I’ve set it to 10s which means Time Remaining and Progress % update every 10 seconds. The value can be set to anything you wish. setting it to 1s would cause the UI to update every second instead of every 10 seconds. I’m using 10s as I don’t want the device to flood the network with messages every second for as long as the device is powered on. Ideally I’d like Time Remaining and Progress % to update once per second but only when a sprinkler is active. I haven’t figured out how to do that yet.

1 Like

@rcblackwell I have this problem with this part of code:

text_sensor:
  - platform: template
    id: progress_percent_controller_irrigazione
    name: "Progress % (controller_irrigazione)"
    update_interval: 2s
    icon: "mdi:progress-clock"
    lambda: |-
      int progress_percent = round(((id(controller_irrigazione).valve_run_duration_adjusted(id(controller_irrigazione).active_valve().value_or(0)) - id(controller_irrigazione).time_remaining().value_or(0)) * 100 / id(controller_irrigazione).valve_run_duration_adjusted(id(controller_irrigazione).active_valve().value_or(0))));
      std::string progress_percent_as_string = std::to_string(progress_percent);
      return progress_percent_as_string;

ERROR:

Compiling /data/esp-irrigazione/.pioenvs/esp-irrigazione/src/main.cpp.o
/config/esphome/esp-irrigazione.yaml: In lambda function:
/config/esphome/esp-irrigazione.yaml:133:48: error: 'to_string' is not a member of 'std'
       std::string progress_percent_as_string = std::to_string(progress_percent);
                                                ^
/config/esphome/esp-irrigazione.yaml:133:48: note: suggested alternatives:
In file included from src/esphome/components/api/proto.h:5:0,
                 from src/esphome/components/api/api_pb2.h:5,
                 from src/esphome/components/api/api_connection.h:4,
                 from src/esphome.h:3,
                 from src/main.cpp:3:
src/esphome/core/helpers.h:259:20: note:   'esphome::to_string'
 inline std::string to_string(const std::string &val) { return val; }
                    ^
src/esphome/core/helpers.h:259:20: note:   'esphome::to_string'
*** [/data/esp-irrigazione/.pioenvs/esp-irrigazione/src/main.cpp.o] Error 1
========================= [FAILED] Took 11.29 seconds =========================

And this…

text_sensor:
 - platform: template
    id: time_remaining_controller_irrigazione
    name: "Time Remaining (controller_irrigazione)"
    update_interval: 3s
    icon: "mdi:timer-sand"
    lambda: |-
      int seconds = round(id(controller_irrigazione).time_remaining().value_or(0));
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
        return {
          ((days ? String(days) + "d " : "") +
          (hours ? String(hours) + "h " : "") +
          (minutes ? String(minutes) + "m " : "") +
          (String(seconds) + "s")
          ).c_str()};

ERROR:

INFO Reading configuration /config/esphome/esp-irrigazione.yaml...
ERROR Error while reading config: Invalid YAML syntax:

mapping values are not allowed here
  in "/config/esphome/esp-irrigazione.yaml", line 127, column 7:
        id: time_remaining_controller_irri ... 
          ^

@KameDomotics , Based on the error, I’m assuming use of an ESP32 device. The code you’ve used works with an ESP8266. It doesn’t work with the ESP32. I’ve subsequently changed this section of code so that it also works with the latter. Please see the change as posted in this message.

The spacing in your code is incorrect! The “id:” and all subsequent lines in that section are indented one space to many. Try;

text_sensor:
  - platform: template
    id: time_remaining_controller_irrigazione
    name: "Time Remaining (controller_irrigazione)"
    update_interval: 3s
    icon: "mdi:timer-sand"
    lambda: |-
      int seconds = round(id(controller_irrigazione).time_remaining().value_or(0));
      int days = seconds / (24 * 3600);
      seconds = seconds % (24 * 3600);
      int hours = seconds / 3600;
      seconds = seconds % 3600;
      int minutes = seconds /  60;
      seconds = seconds % 60;
      return {
        ((days ? String(days) + "d " : "") +
        (hours ? String(hours) + "h " : "") +
        (minutes ? String(minutes) + "m " : "") +
        (String(seconds) + "s")
        ).c_str()};

Ok, now it works thanks!

In the “progress_percent” text sensor is there the possibility to put the unit of measure next to the number? Now I see 100 for example, can I put 100%?
I tried with unit_of_measurement:% but it didn’t work.

And also another question: in the “Time remaining” sensor I see the time of each single valve running, can I see the total time of the valves as a sum? (e.g. valve 1 10s, valve 2 10s, valve 3 20s - Time remaining: 40s)

The value of “Progress %” is passed to HA as a chr string, not a number. It’s for this reason adding unit_of_measurement:% to your code didn’t work. Having said that, it is possible to add the “%” sign to the right of the number. In the section tittled " # Expose Progress Percent as a sensor.", you’ll need to add;

  + "%"

to the end of the line reading;

      std::string progress_percent_as_string = esphome::to_string(progress_percent);

The new line would read;

      std::string progress_percent_as_string = esphome::to_string(progress_percent) + "%";

Your may also wish to revise the name to read;

    name: "Progress"

rather than;

    name: "Progress %"

Here’s the full section of the code that outputs the percentage complete value;

  # Expose Progress Percent as a sensor.
  - platform: template
    id: progress_percent
    name: "Progress"
    update_interval: $sensor_update_frequency
    icon: "mdi:progress-clock"
    lambda: |-
      int progress_percent = round(((id($devicename).valve_run_duration_adjusted(id($devicename).active_valve().value_or(0)) - id($devicename).time_remaining().value_or(0)) * 100 / id($devicename).valve_run_duration_adjusted(id($devicename).active_valve().value_or(0))));
      std::string progress_percent_as_string = esphome::to_string(progress_percent) + "%";
      return progress_percent_as_string;

Thank you for the idea! I’ve amended my code accordingly.

I’m sure that’s possible however I haven’t developed code that would do so. Might be an interesting aspect to take on! Give me a week or so to see what I can do.

1 Like

Sorry to dig this thread up again however I have discovered an issue when using these individual valve run times.

If I set one of the valve run times to zero, the sprinkler system fails to run at all when I call the service: esphome.retic_controller_poe_start_full_cycle , being the controller name in ESPhome.

Has anyone else tried this?

Sorry for jumping into this thread a bit late:

I don’t understand how your configuration preserves the valve durations across restarts of the controller. You have the durations in the sprinkler compoment set to 20s, and number components which can be used to change them, but I don’t see any place where the chosen durations are stored and restored on reboot.

Do you have some other mechanism which is handling that?

@kpfleming, Kevin,

Unfortunately I’ve been unable to come up with a solution for that scenario. It’s something I’ve been struggling with for some time. If i ever find a solution, I will post on the forum.

I’m working on something now… if I get it working I’ll PM if you if that’s OK and you can review it.

Would be greatly appreciated. Thanks

HI.
Is it possible, for each sprinkler, to select the days in which they must work? Now I can choose time and duration, can I add days? (for example sprinkler 2 active only on Monday - Wednesday - Friday)
Thanks



sprinkler:
  - id: controller_irrigazione
    main_switch: "Irrigatori"
    auto_advance_switch: "Irrigatori Auto Advance"
    reverse_switch: "Irrigatori Reverse"
    valve_open_delay: 0s
    valves:
      - valve_switch: "Irrigatore 1"
        enable_switch: "Enable Irrigatore 1"
        pump_switch_id: sprinkler_pump_sw
        run_duration: 1s
        valve_switch_id: sprinkler_valve_sw1
      - valve_switch: "Irrigatore 2"
        enable_switch: "Enable Irrigatore 2"
        pump_switch_id: sprinkler_pump_sw
        run_duration: 1s
        valve_switch_id: sprinkler_valve_sw2
      - valve_switch: "Irrigatore 3"
        enable_switch: "Enable Irrigatore 3"
        pump_switch_id: sprinkler_pump_sw
        run_duration: 1s
        valve_switch_id: sprinkler_valve_sw3

switch:
  - platform: gpio
    id: sprinkler_pump_sw
    pin: 12
    inverted: true
    icon: mdi:water-pump
  - platform: gpio
    id: sprinkler_valve_sw1
    pin: 0
    inverted: true
    icon: mdi:sprinkler-variant
  - platform: gpio
    id: sprinkler_valve_sw2
    pin: 2
    inverted: true
    icon: mdi:sprinkler-variant
  - platform: gpio
    id: sprinkler_valve_sw3
    pin: 4
    inverted: true
    icon: mdi:sprinkler-variant
    
number:
  - platform: template
    id: sprinkler_valve_1_timer
    name: "Sprinkler Timer Controller Sw1"
    icon: mdi:timer-outline
    min_value: 5
    max_value: 120
    step: 1
    mode: box
    unit_of_measurement: s
    lambda: "return id(controller_irrigazione).valve_run_duration(0);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: controller_irrigazione
          valve_number: 0
          run_duration: !lambda 'return x;'   

  - platform: template
    id: sprinkler_valve_2_timer
    name: "Sprinkler Timer Controller Sw2"
    icon: mdi:timer-outline
    min_value: 5
    max_value: 120
    step: 1
    mode: box
    unit_of_measurement: s
    lambda: "return id(controller_irrigazione).valve_run_duration(1);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: controller_irrigazione
          valve_number: 1
          run_duration: !lambda 'return x;'  

  - platform: template
    id: sprinkler_valve_3_timer
    name: "Sprinkler Timer Controller Sw3"
    icon: mdi:timer-outline
    min_value: 5
    max_value: 120
    step: 1
    mode: box
    unit_of_measurement: s
    lambda: "return id(controller_irrigazione).valve_run_duration(2);"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: controller_irrigazione
          valve_number: 2
          run_duration: !lambda 'return x;'   

No, ESPHome doesn’t have any concept of timers or calendars. You’d need to create automations in Home Assistant to do what you want here.

Note that you can’t even choose the time for the program to run in ESPHome; only the durations.