Solution for Keeping Devices Awake During OTA Updates in ESPHome

Yeah, that’s the issue at hand. The device wakes up for a few seconds once or twice a day, and I can’t always be there waiting for the alarm to go off :wink: :wink:.

The crux of the matter is being able to trigger the deep_sleep.prevent precisely when the device wakes up to do its tasks and checks whether there’s an OTA update available from Home Assistant. That way, I can perform the update seamlessly. Otherwise, I’d have to manually connect each device to my computer for the update via cable.

What do you think of this approach? Hopefully, it’ll get us out of this bind! :wink: :wink:.

1 Like

In my setup, I have Home Assistant running in a Docker container, and ESPHome in another Docker container. So, I’m not sure if I could implement your suggestion as it is.

Also, even if I could run commands from the terminal, the question arises: how can I send the update if the device is in deep sleep mode?

The crux of the matter is being able to notify my ESPs from Home Assistant that there’s an available OTA update and prevent them from going into deep sleep mode, so I can easily perform the update.

Do you think there’s a way to tackle this from this perspective? I appreciate any suggestions you can offer. Thanks for your help!

Yes, I have considered the option of waiting longer, but until when? I worry that it might take longer than expected due to various factors, such as network issues, the machine running Home Assistant, the Docker container itself, or any other unforeseen circumstances. In that case, wouldn’t just that delay be enough?

Umm, interesting! I hadn’t understood it that way. And it works! Here’s the corrected code with your suggestion.


  on_boot:
    then:
      - wait_until:
          condition:
            lambda: 'return id(ota_update_available).state != "";'

It seems like a cleaner solution. Can anyone tell me if it’s more optimal in terms of power consumption? At first glance, from a code perspective, it seems much cleaner and more elegant. Thank you very much for your help!

1 Like

Well, it seems like we were already on the same page from the get-go! The use of ‘on_boot’ was present in the code since the first message I shared. Is there something I’m missing or not interpreting correctly? Looking forward to your clarification!

1 Like

I cannot really recommend anything better.

My last idea requires at least physical access to each device: some button or jumper on the deep sleep pin. At least you don’t need to plug it out, but you’ll need the extra hardware as part of your build, plus physical access.

Hey buddy!

What an amazing project you’ve pulled off! Seriously, I’m blown away by what you’ve achieved. I’ve already added it to my favorites and my “must-do” list.

Honestly, I feel like a total newbie compared to you. I’ve only been tinkering with ESPHome for three weeks, and to be honest, I had no previous knowledge of electronics or microcontroller programming whatsoever. My first steps in this world were with Arduino IDE, but really, it’s nothing compared to what you’ve accomplished. I’m also just scratching the surface with home automation, although I’ve started setting it up in my house with Home Assistant a couple of months ago. It’s such an exciting field!

So for now, I’ll take it slow before attempting something as ambitious as yours. But rest assured, you’ve inspired me a ton, and I hope to someday reach your level.

Thanks a million for sharing your project and for all the inspiration you’ve given me!

1 Like

Actually, one more idea: instead of just configuring deep sleep, just control when you put it into deep sleep using the enter call directly. Basically: ESP boots, check an HA input boolean for the device that you set to indicate you’d like to push an update. If not set, go into deep sleep. Otherwise, just wait (maybe with a timeout to eventually go into deep sleep in this case too). At the same time, you can have an automation in HA to wait for the device to connect via the API. When it does, and your input boolean is set, send a push notification to say it’s online and ready for an update. Unset the input boolean (maybe one can automatically do that).

1 Like

Thank you very much for participating. To the extent possible, I am looking for a solution that can be managed remotely without the need to access the devices physically. This is important not only for my current project of watering plants but also for others that I am almost finishing, such as lighting for my closets, and more that I have in mind to do. They won’t always be easily accessible, so a remote solution would be ideal.

If I’m not mistaken, that’s practically the approach I’m taking, at least the first part, which is the one that was driving me crazy: waking up and checking with HA if there’s a pending OTA update. If there isn’t, it continues its scheduled cycle of going back to sleep. But if there is, then it skips going back to sleep.

True. I suppose the difference is the enter vs prevent calls. Practically the two solutions may not really differ.

I agree from a power optimisation perspective there may be an opportunity to sleep sooner than the scheduled time, as soon as the OTA check is complete (assuming that is the only task we are waiting for).

It would look something like adding this:

    - deep_sleep.enter:
        id: deep_sleep_1
        sleep_duration: 20s

At the end of this:

You could use substitutions so you don’t need to replicate the sleep duration value in two places ( sleep_duration:)

I think this should result in the esp typically being awake for a much shorter time that the scheduled 60s. Proabably more like 10 or 20.
So you’re radically reducing “awake time power consumption” (which may or may not make much difference, depending on the sleep/wake cycle of your project).

1 Like

To clarify my suggestion, after you prevent deep sleep have the ESPhome device call another home assistant service which runs esphome via command line to start the OTA update. This service/script should clear the ‘ota_update_available’ after the OTA is finished to prevent a loop. This way the esp wakes up from deep sleep, stays awake when an update is available, initiates the update automatically, then resumes normal operation after the OTA reboot. Otherwise esp could stay awake and drain battery if the operator gets distracted.

Code snippet from original post.
..........
          then:
            - logger.log: "OTA Update Available!"
            # I test using the onboard led.
            - light.turn_off: light_esp
            - delay: 500ms
            - light.turn_on: light_esp
            # Prevent the ESP32 from going to sleep
            - deep_sleep.prevent: deep_sleep_1
            #Add second service call here
            - homeassistant.service:
                service: 'shell_command.esphome_ota_device'
          else:
            - logger.log: "no OTA Update Available!"
            # I test using the onboard led.
            - light.turn_off: light_esp
...........

That is pretty cool!

Wow, I really dig this idea! Thanks a bunch, amigo! I’ll definitely work on it and update my code. Thanks again for your help! :tokyo_tower:

Following with interest! So what was the final code?

Inquiry minds are eager to see what you guys landed on in a complete config file.

None of my esphome devices are battery powered, so I never actually implemented this myself.

There is a new http ota update request component that provides another option.

1 Like

How I Solved the OTA Update Problem for My Deep Sleep Devices

Alright, my friends, today I’m gonna tell you how I solved the OTA update issue for all my ESPHome devices that are in deep sleep 95% of the time. :sleeping: If you’ve ever had to wait for a device to wake up just to do an update, you know how annoying it can be. But don’t worry! I’ve created a super simple and automatic system so that my devices are ready to update whenever needed, without the hassle of trying to catch them while they’re in deep sleep mode. :sunglasses:


1. Code on the ESPHome device

First things first, we need to use substitutions in the ESPHome code to make things more flexible and reusable. This allows you to define variables at the top of the file and then use them throughout the rest of your YAML. It’s like creating a cheat sheet so you don’t have to keep typing the same stuff over and over. :muscle:

Example of code with substitutions in ESPHome:

At the top of your YAML file, you define your variables, like the device name, light pin, etc. Then you use those variables throughout the code.

substitutions:
  esp_name: "Living room plants"
  esp_id: "living_room_plants"
  pin_led_board: GPIO2  # Pin for the status light

# Include the common OTA code
packages:
  ota: !include common/ota_update.yaml

Here, we define variables like esp_name, esp_id, and pin_led_board so you can reuse them everywhere in the file. It keeps the code neat, easy to read, and prevents repetitive stuff. Like your grandma’s recipes, but way more efficient! :plate_with_cutlery:


2. Code for OTA updates (ota_update.yaml)

Now, let’s talk about that ota_update.yaml file you’ve got, which contains all the OTA magic :magic_wand:. Thanks to substitutions, we don’t need to rewrite the same thing for every device. This file becomes like your go-to script for all your ESPHome devices, all ready to be reused. No need to reinvent the wheel, you feel me? :sunglasses:

Example of ota_update.yaml:

# Status light to show if OTA update is pending
output:
  - platform: ledc
    pin: ${pin_led_board}
    id: ${esp_id}_led_board

light:
  - platform: monochromatic
    output: ${esp_id}_led_board
    name: "${esp_name} Status Light"
    id: ${esp_id}_light_board
    effects:
      - pulse:
          name: "Fast Pulse"
          transition_length: 0.5s
          update_interval: 0.5s
          min_brightness: 0%
          max_brightness: 100%

switch:
  # Switch to tell us if there's an OTA update pending
  - platform: template
    id: ${esp_id}_ota_update
    name: "${esp_name} OTA - Prepare update"
    icon: "mdi:bed"
    lambda: |-
      if (id(${esp_id}_ota_update_available).state == "on") {
        return true;
      } else {
        return false;
      }
    optimistic: true
    on_turn_on:
      - logger.log: "Switch turned on"
    on_turn_off:
      - logger.log: "Switch turned off"

text_sensor:
  # Text sensor to show the OTA status
  - platform: template
    id: ${esp_id}_ota_status
    name: "${esp_name} OTA - Status"
    icon: mdi:list-status

  # Receive the signal from Home Assistant and handle the deep sleep state
  - platform: homeassistant
    name: "${esp_name} OTA Update Available"
    entity_id: input_boolean.${esp_id}_ota_update_available
    id: ${esp_id}_ota_update_available
    on_value:
      then:
        - if:
            condition:
              text_sensor.state:
                id: ${esp_id}_ota_update_available
                state: 'off'
            then:
              - logger.log: "Switch turned off"
              - light.turn_off: ${esp_id}_light_board # Turn off the light
              - text_sensor.template.publish:
                  id: ${esp_id}_ota_status
                  state: "No OTA updates pending"
              - script.execute: run  # Run the script to go back to deep sleep
            else:
              - logger.log: "Switch turned on"
              - light.turn_on:
                  id: ${esp_id}_light_board # Turn on the light
                  effect: "Fast Pulse"
              - text_sensor.template.publish:
                  id: ${esp_id}_ota_status
                  state: "Awaiting OTA update"

Now, when there’s an OTA update pending, the status light will blink :man_dancing: like there’s a party going on. When it’s done, or the update isn’t needed, the light goes off :walking_man:. It’s like a visual reminder so you don’t forget that your device is waiting for that OTA action.


3. Code to include in Home Assistant

For Home Assistant, we need to create an input_boolean so we can tell the device whether or not an OTA update is available. This helps manage the update flow between the two systems. :computer::arrows_counterclockwise:

In your configuration.yaml, you add the following code:

homeassistant:
  # Files where I include various settings for my ESPHome devices, automations, etc.
  packages: !include_dir_merge_named arduxmon_packages

This loads the YAML files from arduxmon_packages, including the device-specific ones. For example, in living_room_plants.yaml, you’ll add:

input_boolean:
  living_room_plants_ota_update_available:
    name: "Living room plants - OTA Update Available"
    initial: off
    icon: mdi:nature-people

This input_boolean is the switch that Home Assistant uses to tell the device that an OTA update is available. When it’s turned on, the ESP device stays awake, waiting for that sweet OTA update. :zap:


4. How Everything Works Together

Now let’s connect the dots so you can see how all this flows. :jigsaw:

Interaction between Home Assistant and ESPHome device:

  1. Activate the input_boolean in Home Assistant:
  • You turn on the input_boolean (e.g., living_room_plants_ota_update_available) in Home Assistant when you’re ready for the OTA update.
  1. The ESP32 device:
  • The device wakes up and checks the state of the input_boolean. If it’s on, it stays awake, and the status light turns on, flashing to let you know there’s an update pending.
  1. Turn off the input_boolean once the OTA is done:
  • Once the update happens, or if you just wanna stop it, you turn off the input_boolean in Home Assistant. This sends the device back into deep sleep :sleeping: and turns off the light.
  1. Using the light as a reminder:
  • If you forget to update or just want to be reminded, the flashing light will give you that visual cue :loudspeaker: that the OTA update is still pending.

The flow in a nutshell:

  1. You activate the input_boolean in Home Assistant when you want to start the OTA process.
  2. The ESP32 wakes up and checks the status. If there’s an update, the device stays awake and the light flashes. :arrows_counterclockwise::bulb:
  3. When the update is done (or you decide to cancel), you turn off the input_boolean, and the device goes back to deep sleep. :sleeping_bed:

Recap in the Funniest Way Possible :joy::

You’re chilling in the living room, and your plants :potted_plant: are like “yo, update me!”. So you go into Home Assistant, flip the switch :globe_with_meridians:, and boom, your ESP32 lights up :bulb: like a Christmas tree :house:. It waits for that OTA update while you go back to watching Netflix :clapper:. When it’s all done, the light goes off like “OK, back to sleep!” :sleeping:. Simple, automatic, and with a flashing light as your reminder! :bell:

1 Like