Retain switch states on a power failure

Hi Guys,

So i have ESPHome running on SonOff switches. They use MQTT for communication.
ESPHome was much easier then Tasmota for me.
One issue is when we loose power (that happens more often in India then guys in the west), all my devices default to OFF. (Except devices running stock sonoff devices). This is a problem as say during the night if the fan is on before the power cut, it just stays off even after the power is back and a serious blocker in ‘wife acceptance’.
Just to clarify all my switches and my HassIO reboot. So is there a way out?

Rishab

Yes. Set the restore_mode option for your switches.

  • restore_mode ( Optional ): Control how the GPIO Switch attempts to restore state on bootup. For restoring on ESP8266s, also see esp8266_restore_from_flash in the esphome section.
  • RESTORE_DEFAULT_OFF (Default) - Attempt to restore state and default to OFF if not possible to restore.
  • RESTORE_DEFAULT_ON - Attempt to restore state and default to ON.
  • ALWAYS_OFF - Always initialize the pin as OFF on bootup.
  • ALWAYS_ON - Always initialize the pin as ON on bootup.
2 Likes

So if RESTORE_DEFAULT_OFF is the default state, if should be enabled by default right?
Or this has to be explicitly defined?

Rishab

Not for an ESP8266. See https://esphome.io/components/esphome.html#esphome-esp8266-restore-from-flash

Let me give this a try. :slight_smile:

Nope did not work

Slight modification of @quazzie code

esphome:
  name: ifan02_03
  platform: ESP8266
  board: esp8285
  includes:
    - ifan02.h
  on_boot:
    priority: 800
    # turn off the light as early as possible
    then:
      - light.turn_off: ifan02_03_light

wifi:
  ssid: 
  password: 
  manual_ip:
    static_ip: 
    gateway: 
    subnet: 255.255.255.0
    dns1: 
    dns2: 1.1.1.1

#api:

mqtt:
  broker: 
  username: 
  password: 

logger:

ota:

binary_sensor:
  - platform: gpio
    id: vbutton_light
    pin:
      number: GPIO0
      inverted: True
    on_press:
      then:
        - light.toggle: ifan02_03_light

  - platform: gpio
    id: vbutton_relay_1
    pin:
      number: GPIO9
      inverted: True
    on_press:
      then:
        - switch.toggle: fan_relay1
        - switch.turn_on: update_fan_speed

  - platform: gpio
    id: vbutton_relay_2
    pin:
      number: GPIO10
      inverted: True
    on_press:
      then:
        - switch.toggle: fan_relay2
        - switch.turn_on: update_fan_speed

  - platform: gpio
    id: vbutton_relay_3
    pin:
      number: GPIO14
      inverted: True
    on_press:
      then:
        - switch.toggle: fan_relay3
        - switch.turn_on: update_fan_speed
        
  - platform: gpio
    id: light_button
    pin:
      number: GPIO1
      inverted: True
    on_press:
      then:
        - light.toggle: ifan02_03_light
        
  - platform: gpio
    id: fan_button
    pin:
      number: GPIO3
      inverted: true
    on_multi_click:
      - timing:
        - ON for 0.5s to 2s
        then:
          - fan.turn_off: ifan02_03_fan
      - timing:
        - ON for 050ms to 350ms
        then:
          - fan.turn_on:
              id: ifan02_03_fan
              speed: LOW
      - timing:
        - ON for 050ms to 350ms
        - OFF for 050ms to 200ms
        - ON for 050ms to 350ms
        then:
          - fan.turn_on:
              id: ifan02_03_fan
              speed: MEDIUM
      - timing:
        - ON for 050ms to 350ms
        - OFF for 050ms to 200ms
        - ON for 050ms to 350ms
        - OFF for 050ms to 200ms
        - ON for 050ms to 350ms
        then:
          - fan.turn_on:
              id: ifan02_03_fan
              speed: HIGH
  - platform: status
    name: "ifan02 03 Status"

output:
  - platform: custom
    type: float
    outputs:
      id: fanoutput
    lambda: |-
      auto ifan02_03_fan = new IFan02Output();
      App.register_component(ifan02_03_fan);
      return {ifan02_03_fan};
  - platform: gpio
    pin: GPIO12
    id: light_output

light:
  - platform: binary
    name: "iFan02 03 Light"
    output: light_output
    id: ifan02_03_light

switch:
  - platform: template
    id: update_fan_speed
    optimistic: True
    turn_on_action:
      then:
        - delay: 200ms
        - if:
            condition:
              and:
                - switch.is_off: fan_relay1
                - switch.is_off: fan_relay2
                - switch.is_off: fan_relay3
            then:
              - fan.turn_off: ifan02_03_fan
        - if:
            condition:
              and:
                - switch.is_on: fan_relay1
                - switch.is_off: fan_relay2
                - switch.is_off: fan_relay3
            then:
              - fan.turn_on:
                  id: ifan02_03_fan
                  speed: LOW
        - if:
            condition:
              and:
                - switch.is_on: fan_relay1
                - switch.is_on: fan_relay2
                - switch.is_off: fan_relay3
            then:
              - fan.turn_on:
                  id: ifan02_03_fan
                  speed: MEDIUM
        - if:
            condition:
              and:
                - switch.is_on: fan_relay1
                - switch.is_off: fan_relay2
                - switch.is_on: fan_relay3
            then:
              - fan.turn_on:
                  id: ifan02_03_fan
                  speed: HIGH
        - switch.turn_off: update_fan_speed

  - platform: gpio
    pin: GPIO5
    id: fan_relay1
    restore_mode: RESTORE_DEFAULT_OFF

  - platform: gpio
    pin: GPIO4
    id: fan_relay2
    restore_mode: RESTORE_DEFAULT_OFF

  - platform: gpio
    pin: GPIO15
    id: fan_relay3
    restore_mode: RESTORE_DEFAULT_OFF

fan:
  - platform: speed
    output: fanoutput
    id: ifan02_03_fan
    name: "iFan02 03 Fan"

Did you read the link I posted?

You need to set the option esp8266_restore_from_flash

1 Like

Oops, i am very new to Hassio and Home assistant in general and assumed it was just an option i added to the gpio.
Sorry for that :frowning:
Ok i am flashing it as i speak with the new option added.

So now the state in my lovelace ui is retained.
But device doesn’t seem to retain the state.

Good morning guys.

I’ve been having the exact same issue, only with a WiFi power strip controlled by the Tuya integration.

One of the many reasons (other than tinkering) I setup HA in a Pi 3 was to be able to automate a “return to last state” sort of command/automation for my power strips because the Tuya and Smart Life apps don’t give you that option (Ewelink/Sonoff devices do).

I have 6 of these power strips, and every time there is a power outage, they remain in the “off” state.

Any help with this will be greatly appreciated.

I ended up using Tasmota over ESP Home and an automation that restores the state in HA and life has been better since then :slight_smile:

1 Like

Just to share my settings since I am hesitating to use restore_from_flash due to sonoff s31 logs current/power/voltage too.
This uses an input booleans to detect if I’m away from home (afh) and the switch’s state, it also uses the esphome on_boot function. There is a delay of 10s to compensate if there are multiple power trips and I wasn’t able to play with ‘wait_until’ condition.

  on_boot:
    - priority: -100.0
      then:
        - delay: 10s
        - if:
            condition:
              api.connected:
            then:
             - if:
                condition:
                 and:
                  - binary_sensor.is_off: afh
                  - binary_sensor.is_on: esphome_${device_name}
                then:
                  - switch.turn_on: relay_1
                else:
                  - switch.turn_off: relay_1
                  - homeassistant.service:
                     service: homeassistant.turn_off
                     data:
                      entity_id: input_boolean.esphome_${device_name}
binary_sensor:
  - platform: homeassistant
    entity_id: input_boolean.afh
    id: afh
  - platform: homeassistant
    entity_id: input_boolean.esphome_${device_name}
    id: esphome_${device_name}

On the esphome switch component itself, need to configure an automation that toggles the input boolean.

switch:
  - platform: gpio
    name: '${friendly_name} plug'
    pin: GPIO12
    id: relay_1
    on_turn_on:
     - homeassistant.service:
        service: homeassistant.turn_on
        data:
         entity_id: input_boolean.esphome_${device_name}
    on_turn_off:
     - homeassistant.service:
        service: homeassistant.turn_off
        data:
         entity_id: input_boolean.esphome_${device_name}    

I did not use home assistant automation to avoid it setting the boolean into false state.

Another tip: you can also detect power failure through the uptime component of esphome especially if you have multple esphome around the house.

3 Likes

I had the same problem and I was about to do the same, but I found why it didn’t retain the state and maybe it’s what happened to you, when doing the performance tests after having made the modifications and installed on the device proceeded to change the state of the device and immediately cut off the power, when turning on the power the device had not saved the previous state, what happens is that when making the change of state in the device it takes around 30 to 40 seconds in save the state in memory, so even though it takes a long time to save it during tests in everyday use it is not and it works correctly

esphome:
  name: foco-papas
  
#  on_boot:
#    priority: 800
    # ...
#    then:
#      - light.turn_off: foco_papas

esp8266:
  board: esp01_1m
  restore_from_flash: true #**For me just need to add this line.**

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

I’m trying to retain a value for a switch on esp32 without success while restore_from_flash on esp8266 is working fine. what should I do to write the value in the flash for esp32 devices?

I have been having the same problem for a long time.

I’ve tried both esp8266 and esp32 boards. Tried the esp8266_restore_from_flash with esp8266 and also restore_mode for switch entities on esp8266 and esp32. also I’ve tried restore_value setting with global variables.
Nothing worked properly.
also another parameter related with this is

preferences:
  flash_write_interval: 0s

which makes it write values to flash on every state change.

I found a very effective way of retaining values on technically any entity, however it’s a bit slow.

The solution is to receive the last value from home assistant when the esp device api connects to home assistant. For this to work you need to use the helper entities to hold values and a syncing script on home assistant which sends the retained values to esp home device. on the esp home device you use an interval to detect api connection state changes and when the api is connected, use a homeassistant.service script run command which runs the syncing script on home assistant.

this is my code:

globals:
  - id: api_connection
    type: bool
    restore_value: no
    initial_value: 'false'

interval:
  - interval: 10s
    then:
      - if:
          condition:
            # check last connection state
            - lambda: 'return { (id(api_connection) != true) };'
          then:
            - if:
                condition:
                  api.connected:
                then:
                  # just connected now
                  # request sync from home assistant
                  - lambda: "id(api_connection) = true;"
                  - homeassistant.service:
                      service: script.sync_aquarium_controller
          else:
            - if:
                condition:
                  not:
                    api.connected:
                then:
                  # just disconnected now
                  - lambda: "id(api_connection) = false;"

Also, one other thing to note is that home assistant will loose the latest helper values too if the power is cut without a proper shutdown depending on how recent was the last state change. Anyways the solution to that is a small UPS that triggers a graceful shutdown when the power is abruptly cut.

I hope this helps someone.

2 Likes

It’s working :heart_eyes:

1 Like

Also this is working for me parfect, last status store data need 60 sec that’s it

All - Thank you for this thread, as it helped me get a Sonoff S31 Wi-Fi Smart Plug up and running ESPHome. Since this thread was originally posted, there have been a few tweaks to the general ESPHome yaml config sections. I also had some initial confusion as to exactly how to get the “restore_mode” feature to work correctly on the ESP8266.

I will leave a copy of my ESPHome yaml config here for others who might benefit from it in the future.

EDIT: Updated on 2024-02-21 to work with ESPHome 2024.2.0 due to breaking change for the cse7766 sensor.

esphome:
  name: sonoff-s31
  friendly_name: Sonoff S31

esp8266:
  board: esp01_1m
  restore_from_flash: True  # required on ESP8266 to enable restoring device settings upon restart

preferences:
  flash_write_interval: 5min  # set to 5min to prevent wearing out the onboard flash module too quickly

# Enable logging
logger:
  baud_rate: 0 # (UART logging interferes with cse7766)

# Enable Home Assistant API
api:
  encryption:
    key: "redacted"

ota:
  password: "redacted"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  #ap:
  #  ssid: "Sonoff-S31 Fallback Hotspot"
  #  password: "redacted"

captive_portal:

# Device Specific Config

uart:
  rx_pin: RX
  baud_rate: 4800

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "Sonoff S31 Button"
    on_press:
      - switch.toggle: relay
  - platform: status
    name: "Sonoff S31 Status"

sensor:
  - platform: wifi_signal
    name: "Sonoff S31 WiFi Signal"
    # update_interval: 60s
  - platform: cse7766
    current:
      name: "Sonoff S31 Current"
      accuracy_decimals: 1
      filters:
        - throttle_average: 60s
    voltage:
      name: "Sonoff S31 Voltage"
      accuracy_decimals: 1
      filters:
        - throttle_average: 60s
    power:
      name: "Sonoff S31 Power"
      accuracy_decimals: 1
      id: my_power
      filters:
        - throttle_average: 60s
  - platform: total_daily_energy
    name: "Sonoff S31 Daily Energy"
    power_id: my_power

switch:
  - platform: gpio
    name: "Sonoff S31 Relay"
    pin: GPIO12
    id: relay
    # This next line is necessary to allow the relay to return to its last state after a restart.
    # If no flash data is available, it will leave the relay off.
    restore_mode: RESTORE_DEFAULT_OFF

time:
  - platform: sntp
    id: my_time

status_led:
  pin:
    number: GPIO13
    inverted: True   

Again, thank you all!

2 Likes

When you set the flash_write_interval to 0min the state will be written to flash only when the state changes. When using the device only as a switch normally the number of flash writes will be very low.

Hmmmm… :thinking:

What about the power reporting state data generated by the Sonoff S31 outlet? That was my main concern with wearing out the flash memory, and thus my reason for using the 5min interval. It seemed like a somewhat reasonable compromise in my use-case.