ESPHome 2026.10.2
Loading...
Searching...
No Matches
deep_sleep_component.h
Go to the documentation of this file.
1#pragma once
2
5#include "esphome/core/hal.h"
7#ifdef USE_ESP32
8#include <esp_sleep.h>
9#endif
10
11#ifdef USE_TIME
13#include "esphome/core/time.h"
14#endif
15
16#include <cinttypes>
17
18namespace esphome::deep_sleep {
19
20#if defined(USE_ESP32) || defined(USE_BK72XX)
21
34#endif
35
36#if defined(USE_BK72XX)
42#endif // USE_BK72XX
43
44#ifdef USE_ESP32
45#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
46struct Ext1Wakeup {
47 uint64_t mask;
48 esp_sleep_ext1_wakeup_mode_t wakeup_mode;
49};
50#endif
51
53 // Run duration if woken up by timer or any other reason besides those below.
55 // Run duration if woken up by touch pads.
57 // Run duration if woken up by GPIO pins.
59};
60
61#endif // USE_ESP32
62
63#ifdef USE_DEEP_SLEEP_ON_WAKE
64
78
81
88inline constexpr float ON_WAKE_TRIGGER_SETUP_PRIORITY = 700.0f;
89
91class WakeTrigger : public Trigger<WakeupCause>, public Component {
92 public:
93 void setup() override {
94 const WakeupCause cause = get_wakeup_cause();
95 if (cause != WAKEUP_CAUSE_NONE) {
96 this->trigger(cause);
97 }
98 }
99 float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; }
100};
101
102#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
104class Ext1WakeTrigger : public Trigger<>, public Component {
105 public:
106 explicit Ext1WakeTrigger(uint8_t pin) : pin_(pin) {}
107 void setup() override {
108 if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1 &&
109 (esp_sleep_get_ext1_wakeup_status() & (1ULL << this->pin_))) {
110 this->trigger();
111 }
112 }
113 float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; }
114
115 protected:
116 uint8_t pin_;
117};
118#endif
119
120#endif // USE_DEEP_SLEEP_ON_WAKE
121
122template<typename... Ts> class EnterDeepSleepAction;
123
130class DeepSleepComponent final : public Component {
131 public:
133 void set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; }
134#if defined(USE_ESP32)
138 void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; }
139
140 void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode);
141#endif // USE_ESP32
142
143#if defined(USE_BK72XX)
144 void init_wakeup_pins(size_t capacity) { this->wakeup_pins_.init(capacity); }
145 void add_wakeup_pin(InternalGPIOPin *wakeup_pin, WakeupPinMode wakeup_pin_mode) {
146 this->wakeup_pins_.emplace_back(WakeUpPinItem{wakeup_pin, wakeup_pin_mode, !wakeup_pin->is_inverted()});
147 }
148#endif // USE_BK72XX
149
150#if defined(USE_ESP32)
151#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
152 void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
153#endif
154
155#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3) && \
156 !defined(USE_ESP32_VARIANT_ESP32C5) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
157 !defined(USE_ESP32_VARIANT_ESP32C61) && !defined(USE_ESP32_VARIANT_ESP32H2)
158 void set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
159#endif
160
161 // Set the duration in ms for how long the code should run before entering
162 // deep sleep mode, according to the cause the ESP32 has woken.
163 void set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration);
164#endif // USE_ESP32
165
167 void set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; }
168
169 void setup() override;
170 void dump_config() override;
171 void loop() override;
172 float get_setup_priority() const override;
173
175 void begin_sleep(bool manual = false);
176
177 void prevent_deep_sleep() { this->prevent_ = true; }
178 void allow_deep_sleep() { this->prevent_ = false; }
179
180 protected:
181 // Returns nullopt if no run duration is set. Otherwise, returns the run
182 // duration before entering deep sleep.
183 optional<uint32_t> get_run_duration_() const;
184
186 bool prepare_to_sleep_();
187 void deep_sleep_();
188 void schedule_sleep_();
189 bool should_teardown_();
190
192 this->next_enter_deep_sleep_ = true;
193 this->enable_loop();
194 }
195
196#ifdef USE_BK72XX
197 bool pin_prevents_sleep_(WakeUpPinItem &pin_item) const;
198 bool get_real_pin_state_(InternalGPIOPin &pin) const { return (pin.digital_read() ^ pin.is_inverted()); }
199#endif // USE_BK72XX
200
201 optional<uint64_t> sleep_duration_;
202
203#ifdef USE_BK72XX
205#endif // USE_BK72XX
206
207#ifdef USE_ESP32
210
211#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
212 optional<Ext1Wakeup> ext1_wakeup_;
213#endif
214
215 optional<bool> touch_wakeup_;
216
217 optional<WakeupCauseToRunDuration> wakeup_cause_to_run_duration_;
218#endif // USE_ESP32
219
220 optional<uint32_t> run_duration_;
222 bool prevent_{false};
223};
224
225extern bool global_has_deep_sleep; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
226
227template<typename... Ts> class EnterDeepSleepAction final : public Action<Ts...> {
228 public:
230 TEMPLATABLE_VALUE(uint32_t, sleep_duration);
231
232#ifdef USE_TIME
233 void set_until(uint8_t hour, uint8_t minute, uint8_t second) {
234 this->hour_ = hour;
235 this->minute_ = minute;
236 this->second_ = second;
237 }
238
239 void set_time(time::RealTimeClock *time) { this->time_ = time; }
240#endif
241
242 void play(const Ts &...x) override {
243 if (this->sleep_duration_.has_value()) {
244 this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
245 }
246#ifdef USE_TIME
247
248 if (this->hour_.has_value()) {
249 auto time = this->time_->now();
250 const uint32_t timestamp_now = time.timestamp;
251
252 bool after_time = false;
253 if (time.hour > this->hour_) {
254 after_time = true;
255 } else {
256 if (time.hour == this->hour_) {
257 if (time.minute > this->minute_) {
258 after_time = true;
259 } else {
260 if (time.minute == this->minute_) {
261 if (time.second > this->second_) {
262 after_time = true;
263 }
264 }
265 }
266 }
267 }
268
269 time.hour = *this->hour_;
270 time.minute = *this->minute_;
271 time.second = *this->second_;
272 time.recalc_timestamp_utc();
273
274 time_t timestamp = time.timestamp; // timestamp in local time zone
275
276 if (after_time)
277 timestamp += 60 * 60 * 24;
278
279 int32_t offset = ESPTime::timezone_offset();
280 timestamp -= offset; // Change timestamp to utc
281 const uint32_t ms_left = (timestamp - timestamp_now) * 1000;
282 this->deep_sleep_->set_sleep_duration(ms_left);
283 }
284#endif
285 this->deep_sleep_->begin_sleep(true);
286 }
287
288 protected:
290#ifdef USE_TIME
291 optional<uint8_t> hour_;
292 optional<uint8_t> minute_;
293 optional<uint8_t> second_;
294
296#endif
297};
298
299} // namespace esphome::deep_sleep
void enable_loop()
Enable this component's loop.
Definition component.h:246
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:621
virtual bool digital_read()=0
virtual bool is_inverted() const =0
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:528
This component allows setting up the node to go into deep sleep mode to conserve battery.
void set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration)
void set_run_duration(uint32_t time_ms)
Set a duration in ms for how long the code should run before entering deep sleep mode.
bool pin_prevents_sleep_(WakeUpPinItem &pin_item) const
optional< uint32_t > get_run_duration_() const
void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode)
bool get_real_pin_state_(InternalGPIOPin &pin) const
void add_wakeup_pin(InternalGPIOPin *wakeup_pin, WakeupPinMode wakeup_pin_mode)
void begin_sleep(bool manual=false)
Helper to enter deep sleep mode.
void set_wakeup_pin(InternalGPIOPin *pin)
Set the pin to wake up to on the ESP32 once it's in deep sleep mode.
void set_sleep_duration(uint32_t time_ms)
Set the duration in ms the component should sleep once it's in deep sleep mode.
void set_ext1_wakeup(Ext1Wakeup ext1_wakeup)
optional< WakeupCauseToRunDuration > wakeup_cause_to_run_duration_
EnterDeepSleepAction(DeepSleepComponent *deep_sleep)
TEMPLATABLE_VALUE(uint32_t, sleep_duration)
void set_until(uint8_t hour, uint8_t minute, uint8_t second)
Fires once on boot when the device was woken from deep sleep by the given ext1 pin.
Fires once on boot when the device woke from deep sleep, with the wakeup cause.
float get_setup_priority() const override
The RealTimeClock class exposes common timekeeping functions via the device's local real-time clock.
ESPTime now()
Get the time in the currently defined timezone.
uint8_t second
uint8_t minute
uint8_t hour
WakeupPinMode
The values of this enum define what should be done if deep sleep is set up with a wakeup pin on the E...
@ WAKEUP_PIN_MODE_KEEP_AWAKE
As long as the wakeup pin is still in the wakeup state, keep awake.
@ WAKEUP_PIN_MODE_INVERT_WAKEUP
Automatically invert the wakeup level.
@ WAKEUP_PIN_MODE_IGNORE
Ignore the fact that we will wake up when going into deep sleep.
constexpr float ON_WAKE_TRIGGER_SETUP_PRIORITY
Setup priority of on_wake triggers.
WakeupCause get_wakeup_cause()
Return why the device woke from deep sleep. Implemented per platform.
WakeupCause
Why the device woke from deep sleep. Passed to on_wake automations.
@ WAKEUP_CAUSE_TOUCH
The device was woken by a touch pad.
@ WAKEUP_CAUSE_GPIO
The device was woken by a GPIO pin (wakeup_pin or esp32_ext1_wakeup).
@ WAKEUP_CAUSE_TIMER
The device was woken by the sleep timer.
@ WAKEUP_CAUSE_UNKNOWN
The device woke from deep sleep, but the source could not be identified.
@ WAKEUP_CAUSE_NONE
The device did not wake from deep sleep (for example a cold boot, reset or OTA restart).
uint16_t uint16_t & capacity
Definition helpers.cpp:33
static void uint32_t
static int32_t timezone_offset()
Definition time.cpp:351
time_t timestamp
unix epoch time (seconds since UTC Midnight January 1, 1970)
Definition time.h:48
esp_sleep_ext1_wakeup_mode_t wakeup_mode
uint16_t x
Definition tt21100.cpp:5
uint16_t timestamp
Definition tt21100.cpp:2