ESPHome Sprinkler controller

Progress bar is actually a slider (mushroom number card) to choose runtime between 1-15 mins

Scheduler is also an add on via HACS

I am looking for some help with the ESPHome Sprinkler Component and based on Robert Blackwell’s yaml, modified to include a pump start relay and 4 valves. I’m about 90% to where I want to be with the project, but I am not able to restore the valve run time settings between reboots.

The relay board is a Lilygo ESP32 T Relay 8. T-Relay 5V 8 Channel Relay – LILYGO®

I have default run_durations set in the valve definitions of the controller. I am able to modify the run durations via HA and the updated run times are maintained… until the device reboots (power failure, forced reboot, etc.). Then the default run durations are restored, not the ones used prior to the reboot.

Any thoughts on what I need to do are welcome. Thanks for your help & suggestions!

Can you post your configuration YAML, at least the sprinkler section?

Absolutely! Here is the sprinkler section.

sprinkler:
  - id: $devicename
    main_switch:
      name: "Start/Stop/Resume"
      id: main_switch
    auto_advance_switch: "Lawn Sprinklers Auto Advance"

    pump_start_pump_delay: 3s
    pump_stop_valve_delay: 3s
    valve_overlap: 3s

    valves:
      - valve_switch: $zone_1_name
        enable_switch: Enable $zone_1_name
        run_duration: 4s
        pump_switch_id: pump_valve_id
        valve_switch_id: ${devicename}_1

      - valve_switch: $zone_2_name
        enable_switch: Enable $zone_2_name
        run_duration: 4s
        pump_switch_id: pump_valve_id
        valve_switch_id: ${devicename}_2
        
      - valve_switch: $zone_3_name
        enable_switch: Enable $zone_3_name
        run_duration: 4s
        pump_switch_id: pump_valve_id
        valve_switch_id: ${devicename}_3
        
      - valve_switch: $zone_4_name
        enable_switch: Enable $zone_4_name
        run_duration: 4s
        pump_switch_id: pump_valve_id
        valve_switch_id: ${devicename}_4

and, if it helps, here is a portion of the number section–the other valves are similar, of course.


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;'

If there’s anything else, please let me know . And thank you in advance for your help!!!

uom is defined to be Min

It appears that you started from an ‘old’ example configuration, which required separate number components to achieve the result. The current version of the sprinkler component has direct support for number components, and I’ve posted an example here.

Using the new features will greatly simplify your configuration and solve the ‘restoration on reboot’ problems.

1 Like

Thanks, I was not aware of the changes. I will study your post & give it a shot. Is your March 1 post sufficient or should I go from your .txt file on March 4?

Either one should be informative; they are fairly close to what I run in production.

Thanks, again! I shall dive in.

I followed your posts and things are working much better now! Changes are saved as expected & kept through reboots & power cycling. Thank you!

There is one “interesting” issue I’m having. It may be a result of setting pump_start_delay, pump_stop_valve_delay, and valve_overlap times (all 3 seconds).

When the controller progress percentage is initially reported for each valve, the percentage reported is 71582788% until the overlapping valve is turned off. It also occurs when first valve–which opens prior to the pump starting, until the pump_start_delay expires. Perhaps an overflow in the value because the time(s) are added together?

The yaml for computing the percentage complete is below.

  - platform: template
    id: progress_percent
    name: $upper_devicename Progress %
    update_interval: $sensor_update_frequency
    icon: "mdi:progress-clock"

    lambda: !lambda |-
      auto ctrl = id($devicename) ;
      auto active = ctrl->active_valve();

      if (!active.has_value()) {
        return std::string("--");
      }
      auto time_remaining = ctrl->time_remaining_active_valve();
      if (!time_remaining.has_value()) {
        return std::string("00");
      }
      auto duration_adjusted = ctrl->valve_run_duration_adjusted(active.value());
      auto percent_complete = 100.0 * (duration_adjusted - time_remaining.value()) / duration_adjusted;
      if (percent_complete > 100) {percent_complete=100;}
      return std::to_string((uint32_t)(100.0 * (duration_adjusted - time_remaining.value())) / duration_adjusted) +"%";
    disabled_by_default: false


Never mind–I had a brain fart & meant to return the percent_complete, not the re-computed value. Better now!

Is it possible to include a sensor that is visible in HA that lets you know the state of a pump start relay? I’d like to add it to my sprinkler dashboard, if possible.

The pump’s yaml in the switch section is below and the pump_valve_id is defined in the substitution section.

Thanks!

switch:
  - platform: restart
    name: "Restart $devicename"
   
#
#   Pump Start Relay
#
  - platform: gpio
    name: Relay 0
    restore_mode: RESTORE_DEFAULT_OFF
    id: pump_valve_id
    on_turn_on:
      - binary_sensor.template.publish:
          id: lawn_high_speed_update
          state: ON
      - binary_sensor.template.publish:
          id: garden_high_speed_update
          state: ON
    on_turn_off:
      - delay: 3500ms  #  Delay for valve stop delay plus a little
      - binary_sensor.template.publish:
          id: lawn_high_speed_update
          state: OFF
      - binary_sensor.template.publish:
          id: garden_high_speed_update
          state: OFF
    pin: GPIO33
    internal: true
1 Like

You shouldn’t run irrigation in the evening FYI. Leaving you lawn wet overnight is a recipe for fungus and disease issues. If you absolutely need to do it twice a day, you should do it early enough that it has time to dry out before it gets dark.

Hi, anyone can assist me with this warning

src/esphome/components/sprinkler/sprinkler.cpp: In member function ‘bool esphome::sprinkler::Sprinkler::is_a_valid_valve(size_t)’:
src/esphome/components/sprinkler/sprinkler.cpp:1030:25: warning: comparison of unsigned expression >= 0 is always true [-Wtype-limits]
return ((valve_number >= 0) && (valve_number < this->number_of_valves()));
~~~~~~~~~~~^~

who can help with the fix?

just post your sprinkler config.

this is my config

# Establish Substitutions
substitutions:
  device_name: irrigation-control
  friendly_name: "Irrigation Control"
  device_platform: ESP32
  device_board: esp32dev
  #device_ip: 192.168.1.42
  device_ip: 192.168.1.50
  sensor_update_frequency: 1s
  sprinkler_name: irrigation_sprinkler_ctrlr


esp32:
#  board: esp32dev
  framework:
    type: esp-idf

packages:
  lan: !include common/device_TTGO_POE.yaml
#  wifi: !include common/device_wifi.yaml
#  sensor_wifi_ip_address: !include common/sensor_wifi_ip_address.yaml      
  device_base: !include common/device_base_ESP32.yaml
  home_assistant_api: !include common/device_api.yaml
  
# Enable logging
logger:

###################################################################################################
#####  I/O expander hub definition
###################################################################################################
mcp23017:
  - id: 'mcp23017_hub'
    address: 0x20
#nodemcu devkit v4.0
i2c:
#  sda: 21
#  scl: 22
  sda: GPIO14  # GPIO 14 (Pin 14) as SDA
  scl: GPIO13  # GPIO 13 (Pin 13) as SCL

  scan: True
  frequency: 200kHz
###################################################################################################

# 10x4 LCD config
display:
  - platform: lcd_pcf8574
    dimensions: 20x4
    address: 0x27
    update_interval: 1s
    lambda: |-
      // set the time and total time remaing display
      if (id(irrigation_sprinkler_ctrlr).time_remaining_active_valve().has_value()) {
      // time left
      auto tl = id(irrigation_sprinkler_ctrlr).time_remaining_active_valve().value();
      auto tl_h = (int) tl/3600;
      auto tl_m = (int) (tl - 3600*tl_h)/60;
      auto tl_s = (int) (tl - 3600*tl_h - 60*tl_m); 
      it.printf(0,2, "Time left:  %02d:%02d:%02d", tl_h, tl_m, tl_s);
      }

      if (id(irrigation_sprinkler_ctrlr).time_remaining_current_operation().has_value()) {
      // total time left
      auto ttl = id(irrigation_sprinkler_ctrlr).time_remaining_current_operation().value();
      auto ttl_h = (int) ttl/3600;
      auto ttl_m = (int) (ttl - 3600*ttl_h)/60;
      auto ttl_s = (int) (ttl - 3600*ttl_h - 60*ttl_m); 
      it.printf(0,3, "Total Time: %02d:%02d:%02d", ttl_h, ttl_m, ttl_s);
      }

      if (id(irrigation_sprinkler_ctrlr).active_valve().has_value()) {
        // the controller is running, get the active valve into running_valve and print it
        auto running_valve = id(irrigation_sprinkler_ctrlr).active_valve().value()+1;
        it.printf(0, 0, "Zone %u active", running_valve);

        if (id(irrigation_progress_percent_valve_num).state == 100) {
         it.printf(16, 0, "%s", id(irrigation_progress_percent_valve).state.c_str());
        } else if (id(irrigation_progress_percent_valve_num).state < 10) {
          it.printf(18, 0, "%s", id(irrigation_progress_percent_valve).state.c_str());
        } else {
          it.printf(17, 0, "%s", id(irrigation_progress_percent_valve).state.c_str());
        }
        it.printf(19, 0,  "%s%%", "");

      } else {
      
        // the controller is NOT running
        // PAUSE
        if (id(irrigation_sprinkler_ctrlr).paused_valve().has_value()) {
          auto paused_valve = id(irrigation_sprinkler_ctrlr).paused_valve().value()+1;
          it.printf(0, 0, "Zone %u paused", paused_valve);
          it.printf(0, 2, "Time left: %s", id(irrigation_zone_time_remaining_sensor).state.c_str());
        } else {
          // IDLE
          it.strftime(0, 0, "%A", id(homeassistant_time).now());	
          it.strftime(12, 0, "%d-%m-%y", id(homeassistant_time).now());
          it.strftime(12, 1,"%H:%M:%S", id(homeassistant_time).now());
          it.printf(0, 3, "Status: %s", id(irrigation_sprinkler_ctrlr_status).state.c_str());
        }
      }

output:
  - platform: ledc
#    pin: GPIO14
    pin: GPIO33
    #id: irrigation_sprinkler_backlight
    id: irrigation_lcd

light:
  - platform: monochromatic
    #output: irrigation_sprinkler_backlight
    output: irrigation_lcd
    name: "Irrigation LCD Display Backlight"
    id: irrigation_light_backlight
    restore_mode: ALWAYS_ON

###################################################################################################
###################################################################################################
# Enable Home Assistant APIs
api:
  reboot_timeout: 0s
  services:
    - service: set_multiplier
      variables:
        multiplier: float
      then:
        - sprinkler.set_multiplier:
            id: irrigation_sprinkler_ctrlr
            multiplier: !lambda 'return multiplier;'
    - service: start_full_cycle
      then:
        - sprinkler.start_full_cycle: irrigation_sprinkler_ctrlr
    - service: start_single_valve
      variables:
        valve: int
      then:
        - sprinkler.start_single_valve:
            id: irrigation_sprinkler_ctrlr
            valve_number: !lambda 'return valve;'
    - service: next_valve
      then:
        - sprinkler.next_valve: irrigation_sprinkler_ctrlr
    - service: previous_valve
      then:
        - sprinkler.previous_valve: irrigation_sprinkler_ctrlr
    - service: shutdown
      then:
        - sprinkler.shutdown: irrigation_sprinkler_ctrlr
    - service: pause
      then:
        - sprinkler.pause: irrigation_sprinkler_ctrlr
    - service: resume
      then:
        - sprinkler.resume: irrigation_sprinkler_ctrlr
    - service: resume_or_full_cycle
      then:
        - sprinkler.resume_or_start_full_cycle: irrigation_sprinkler_ctrlr
    - service: repeat_2
      then:
        - sprinkler.set_repeat:
            id: irrigation_sprinkler_ctrlr
            repeat: 2  # would run three cycles
    - service: repeat_3
      then:
        - sprinkler.set_repeat:
            id: irrigation_sprinkler_ctrlr
            repeat: 3  # would run three cycles

# Main sprinkler code
sprinkler:
  - id: irrigation_sprinkler_ctrlr
    main_switch: "Irrigation Master Run/Stop"
    auto_advance_switch: "Irrigation Zones Auto Advance"
    next_prev_ignore_disabled: true
    reverse_switch: "Irrigation Zones Reverse"
    valve_open_delay: 2s
    valves:
      - valve_switch: "Irrigation Zone 1"
        enable_switch: "Irrigation Zone 1 Enable"
        run_duration: 900s
        valve_switch_id: irrigation_zone_valve_01
      - valve_switch: "Irrigation Zone 2"
        enable_switch: "Irrigation Zone 2 Enable"
        run_duration: 900s
        valve_switch_id: irrigation_zone_valve_02
      - valve_switch: "Irrigation Zone 3"
        enable_switch: "Irrigation Zone 3 Enable"
        run_duration: 900s
        valve_switch_id: irrigation_zone_valve_03
      - valve_switch: "Irrigation Zone 4"
        enable_switch: "Irrigation Zone 4 Enable"
        run_duration: 900s
        valve_switch_id: irrigation_zone_valve_04
      - valve_switch: "Irrigation Zone 5"
        enable_switch: "Irrigation Zone 5 Enable"
        run_duration: 900s
        valve_switch_id: irrigation_zone_valve_05
      - valve_switch: "Irrigation Zone 6"
        enable_switch: "Irrigation Zone 6 Enable"
        run_duration: 900s
        valve_switch_id: irrigation_zone_valve_06

# Valve control outputs config via I/O expander       
switch:
#################################################################################################
####### CONTROL SWITCH
#################################################################################################
  - platform: template
    id: irrigation_sprinkler_ctrlr_run
    name: "Irrigation Controller Run"
    optimistic: true
    on_turn_on:
      - text_sensor.template.publish:
          id: irrigation_sprinkler_ctrlr_status
          state: "Running"
      - sprinkler.resume_or_start_full_cycle: irrigation_sprinkler_ctrlr
      - switch.turn_off: irrigation_sprinkler_ctrlr_pause
      - switch.turn_off: irrigation_sprinkler_ctrlr_stop

  - platform: template
    id: irrigation_sprinkler_ctrlr_stop
    name: "Irrigation Controller Stop"
    optimistic: true
    on_turn_on:
      - text_sensor.template.publish:
          id: irrigation_sprinkler_ctrlr_status
          state: "Stopped"
      - sprinkler.shutdown: irrigation_sprinkler_ctrlr
      - switch.turn_off: irrigation_sprinkler_ctrlr_pause
      - switch.turn_off: irrigation_sprinkler_ctrlr_run

  - platform: template
    id: irrigation_sprinkler_ctrlr_pause
    name: "Irrigation Controller Pause"
    optimistic: true
    turn_on_action:
      - delay: 500ms
      - lambda: |-
          if(id(irrigation_sprinkler_ctrlr_status).state != "Running")
          {
            id(irrigation_sprinkler_ctrlr_pause).turn_off();
          }
      - lambda: |-
          if(id(irrigation_sprinkler_ctrlr_status).state == "Running")
          {
            id(irrigation_sprinkler_ctrlr_status).publish_state("Paused");
            id(irrigation_sprinkler_ctrlr).pause();
          }
    on_turn_off:
      lambda: |-
        if(id(irrigation_sprinkler_ctrlr_status).state == "Paused")
        {
          id(irrigation_sprinkler_ctrlr_status).publish_state("Running");
          id(irrigation_sprinkler_ctrlr).resume();
        } 

  - platform: template
    id: irrigation_sprinkler_ctrlr_resume
    name: "Irrigation Controller Resume"
    optimistic: true
     
#################################################################################################
####### I/0  SWITCH
#################################################################################################
###################################
  - platform: gpio
    id: irrigation_zone_valve_01
    name: "MCP23017 Pin B1"
    pin:
      mcp23xxx: mcp23017_hub
      # Use pin B1
      number: 9
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: true
    internal: true
###################################
  - platform: gpio
    id: irrigation_zone_valve_02
    name: "MCP23017 Pin B2"
    pin:
      mcp23xxx: mcp23017_hub
      # Use pin B2
      number: 10
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: true
    internal: true
###################################
  - platform: gpio
    id: irrigation_zone_valve_03
    name: "MCP23017 Pin B3"
    pin:
      mcp23xxx: mcp23017_hub
      # Use pin B3
      number: 11
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: true
    internal: true
###################################
  - platform: gpio
    id: irrigation_zone_valve_04
    name: "MCP23017 Pin B4"
    pin:
      mcp23xxx: mcp23017_hub
      # Use pin B4
      number: 12
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: true
    internal: true
###################################
  - platform: gpio
    id: irrigation_zone_valve_05
    name: "MCP23017 Pin B5"
    pin:
      mcp23xxx: mcp23017_hub
      # Use pin B5
      number: 13
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: true
    internal: true
###################################
  - platform: gpio
    id: irrigation_zone_valve_06
    name: "MCP23017 Pin B6"
    pin:
      mcp23xxx: mcp23017_hub
      # Use pin B6
      number: 14
      # One of INPUT or OUTPUT
      mode:
        output: true
      inverted: true
    internal: true
###################################

###################################      
  #- platform: template #this switch doesn't work properly. Can pause via HA frontend, but will not resume...  Via Services does work...
  #  id: pause_switch
  #  name: "Pause Irrigation Switch"
  #  turn_on_action:
  #    then:
  #      - sprinkler.pause: irrigation_sprinkler_ctrlr
  #  turn_off_action:
  #    then:
  #      - sprinkler.resume: irrigation_sprinkler_ctrlr    

number:
# Configuration to set multiplier via number
  - platform: template
    id: irrigation_sprinkler_ctrlr_multiplier
    name: "Irrigation Run Duration Multiplier"
    min_value: 1.0
    max_value: 3.0
    step: 0.1
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).multiplier();"
    set_action:
      - sprinkler.set_multiplier:
          id: irrigation_sprinkler_ctrlr
          multiplier: !lambda 'return x;'  
          
# Configure repeat
  - platform: template
    id: irrigation_sprinkler_ctrlr_repeat_cycles
    name: "Irrigation Sprinkler Repeat Cycles"
    min_value: 0
    max_value: 300
    step: 1
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).repeat();"
    set_action:
      - sprinkler.set_repeat:
          id: irrigation_sprinkler_ctrlr
          repeat: !lambda 'return x;'

# Configuration to set valve run duration via number
  - platform: template
    id: irrigation_sprinkler_valve_1_duration
    name: "Irrigation Zone 1 Duration"
    icon: mdi:timer
    unit_of_measurement: Min
    min_value: 1
    max_value: 120
    step: 1.0
    update_interval: $sensor_update_frequency
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(0) / 60;"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_sprinkler_ctrlr
          valve_number: 0
          run_duration: !lambda "return x * 60;"
          
  - platform: template
    id: irrigation_sprinkler_valve_2_duration
    name: "Irrigation Zone 2 Duration"
    icon: mdi:timer
    unit_of_measurement: Min
    min_value: 1
    max_value: 120
    step: 1.0
    update_interval: $sensor_update_frequency
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(1) / 60;"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_sprinkler_ctrlr
          valve_number: 1
          run_duration: !lambda "return x * 60;" 

  - platform: template
    id: irrigation_sprinkler_valve_3_duration
    name: "Irrigation Zone 3 Duration"
    icon: mdi:timer
    unit_of_measurement: Min
    min_value: 1
    max_value: 120
    step: 1.0
    update_interval: $sensor_update_frequency
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(2) / 60;"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_sprinkler_ctrlr
          valve_number: 2
          run_duration: !lambda "return x * 60;" 
          
  - platform: template
    id: irrigation_sprinkler_valve_4_duration
    name: "Irrigation Zone 4 Duration"
    icon: mdi:timer
    unit_of_measurement: Min
    min_value: 1
    max_value: 120
    step: 1.0
    update_interval: $sensor_update_frequency
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(3) / 60;"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_sprinkler_ctrlr
          valve_number: 3
          run_duration: !lambda "return x * 60;"  
          
  - platform: template
    id: irrigation_sprinkler_valve_5_duration
    name: "Irrigation Zone 5 Duration"
    icon: mdi:timer
    unit_of_measurement: Min
    min_value: 1
    max_value: 120
    step: 1.0
    update_interval: $sensor_update_frequency
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(4) / 60;"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_sprinkler_ctrlr
          valve_number: 4
          run_duration: !lambda "return x * 60;"
          
  - platform: template
    id: irrigation_sprinkler_valve_6_duration
    name: "Irrigation Zone 6 Duration"
    icon: mdi:timer
    unit_of_measurement: Min
    min_value: 1
    max_value: 120
    step: 1.0
    update_interval: $sensor_update_frequency
    mode: box
    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(5) / 60;"
    set_action:
      - sprinkler.set_valve_run_duration:
          id: irrigation_sprinkler_ctrlr
          valve_number: 5
          run_duration: !lambda "return x * 60;" 
          
sensor:
### SENSORS
#  - platform: template
#    name: "Cycle Total Time Sensor"
#    icon: mdi:progress-clock    
#    unit_of_measurement: 'Min'
#    accuracy_decimals: 0
#    update_interval: $sensor_update_frequency  
#    lambda: "return id(irrigation_sprinkler_ctrlr).valve_run_duration(0)/60 + id(irrigation_sprinkler_ctrlr).valve_run_duration(1)/60;"

#  # Expose Progress Percent as a sensor as int
  - platform: template
    id: irrigation_progress_percent_valve_num
    name: "Irrigation Zone Progress Number"
    unit_of_measurement: '%'
    accuracy_decimals: 0
    update_interval: $sensor_update_frequency
    icon: "mdi:progress-clock"
    lambda: |-
      if (id(irrigation_sprinkler_ctrlr).active_valve().has_value()) {
        int valve_progress_percent_int = round(((id(irrigation_sprinkler_ctrlr).valve_run_duration_adjusted(id(irrigation_sprinkler_ctrlr).active_valve().value_or(0)) - id(irrigation_sprinkler_ctrlr).time_remaining_active_valve().value_or(0)) * 100 / id(irrigation_sprinkler_ctrlr).valve_run_duration_adjusted(id(irrigation_sprinkler_ctrlr).active_valve().value_or(0))));
        return valve_progress_percent_int;
      } else {
        int valve_progress_percent_int = 0;
        return valve_progress_percent_int;
      }

text_sensor:
    - platform: template
      id: irrigation_sprinkler_ctrlr_status
      name: "Irrigation Sprinklers Status"
      update_interval: $sensor_update_frequency

    - platform: template
      name: "Irrigation Zone Active Sensor"
      id: irrigation_zone_active_sensor
      #unit_of_measurement: ''
      #accuracy_decimals: 0
      #Valves are numbered from 0-7 internally which is an issue when displaying !
      lambda: |-
        if(id(irrigation_sprinkler_ctrlr_status).state == "Stopped")
        {
          int zone_active = id(irrigation_sprinkler_ctrlr).active_valve().value_or(NAN);
          std::string zone_active_as_string = esphome::to_string(zone_active);
          return zone_active_as_string;
        }
        else
        {
          int zone_active = id(irrigation_sprinkler_ctrlr).active_valve().value_or(NAN) + 1;
          std::string zone_active_as_string = esphome::to_string(zone_active);
          return zone_active_as_string;
        }
      update_interval: $sensor_update_frequency

#  # Expose Valve Progress Percent as a sensor string.
    - platform: template
      id: irrigation_progress_percent_valve
      name: "Irrigation Zone Progress"
      #unit_of_measurement: '%'
      #accuracy_decimals: 0
      update_interval: $sensor_update_frequency
      icon: "mdi:progress-clock"
      lambda: |-
        if (id(irrigation_sprinkler_ctrlr).active_valve().has_value()) {
          int valve_progress_percent = round(((id(irrigation_sprinkler_ctrlr).valve_run_duration_adjusted(id(irrigation_sprinkler_ctrlr).active_valve().value_or(0)) - id(irrigation_sprinkler_ctrlr).time_remaining_active_valve().value_or(0)) * 100 / id(irrigation_sprinkler_ctrlr).valve_run_duration_adjusted(id(irrigation_sprinkler_ctrlr).active_valve().value_or(0))));
          std::string valve_progress_percent_as_string = esphome::to_string(valve_progress_percent);
          return valve_progress_percent_as_string;
        } else {
          int valve_progress_percent = 0;
          std::string valve_progress_percent_as_string = esphome::to_string(valve_progress_percent);
          return valve_progress_percent_as_string;
        }

# # Expose Zone Time Remaining
    - platform: template
      name: "Irrigation Zone Time Remaining Sensor"
      id: irrigation_zone_time_remaining_sensor
      icon: mdi:progress-clock    
      #unit_of_measurement: 'Min'
      #accuracy_decimals: 0
      update_interval: $sensor_update_frequency
      lambda: |-
        if(id(irrigation_sprinkler_ctrlr_status).state != "Paused")
        {
          int seconds = round(id(irrigation_sprinkler_ctrlr).time_remaining_active_valve().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 ? std::to_string(days) + "d " : "") +
            (hours ? std::to_string(hours) + "h " : "") +
            (minutes ? std::to_string(minutes) + "m " : "") +
            (std::to_string(seconds) + "s")
            ).c_str()};
        }
        else
        {
          return {};
        }

### Input to managet the display backlight
    - platform: homeassistant    
      #id: irrigation_display_sprinkler_backlight
      id: irrigation_display_backlight
      entity_id: input_number.irrigation_backlight_level
      internal: true
      on_value:
        then:
          #- output.turn_on: irrigation_sprinkler_backlight
          #- output.set_level:
          #    id: irrigation_sprinkler_backlight
          - output.turn_on: irrigation_lcd
          - output.set_level:
              id: irrigation_lcd
              level: !lambda |-
                return atoi(id(irrigation_display_backlight).state.c_str()) / 100.0;
              #return atoi(id(irrigation_display_sprinkler_backlight).state.c_str()) / 100.0;

    - platform: homeassistant
      id: homeassistant_irrigation_countdown
      entity_id: timer.irrigation_countdown
      attribute: finishes_at
      internal: true

# Time source config
time:
  - platform: homeassistant
    id: homeassistant_time
    timezone: Europe/Rome

binary_sensor:
  - platform: gpio
    name: "Irrigation Button Run" #MCP23017 Pin A1
    id: irrigation_button_1 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 1
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    internal: true
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        #- delay: 10000ms
        #- sprinkler.resume_or_start_full_cycle: irrigation_sprinkler_ctrlr
        - switch.turn_on: irrigation_sprinkler_ctrlr_run

  - platform: gpio
    name: "Irrigation Button Stop" #MCP23017 Pin A2
    id: irrigation_button_2 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 2
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        #- sprinkler.shutdown: irrigation_sprinkler_ctrlr
        - switch.turn_on: irrigation_sprinkler_ctrlr_stop

  - platform: gpio
    name: "Irrigation Button Previous Valve" #MCP23017 Pin A3
    id: irrigation_button_3 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 3
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    internal: true
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        - sprinkler.previous_valve: irrigation_sprinkler_ctrlr

  - platform: gpio
    name: "Irrigation Button Next Valve" #MCP23017 Pin A4
    id: irrigation_button_4 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 4
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    internal: true
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        - sprinkler.next_valve: irrigation_sprinkler_ctrlr

  - platform: gpio
    name: "Irrigation Button Resume" #MCP23017 Pin A5
    id: irrigation_button_5 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 5
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    internal: true
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        #- sprinkler.shutdown: irrigation_sprinkler_ctrlr
        - switch.turn_on: irrigation_sprinkler_ctrlr_resume

  - platform: gpio
    name: "Irrigation Button Pause" #MCP23017 Pin A6
    id: irrigation_button_6 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 6
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    internal: true
    on_click:
    - min_length: 50ms
      max_length: 350ms
      then:
        #- sprinkler.pause: irrigation_sprinkler_ctrlr
        - switch.turn_on: irrigation_sprinkler_ctrlr_pause

  - platform: gpio
    name: "Irrigation Control - Rain Sensor" #MCP23017 Pin A7
    id: irrigation_button_7 #push button on front of controller enclosure
    pin:
      mcp23xxx: mcp23017_hub
      number: 7
      # One of INPUT or OUTPUT
      mode:
        input: true
        pullup: true
      inverted: true
    internal: false
    device_class: moisture
    #filters:
    #  - delayed_on: 500s
    #on_state:
    #  then:
    #    - lambda: |-
    #        if (id(irrigation_button_5).state) {
    #        } else {
    #        }

you need to format the code when posting. Use 3x ``` before and after code like this.

or use the button here.

done, sorry

It’s a warning, not an error, you can ignore it. The compiler is just indicating that the code could be improved if desired.

Thank you @kpfleming , that means that the chance should be performed on the sprinkler.cpp, right? That’s something I should report in github to the dev?