Solution for Keeping Devices Awake During OTA Updates in ESPHome

On mobile here, but look at the prevent call here: Deep Sleep Component — ESPHome.

It’s to solve exactly this issue.

Wrap that call in a template switch automation. You can then control the switch from HA.

@xmon-df ^^

If you’re running ESPHome add-on then you can add execution of a shell command to the service something like the below to automatically start the OTA. This needs the Advanced SSH & Web Terminal add-on with protected mode disabled and the SSH keys setup first.

shell_command:
  esphome_ota_device: ssh root@<HA IP> -i /config/.ssh/haid -o StrictHostKeyChecking=no docker exec addon_5c53de3b_esphome esphome run /config/esphome/<target>.yaml --device OTA --no-logs

Hmm right. Possibly you need to wait a little longer to then retrieve the actual value after connection. Not sure. Could try adding a 1sec delay etc.

What I meant was, you could “wait_until there is a value in the response”

So maybe wait until

lambda: 'return id(ota_update_available).state != "";'

But the esp is asleep and won’t receive the prevent deep sleep action?

This is why you need to wedge logic into on_boot actions.

The OP already makes use of deep_sleep.prevent.

Yes, I was just about to answer that you need to implement the on_boot action. It seems like you don’t like that.

I already have a similar solution to this which wakes up, updates all sensors, and goes to sleep if a deep sleep prevent toggle isn’t on.

Using on_boot and a prevent deep sleep switch/Boolean is a pretty standard approach around the forums.

The OP has a pretty good solution and is looking for suggestions of improvements.

The OP already uses both on boot and deep sleep prevent (as do I).

1 Like

Yeah, you’re right. I lost track of the fact that there was a working solution.

1 Like

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!