Keep relay on during firmware update

Hello, I’m using a shelly EM flashed with esphome to control a contactor in my electric panel. I want to delay switching on the contactor in case of a power outage so I set switch restore_mode: ALWAYS_OFF and

  on_boot:
    priority: 300
    then:
      - delay: 30s
      - switch.turn_on: relay

But there is a problem, when updating shelly firmware the relay turns off. I want the relay to be off on boot only after a power outage not a reboot. So i tried this, but doesn’t work:

  on_shutdown:
    priority: 800
    then:
      - lambda: 
          relay->set_restore_mode(SWITCH_ALWAYS_ON);

Here is the full yaml:

esphome:
  name: contor-em
  on_boot:
    priority: 300
    then:
      - delay: 30s
      - switch.turn_on: relay

  on_shutdown:
    priority: 800
    then:
      - lambda: 
          relay->set_restore_mode(SWITCH_ALWAYS_ON);

esp8266:
  board: esp01_1m
  early_pin_init: false

# Enable logging
logger:

# Enable HA API
api:
  encryption:
    key: "redacted"

# Enable OTAs
ota:
  password: "redacted"

# Sync RTC with HA
time:
  - platform: homeassistant
    timezone: Europe/Bucharest

# Wi-Fi Setup
wifi:
  ssid: IOT
  password: redacted

  power_save_mode: LIGHT

  # Enable fallback hotspot (captive portal) in case Wi-Fi connection fails
  ap:
    ssid: shelly-em AP
    password: "12345678"

captive_portal:

i2c:
  sda: GPIO12
  scl: GPIO14

sensor:
  - platform: ade7953_i2c
    irq_pin: GPIO13
    voltage:
      name: Contor Voltage
      id: voltage
    current_a:
      name: Contor Phase 1 Current
      id: current_phase_1
    current_b:
      name: Contor Phase 2 Current
      id: current_phase_2
    active_power_a:
      name: Contor Phase 1 Power
      id: power_phase_1
      filters:
      - lambda: if (x <= 0.0) return 0; else return x * 1.156612516;
    active_power_b:
      name: Contor Phase 2 Power
      id: power_phase_2
      filters:
      - lambda: if (x <= 0.0) return 0; else return x * 1.156612516;
    update_interval: 5s

  - platform: template
    name: Contor Total Power
    id: total_power
    state_class: measurement
    device_class: power
    lambda: return id(power_phase_1).state + id(power_phase_2).state;
    update_interval: 5s
    unit_of_measurement: W

  - platform: total_daily_energy
    name: Contor Phase 1 Energy
    id: energy_phase_1
    power_id: power_phase_1
    filters:
      # Multiplication factor from W to kWh is 0.001
      - multiply: 0.001
    unit_of_measurement: kWh

  - platform: total_daily_energy
    name: Contor Phase 2 Energy
    id: energy_phase_2
    power_id: power_phase_2
    filters:
      # Multiplication factor from W to kWh is 0.001
      - multiply: 0.001
    unit_of_measurement: kWh

  - platform: template
    name: Contor Total Energy
    id: total_energy
    state_class: total_increasing
    device_class: energy
    lambda: return id(energy_phase_1).state + id(energy_phase_2).state;
    unit_of_measurement: kWh

  - platform: template
    name: Contor Total Current
    id: total_current
    state_class: measurement
    device_class: current
    lambda: return id(current_phase_1).state + id(current_phase_2).state;
    update_interval: 5s
    unit_of_measurement: A

  - platform: template
    name: Contor Phase 1 Apparent Power
    id: apparent_power_phase_1
    state_class: measurement
    device_class: power
    lambda: return id(current_phase_1).state * id(voltage).state;
    update_interval: 5s
    unit_of_measurement: VA

  - platform: template
    name: Contor Phase 2 Apparent Power
    id: apparent_power_phase_2
    state_class: measurement
    device_class: power
    lambda: return id(current_phase_2).state * id(voltage).state;
    update_interval: 5s
    unit_of_measurement: VA

  - platform: template
    name: Contor Total Apparent Power
    id: apparent_total_power
    state_class: measurement
    device_class: power
    lambda: return id(apparent_power_phase_1).state + id(apparent_power_phase_2).state;
    update_interval: 5s
    unit_of_measurement: VA

  - platform: template
    name: Contor Phase 1 Power Factor
    id: power_factor_phase_1
    state_class: measurement
    device_class: power_factor
    lambda: return id(power_phase_1).state / id(apparent_power_phase_1).state;
    accuracy_decimals: 3
    update_interval: 5s
    unit_of_measurement: "%"

  - platform: template
    name: Contor Phase 2 Power Factor
    id: power_factor_phase_2
    state_class: measurement
    device_class: power_factor
    lambda: return id(power_phase_2).state / id(apparent_power_phase_2).state;
    accuracy_decimals: 3
    update_interval: 5s
    unit_of_measurement: "%"

  - platform: template
    name: Contor Total Power Factor
    id: total_power_factor
    state_class: measurement
    device_class: power_factor
    lambda: return id(total_power).state / id(apparent_total_power).state;
    accuracy_decimals: 3
    update_interval: 5s
    unit_of_measurement: "%"

status_led:
  pin:
    number: GPIO0
    inverted: yes

switch:
  - platform: gpio
    name: Contactor
    pin: GPIO15
    id: relay
    restore_mode: ALWAYS_OFF

  - platform: restart
    name: Reboot

text_sensor:
  - icon: mdi:application-braces-outline
    platform: version
    name: ESPHome Ver

I think esp82xx based solutions always toggle GPIO’s on reboot if I’m not mistaken. :thinking:

Wouldn’t you want a early pin initialization? :eyes:

Ya, it can be a problem with gpio switches. I think the bigger question is, how to check if the last shutdown was a reset or power outage.

NM, i didnt realize this was a feature of the debug component.

So, you DO want it to come on aftet 30 seconds if it was a legitimate reset but stay off if it wasnt a valid restart?

In the docs, the restart reason is part of the debug component and can be displayed as a text sensor.

You can create that text sensor and then add an automation onto it that checks for a specific restart reason like a power failure and If that condition is True - turn_on:

I want to turn on after 30 seconds only if it was a power failure, not a legitimate reset. It it is a legitimate reset i want to remain allways on.
To make me more clear, the contactor is controlling the electricity in my house, so for safety reason i want to delay powering on the house after a power failure. With the current settings, i can’t update the esphome firmware on shelly because on reboot the contactor will turn off.

If I set restore_mode: RESTORE_DEFAULT_OFF on relay, I can reboot shelly without turning off the contactor, but i don’t know if after a power failure the relay will come on or off and will turn on after delay.

As per esphome docs: Recommended value is false where switches are involved, as these will toggle when updating the firmware or when restarting the device. Defaults to true .

You should just need to add the text sensor for restart reason. Then under on_boot just add a condition that checks if the state of that text sensor is a valid restart or power failure. So, for example after the 30s delay add a condition that checks for valid restart - if true turn on switch

With a condition check, the relay wont turn on if you had a power failure

For the text sensor state, obviously youll have to get the proper verbage esphome uses so it will match. Im not sure what the sensor returns for each restart reason, you just gotta do some testing.

I think you get it wrong. I don’t need to check for a condition on_boot, the relay must turn on indiferently after the 30s delay.
What I want is to not turn off the relay when shelly restarts to apply an update.

What restore_mode on switch entity must be set ? If I set it to ALWAYS_OFF, the relay will turn off before on_boot condition leaving my house without power.

With current setting is like this:

  1. Power Outage → Relay OFF, When power restore, shelly boot up, wait 30s then turn on the relay.
  2. Firmware update or reboot shelly → Relay OFF → Boot → wait 30s → turn on relay

What i want:

  1. The same behaviour
  2. Firmware update or reboot shelly → Relay remains ON → Boot → relay remains ON

You would need to change something externally to do what you want. You either need a contactor that is normally closed (It leaves power on if there is no input) or you need a relay to go between the shelly and the contactor that will supply it with power if the shelly is not doing anything. That way when you want the 30 second delay you apply power to the contactor/relay and the house power is cut off, then you remove it when the timer is up. I am doing something similar with my well pump controller (it gets fussy if it is not off for at least 60 seconds before power is restored) I was looking for how to differentiate between a power loss and an OTA, so this helped me, thanks!