ESPHome 2026.11.0-dev
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
76
79
82
83#ifdef USE_DEEP_SLEEP_ON_WAKE
84
91inline constexpr float ON_WAKE_TRIGGER_SETUP_PRIORITY = 700.0f;
92
94class WakeTrigger : public Trigger<WakeupCause>, public Component {
95 public:
96 void setup() override {
97 const WakeupCause cause = get_wakeup_cause();
98 if (cause != WAKEUP_CAUSE_NONE) {
99 this->trigger(cause);
100 }
101 }
102 float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; }
103};
104
105#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
107class Ext1WakeTrigger : public Trigger<>, public Component {
108 public:
109 explicit Ext1WakeTrigger(uint8_t pin) : pin_(pin) {}
110 void setup() override {
111 if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1 &&
112 (esp_sleep_get_ext1_wakeup_status() & (1ULL << this->pin_))) {
113 this->trigger();
114 }
115 }
116 float get_setup_priority() const override { return ON_WAKE_TRIGGER_SETUP_PRIORITY; }
117
118 protected:
119 uint8_t pin_;
120};
121#endif
122
123#endif // USE_DEEP_SLEEP_ON_WAKE
124
125template<typename... Ts> class EnterDeepSleepAction;
126
133class DeepSleepComponent final : public Component {
134 public:
136 void set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; }
137#if defined(USE_ESP32)
141 void set_wakeup_pin(InternalGPIOPin *pin) { this->wakeup_pin_ = pin; }
142
143 void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode);
144#endif // USE_ESP32
145
146#if defined(USE_BK72XX)
147 void init_wakeup_pins(size_t capacity) { this->wakeup_pins_.init(capacity); }
148 void add_wakeup_pin(InternalGPIOPin *wakeup_pin, WakeupPinMode wakeup_pin_mode) {
149 this->wakeup_pins_.emplace_back(WakeUpPinItem{wakeup_pin, wakeup_pin_mode, !wakeup_pin->is_inverted()});
150 }
151#endif // USE_BK72XX
152
153#if defined(USE_ESP32)
154#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
155 void set_ext1_wakeup(Ext1Wakeup ext1_wakeup);
156#endif
157
158#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3) && \
159 !defined(USE_ESP32_VARIANT_ESP32C5) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
160 !defined(USE_ESP32_VARIANT_ESP32C61) && !defined(USE_ESP32_VARIANT_ESP32H2)
161 void set_touch_wakeup(bool touch_wakeup) { this->touch_wakeup_ = touch_wakeup; }
162#endif
163
164 // Set the duration in ms for how long the code should run before entering
165 // deep sleep mode, according to the cause the ESP32 has woken.
166 void set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration);
167#endif // USE_ESP32
168
170 void set_run_duration(uint32_t time_ms) { this->run_duration_ = time_ms; }
171
172 void setup() override;
173 void dump_config() override;
174 void loop() override;
175 float get_setup_priority() const override;
176
178 void begin_sleep(bool manual = false);
179
180 void prevent_deep_sleep() { this->prevent_ = true; }
181 void allow_deep_sleep() { this->prevent_ = false; }
182
183 protected:
184 // Returns nullopt if no run duration is set. Otherwise, returns the run
185 // duration before entering deep sleep.
186 optional<uint32_t> get_run_duration_() const;
187
189 bool prepare_to_sleep_();
190 void deep_sleep_();
191 void schedule_sleep_();
192 bool should_teardown_();
193
195 this->next_enter_deep_sleep_ = true;
196 this->enable_loop();
197 }
198
199#ifdef USE_BK72XX
200 bool pin_prevents_sleep_(WakeUpPinItem &pin_item) const;
201 bool get_real_pin_state_(InternalGPIOPin &pin) const { return (pin.digital_read() ^ pin.is_inverted()); }
202#endif // USE_BK72XX
203
204 optional<uint64_t> sleep_duration_;
205
206#ifdef USE_BK72XX
208#endif // USE_BK72XX
209
210#ifdef USE_ESP32
213
214#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
215 optional<Ext1Wakeup> ext1_wakeup_;
216#endif
217
218 optional<bool> touch_wakeup_;
219
220 optional<WakeupCauseToRunDuration> wakeup_cause_to_run_duration_;
221#endif // USE_ESP32
222
223 optional<uint32_t> run_duration_;
225 bool prevent_{false};
226};
227
228extern bool global_has_deep_sleep; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
229
230template<typename... Ts> class EnterDeepSleepAction final : public Action<Ts...> {
231 public:
233 TEMPLATABLE_VALUE(uint32_t, sleep_duration);
234
235#ifdef USE_TIME
236 void set_until(uint8_t hour, uint8_t minute, uint8_t second) {
237 this->hour_ = hour;
238 this->minute_ = minute;
239 this->second_ = second;
240 }
241
242 void set_time(time::RealTimeClock *time) { this->time_ = time; }
243#endif
244
245 void play(const Ts &...x) override {
246 if (this->sleep_duration_.has_value()) {
247 this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
248 }
249#ifdef USE_TIME
250
251 if (this->hour_.has_value()) {
252 auto time = this->time_->now();
253 const uint32_t timestamp_now = time.timestamp;
254
255 bool after_time = false;
256 if (time.hour > this->hour_) {
257 after_time = true;
258 } else {
259 if (time.hour == this->hour_) {
260 if (time.minute > this->minute_) {
261 after_time = true;
262 } else {
263 if (time.minute == this->minute_) {
264 if (time.second > this->second_) {
265 after_time = true;
266 }
267 }
268 }
269 }
270 }
271
272 time.hour = *this->hour_;
273 time.minute = *this->minute_;
274 time.second = *this->second_;
275 time.recalc_timestamp_utc();
276
277 time_t timestamp = time.timestamp; // timestamp in local time zone
278
279 if (after_time)
280 timestamp += 60 * 60 * 24;
281
282 int32_t offset = ESPTime::timezone_offset();
283 timestamp -= offset; // Change timestamp to utc
284 const uint32_t ms_left = (timestamp - timestamp_now) * 1000;
285 this->deep_sleep_->set_sleep_duration(ms_left);
286 }
287#endif
288 this->deep_sleep_->begin_sleep(true);
289 }
290
291 protected:
293#ifdef USE_TIME
294 optional<uint8_t> hour_;
295 optional<uint8_t> minute_;
296 optional<uint8_t> second_;
297
299#endif
300};
301
302} // namespace esphome::deep_sleep
void enable_loop()
Enable this component's loop.
Definition component.h:248
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:624
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:556
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
bool woke_from_deep_sleep()
Return true when this boot is a wake from deep sleep.
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:34
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