ESPHome Sprinkler controller

I am trying to make a single-valve sprinkler controller to work on the same way as a multi-valve controller, but not getting closer.

I am just wondering, is it on purpose/intentional/by design that it is not possible to use " sprinkler.start_full_cycle" or " sprinkler.resume_or_start_full_cycle" actions when there is only a single valve under a controller?

Following up on this it also looks like adding a main_switch is not possible to a single valve controller, hence (is it a consequence?) the controller multiplier doesn’t work either (doesn’t even get exposed to HA).

As soon as I am adding a second valve to the controller config with a main_switch and auto_advance_switch (which seems mandatory for multi-valve controllers) the multiplier gets exposed immediately and the controllers starts behaving as expected.

However, I don’t need the second valve on that controller. Obviously I can have a dummy valve hanging around there as a workaround, but is this intentional for some reason or could that be a bug in esphome?

Hello @febalci Great job!

I’m trying to replicate your code in my 8-sector irrigation project, but I can’t get the ESP32 to return to the previous irrigation state when there’s a power outage. When it’s turned on, it doesn’t resume from where it left off, but rather starts the irrigation from the beginning. I copied your entire code without any modifications. Could it be because I’m using an ESP32 board?

Hi @joaopedros2 ,

maybe what i wrote was confusing; the code doesn’t save the state to resume where it was left off; my intention was to stop sprinklers when there is a power outage, and keep the valves closed when the power comes back on. Just save the run durations and and multiplier values and restore these values back when the power comes back on, so that it can continue the next cycle as i intended.

Btw, if i am not mistaken, i remember something like “esp8266_restore_from_flash : true” doesn’t work with ESP32 anyway.

1 Like

thank you for your answer. What can be done to resume irrigation in the event of a power failure?

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.

Thanks for all the info and just if someone finds this thread, here is how I solved the challenge regarding the update_interval:

First set the “update_interval” of both sensors to a high number, e.g. 10000s (I haven’t tested “never”)

Then create a “binary_sensor template” in esphome

binary_sensor:
  - platform: template
    name: "${friendly_name} High Speed Update"
    id: high_speed_update
    on_state:
      - lambda: !lambda |-
          // This is to revert back to the default update interval when needed
          static uint32_t default_time_interval = id(time_remaining).get_update_interval();
          static uint32_t default_percent_interval = id(progress_percent).get_update_interval();

          // 1000 = 1 secs = New "high speed" interval
          int update_time_interval = x ? 1000 : default_time_interval;
          int update_percent_interval = x ? 1000 : default_percent_interval;

          id(time_remaining).set_update_interval(update_time_interval);
          id(progress_percent).set_update_interval(update_percent_interval);

          id(time_remaining).call_setup();
          id(progress_percent).call_setup();

You can also set the parameter “internal” to true to hide it from HA. I haven’t done that to easily see that it is working :slight_smile:

Finally, in the “on_turn_on” and on “on_turn_off” actions of the valves switch the binary_sensor:

switch:
  - platform: gpio
    pin: GPIO22
    id: relay01
    restore_mode: RESTORE_DEFAULT OFF
    on_turn_on:
      - binary_sensor.template.publish:
          id: high_speed_update
          state: ON
      - text_sensor.template.publish:
          id: valve_status
          state: "Drip Active"
    on_turn_off:
      - text_sensor.template.publish:
          id: valve_status
          state: "Idle"
      - binary_sensor.template.publish:
          id: high_speed_update
          state: OFF

I found this piece of code in this post: Dynamically change update_interval - #10 by 0x3333 provided by @0x3333

1 Like

@derjoerg , thanks for the code snippit. I’ve added it to my code. Works great.

My sprinkler is super simple, a single relay as I have a small garden. I use the scheduler add on for timing.

For anyone wanting to use. Some lambda code taken from other posts and consolidated:

switch:
  - platform: gpio
    name: "Sprinkler Switch"
    id: sprinkler_switch
    pin: D1
    internal: True
    on_turn_off:
      then:
      - lambda: |-
          id(time_remaining) = 0;

sensor:
  - platform: homeassistant
    id: runtime_mins
    unit_of_measurement: "min"
    accuracy_decimals: 0
    entity_id: input_number.sprinkler_runtime 
    on_value:
      - sprinkler.set_valve_run_duration:
          id: garden_sprinkler_ctrlr
          valve_number: 0
          run_duration: !lambda "return x * 60;"

sprinkler:
  - id: garden_sprinkler_ctrlr
    valves:
      - valve_switch: "Garden Sprinkler"
        run_duration: 300s  # default max
        valve_switch_id: sprinkler_switch

text_sensor:
  - platform: template
    id: time_remaining
    name: "Time Remaining"
    update_interval: 5s
    icon: "mdi:timer-sand"
    filters:
      - lambda: |-
          static std::string last;
          if (x == last)
            return {};
          last = x;
          return x;
    lambda: !lambda |-
      int seconds = round(id(garden_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 ? String(days) + "d " : "") +
            (hours ? String(hours) + "h " : "") +
            (minutes ? String(minutes) + "m " : "") +
            (String(seconds) + "s")
             ).c_str()};

View in HA (schedules currently disabled as winter here!)

@chimeranzl, can you share some details on the Scheduler part in your UI? how it works? also, what is the progress bar?

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