Shut down irrigation valves locally on WiFi loss

I have a very simple NodeMCUv1 and 4 relays circuit to send 24v to solenoid valves for irrigation. In order to use it from HA, i used ESPHome. It works well, aside from the fact that sometimes ESPHome loses WiFi connection. The thing is, it is outside of the house in an IP67 box, and it connects to WiFi weakly. Some days it never disconnects, and some other days it disconnects and connects back again several times (every 2-3 minutes) for some hours.

Lately i realized if this WiFi loss happens in the middle of an irrigation schedule (When the valve is open and watering the garden), it doesn’t stop. I waited for some minutes but since it doesn’t reconnect in some minutes i simply pull the power plug of esphome and it shut down the valves as per “Default Off” setting. So, i have two questions:

  1. I use HA_Scheduler or Scheduler Card for Irrigation Schedules. Let’s say it started watering starts on 10:00 and will end at 10:05. However, let’s suppose the Wifi of esphome went off on 10:03 and came back on 10:10. I know that the switch of the valve will not close down on 10:05 since it lost connection. Does that mean when the connection comes back on 10:10 the system will shut down the valve switch or because it simply missed the switch_off command on 10:05 it will continue watering?

  2. How can i add a default max_time window for the relay switch to stay on? So, even the disconnected esphome misses to get the HA switch_off command, it can simply shut down the valve relay switch on a max of 15 minutes locally (The maximum amount of any valve switch that can stay ON is 15 minutes)?

Btw, here is my simple current esphome yaml:

esphome:
  name: esp_sprinkler
  platform: ESP8266
  board: nodemcuv2
  
wifi:
  ssid: "XXXXXXXX"
  password: "XXXXXXX"

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Sprinkler Fallback Hotspot"
    password: "XXXXXXX"

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

web_server:
  port: 80

switch:
  - platform: gpio
    name: "Relay_1"
    pin: D1
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: gpio
    name: "Relay_2"
    pin: D2
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: gpio
    name: "Relay_3"
    pin: D6
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
  - platform: gpio
    name: "Relay_4"
    pin: D7
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF

Thanks…

I too am looking to configure a fail safe timer in esphome that will always run and shut off outputs after an elapsed time.

I think we can modify this esphome irrigation.yaml here:
Irrigation-with-display

It has default timeouts which is stored locally and can be changed with a HA service call. I have to modify it though since i do not have a display and handle the schedules with schedule card.

Btw, using Static IP in yaml decreased the “unavailable WiFi” duration from 1-2 minutes to 1 second only. That looks like a progress on my side. I also used “interlock” on the switches, so only one relay can be ON at a time out of 4 relays and that is handled locally on the esphome (That is needed because of water pressure is enough only for one zone at a time). More details on: Interlocking

I created a service in esphome to turn on the switch and run the failsafe timer. I call the service from HA instead of manipulating the switch. For interlocking, my sprinklers have NO and NC contacts with a common terminal so I interlock with the wiring. Providing the hot to the next output common from the previous output NC. Many output maybe on but only one gets power,

1 Like

What a brilliant idea to handle interlocking on hardware, thanks for that solution. Is it possible for you to post yaml regarding the failsafe timer and the service? Thanks…

Sure, I gave my switch an id.

switch:
  - platform: gpio
    name: "Sonoff 4CH Relay 1"
    pin: GPIO12
    id: relay_1

then under the existing api: section that has password, I added:

api:
  password: 'xxxxxxxx'
  services:
    - service: flame_on
      then:
        - switch.turn_on: relay_1
        - delay: 1800s
        - switch.turn_off: relay_1

I see esphome.basement_fireplace_flame_on as a new service after uploading and call that service from Node-Red instead of the switch.

Using the yaml of bruxy70 on https://github.com/bruxy70/Irrigation-with-display/blob/5de2a4dc2c809aac45e703af50d8c25b36b1e503/irrigation.yaml, i revisedit according to my needs:

esphome:
  name: esp_sprinkler
  platform: ESP8266
  board: nodemcuv2
  
wifi:
  ssid: "XXXXXXX"
  password: "XXXXXX"
  manual_ip:
    static_ip: 192.168.1.XX
    gateway: 192.168.1.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Sprinkler Fallback Hotspot"
    password: "XXXXXXXX"

captive_portal:

# Enable logging
logger:

ota:

web_server:
  port: 80

globals:
 - id: time1
   type: int
   restore_value: no
   initial_value: '600'
 - id: time2
   type: int
   restore_value: no
   initial_value: '600'
 - id: time3
   type: int
   restore_value: no
   initial_value: '1000'
 - id: time4
   type: int
   restore_value: no
   initial_value: '1000'
 - id: remaining_time1
   type: int
   restore_value: no
   initial_value: '600'
 - id: remaining_time2
   type: int
   restore_value: no
   initial_value: '600'
 - id: remaining_time3
   type: int
   restore_value: no
   initial_value: '1000'
 - id: remaining_time4
   type: int
   restore_value: no
   initial_value: '1000'

api:
  services:
    - service: set_time_1
      variables:
        time: int
      then:
        globals.set:
          id: time1
          value: !lambda "return time;"
    - service: set_time_2
      variables:
        time: int
      then:
        globals.set:
          id: time2
          value: !lambda "return time;"
    - service: set_time_3
      variables:
        time: int
      then:
        globals.set:
          id: time3
          value: !lambda "return time;"
    - service: set_time_4
      variables:
        time: int
      then:
        globals.set:
          id: time4
          value: !lambda "return time;"
          
switch:
  - platform: gpio
    name: "Relay_1"
    id: relay1
    pin: D1
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
    interlock: &interlock_group [relay1, relay2, relay3, relay4]
    on_turn_on:
      then:
      - globals.set:
          id: remaining_time1
          value: !lambda "return id(time1);"
  - platform: gpio
    name: "Relay_2"
    pin: D2
    id: relay2
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
    interlock: *interlock_group
    on_turn_on:
      then:
      - globals.set:
          id: remaining_time2
          value: !lambda "return id(time2);"
  - platform: gpio
    name: "Relay_3"
    pin: D6
    id: relay3
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
    interlock: *interlock_group
    on_turn_on:
      then:
      - globals.set:
          id: remaining_time3
          value: !lambda "return id(time3);"
  - platform: gpio
    name: "Relay_4"
    pin: D7
    id: relay4
    inverted: True
    restore_mode: RESTORE_DEFAULT_OFF
    interlock: *interlock_group
    on_turn_on:
      then:
      - globals.set:
          id: remaining_time4
          value: !lambda "return id(time4);"
          
interval:
 - interval: 5s
   then:
     - lambda: |-
         if(id(relay1).state) {
           id(remaining_time1)=id(remaining_time1)-5;
           if(id(remaining_time1)<=0){
             id(relay1).turn_off();
           }
         }
         if(id(relay2).state) {
           id(remaining_time2)=id(remaining_time2)-5;
           if(id(remaining_time2)<=0){
             id(relay2).turn_off();
           }
         }
         if(id(relay3).state) {
           id(remaining_time3)=id(remaining_time3)-5;
           if(id(remaining_time3)<=0){
             id(relay3).turn_off();
           }
         }
         if(id(relay4).state) {
           id(remaining_time4)=id(remaining_time4)-5;
           if(id(remaining_time4)<=0){
             id(relay4).turn_off();
           }
         }

So, each relay has its own default max time set in “globals:” part where time1=remaining_time1 and so on.

If do not set any time and switch the relay on, it counts up to the default max time and switches itself off. If i want to change the time, in HA i use this service:

  - service: esphome.esp_sprinkler_set_time_1
    data:
      time: 450
  - service: switch.turn_on
    entity_id: switch.relay_1

I haven’t tried it yet; i will upload this to esp and see how it goes on…

UPDATE: Works pretty well. You can either use switch off manually from HA anytime you like (Lower time than max timeout obviously), otherwise it will locally count up to the hardcoded timeout and switches itself off. If you change the set_time with HA service than this new time is permanent (up until esp reboot maybe?) Anyhow this looks like it solves my problem. The tolerance of max timeout is -+5 seconds since it counts in 5 sec intervals.