Return light state after restart and forcing to stay off returning from a power failure

Hi folks!

I’m on a task to convert almost all my sonoffs and generic Tuya modules from tasmota to esphome as the latter has a centralized control, has support to ESP8266, ESP32 and Beken chips, with reuse of code and other things.

But I’m struggling to have a very nice characteristic of Tasmota: it maintains the lights state (on or off) on reboots / OTA upgrades (can be called “Warm Start”) and switch off lights after returning from a power failure (can be called “Cold Start”).

In other words:

  1. Reboot (pressing button on web page or sending an MQTT message to do so): light state doesn’t change (on stays on, off stays off, without any relay flick or light blink)
  2. After a power failure: light starts off

On Tasmota this behavior is controlled with PowerOnState 0 as can be found here.

It is a very nice behavior because one can leave the house on a power failure and, after power is back, all lights is off, even before HASS server is up and running.

I’ve tried (without any success):

  • restore_mode: RESTORE_DEFAULT_OFF on light platform. No avail. After a reboot if light is on it stays on. After a power on if lights were on, it turns on.

  • using global variables, saving them on shutdown: I could not manage to compile (messed up in accessing light state (on or off) and global variable inside a lambda). I’ve tried a logic in on_boot and on_shutdown, but my knowledge on YAML and ESPHome is limited.

  • on the flick problem I’ve tried early_pin_init: false on esp8266: entry: it works only for switches, not lights. And it solves part of the problem (the relay flicking on reboot or OTA)

Here is my code:


packages:
  device_base: !include device_base-mini.yaml

esp8266:
  board: esp01_1m

globals:
  - id: last_light_state
    type: bool
    restore_value: yes
    initial_value: "false"

binary_sensor:
  - platform: gpio
    name: "${sigla} reset Button"
    pin: GPIO0
    id: button_reset
    internal: true
    filters:
      - invert:
      - delayed_off: 10ms
    on_press:
      - light.toggle:
          id: light_1

  - platform: gpio
    name: "${sigla} Switch 1"
    id: switch_1
    internal: true
    pin:
      number: GPIO4
      mode:
        input: true
        pullup: true
      inverted: true
    on_press:
      then:
        - light.toggle: light_1
    on_release:
      then:
        - light.toggle: light_1

status_led:
  id: led_status
  pin:
    number: GPIO13

output:
  - platform: gpio
    pin: GPIO12
    id: relay_1

light:
  - platform: binary
    name: "${light_1}"
    output: relay_1
    id: light_1

And device_base-mini.yaml:

wifi:
  ssid: ${wifi_ssid_rede_tas}
  password: !secret wifi_password_rede_tas
  ap:
    password: !secret ap_password

logger:

api:
  encryption:
    key: !secret api_encryption_key

ota:
  platform: esphome
  password: !secret ota_password

esphome:
  name: ${sigla}
  friendly_name: "${sigla} ${nome_modulo}"
  comment: "${comentario}"
  area: "IoT"

button:
  - platform: restart
    name: Restart

sensor:
  - platform: wifi_signal
    name: "RSSI"
    update_interval: 60s
    entity_category: "diagnostic"
    id: wifi_rssi
    internal: true
    device_class: "signal_strength"
  - platform: uptime
    name: "Uptime"
    id: up_time
    entity_category: "diagnostic"
  - platform: copy # Reports the WiFi signal strength in %
    source_id: wifi_rssi
    name: "WIFI"
    filters:
      - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
    unit_of_measurement: "%"
    entity_category: "diagnostic"
    device_class: ""

text_sensor:
  - platform: wifi_info
    ip_address:
      name: IP
      entity_category: "diagnostic"
    ssid:
      name: SSID
      entity_category: "diagnostic"

captive_portal:

binary_sensor:
  - platform: status                                 
    name: "Status"

Any clues?

Cheers!

No clue, but there probably is a way for it.
If not, you could check wifi at boot and if it’s not available you could consider it as a power failure and turn off light (I expect Esp recovers much quicker than router).

Thanks for your idea! Unfortunately all my Wifi routers are backed-up with an UPS. On a small light outage wifi routers will not go out, by my modules will.

A try that I’ve made was (in plain words as I can’t express it in YAML):

  • declare a boolean variable (using globals platform, as a guess) named “light_state” that can persist on module’s flash
  • define “light_state” as “false” (ie, light off)
  • on a “gracefully shutdown” (restart and OTA), if light is on then “light_state” should be “true”
  • on boot proceed with two actions in sequence: if “light_state” is “true” then light should be on; define “light_state” with “false”

Light configuration can have restore_mode: ALWAYS_OFF (default value).

But I’m stuck on putting this logic in YAML using (maybe) lambda and so on. My experience is very limited with ESPHome…

Cheers!

Have you tested RESTORE_AND_OFF?

What if you use on_shutdown to save a variable and on_boot to check it?

In on_shutdown you save the variable as “shutdown”, if during boot the variable is shutdown you keep the light in the last state and save the variable as “running”.

If at boot the variable is “running” you keep the light off.

So get UPSs for you esphome-devices as well…
Just kidding. :wink:
If anybody knows the differences Esphome has between soft- and hard boot, it would be nice to have on hands.