ESP-based devices, like the M5Stack Atom, are a great platform for building small automation projects on.
ESPHome is a great way of rapidly generating feature-rich firmware for these devices.
Emad Alashi – a long-time coworker of mine – recently blogged about a soil moisture sensor that he’s built using exactly this combination of M5Stack Atom + ESPHome. He’s working on battery power, and thus needs to put the device into deep sleep most of the time to conserve energy.
The challenge: Combining deep sleep behaviour with over-the-air updates. It’s incredibly hard to push an over-the-air firmware update to the device when it’s only awake for a few seconds at a time!
The solution: Publish a flag that says “stay awake”. When the device next wakes up, it’ll read this flag, and skip a further sleep cycle. It’s essentially an advertised maintenance mode.
Emad hit this challenge in his project and implemented the solution that’s documented with the ESPHome Deep Sleep Component.
That solution relies on an MQTT broker holding the message, and the device checking for this pending message on boot:
deep_sleep:
# ...
id: deep_sleep_1
mqtt:
# ...
on_message:
- topic: livingroom/ota_mode
payload: 'ON'
then:
- deep_sleep.prevent: deep_sleep_1
- topic: livingroom/sleep_mode
payload: 'ON'
then:
- deep_sleep.enter: deep_sleep_1
I wanted to document an alternate approach, that avoids the need to introduce an MQTT connection, and sticks with a purely Home Assistant-native approach instead.
One of the things I really like about ESPHome is how natively it is integrated with Home Assistant, and the entity model that’s already there. Let’s avoid adding another protocol and set of messaging concepts in the mix.
Introducing a Global Flag
First up, we need a way of storing the “please stay awake” flag.
Home Assistant has the concept of “helpers”, which are just easy places to store an extra little bit of state like this.
They’re available in the web interface under Configuration > Helpers.

I’ve gone ahead and created one for our flag. This will be a single global flag, that all of my sleep-aware devices can watch and respond to.
Type | Toggle / Boolean |
Name | Prevent Deep Sleep |
Icon | mdi:sleep-off |
Entity ID | input_boolean.prevent_deep_sleep |

If you’re more of a fan of managing Home Assistant via configuration.yaml
, you can declare the helper there too:
input_boolean:
prevent_deep_sleep:
name: Prevent Deep Sleep
icon: mdi:sleep-off
Firmware
Next, we need to read this flag in our firmware.
ESPHome has a binary sensor that will read it from Home Assistant for us.
binary_sensor:
- platform: homeassistant
id: prevent_deep_sleep
name: Prevent Deep Sleep
entity_id: input_boolean.prevent_deep_sleep
When our device boots up, and connects to the Home Assistant API, it’ll read the helper value in to the local state. If Home Assistant isn’t connected, or is offline, it’ll default to false.
⚠ Important note: The connection is actually triggered from Home Assistant to the ESP-device, and it’s only initiated if the device is setup under Home Assistant > Integrations. Just because you can see the device in the ESPHome web interface doesn’t mean it’s actually setup as an integration. If your firmware seems to be ignoring the helper value, it’s probably not actually connected.
With the value available, we can now combine that with the deep_sleep
component to setup the right balance of power saving logic.
Here’s the complete, working ESPHome config for an M5Stack Atom Lite:
Result
Now I have a simple, global toggle when I want to put devices into development / maintenance mode.
The process becomes:
- I want to do some device maintenance
- I turn the “Prevent Deep Sleep” toggle on in Home Assistant
- Over time, all of the different battery-based devices come out of their regular sleep cycles, and then stay on
- I do the maintenance I want
- I turn the “Prevent Deep Sleep” toggle back to off
- All the devices resume their normal, power-saving sleep cycle
No extra brokers or message constructs had to be deployed. 🤘
Tip: Quick Bar
I don’t really want to add this toggle to the regular UI surface in Home Assistant, but I also don’t want to go digging for it in the dev tools every time I want to turn it on or off.
Home Assistant includes a ‘quick bar‘, modelled off the command palette in VS Code.
Just press e
(for ‘entity’) as a hotkey anywhere in the frontend, then type the name of the toggle:

I think you need – deep_sleep.prevent: deep_sleep_control in first condition of your script. And I needed to increase a script time to 15s for reliable work.
Works excellent.
Such a great guide, thank you very much!
Hey Victor 👋 My experience is that the deep_sleep component will only trigger automatically if a run_duration is set. Without that, there’s nothing to prevent. The way I’ve setup the loop means that the loop itself replaces the run_duration timer. Make sense?
It just didn’t work for me without it. When the flag was enabled in Home assistant, the device still entered to the deep sleep sometimes. Now it is stable.