mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 07:28:10 +01:00
Allow specifying deep sleep wakup clock time (#3312)
This commit is contained in:
parent
b622a8fa58
commit
8be704e591
5 changed files with 118 additions and 8 deletions
|
@ -1,13 +1,18 @@
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import time
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins, automation
|
from esphome import pins, automation
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_HOUR,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_MINUTE,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
CONF_NUMBER,
|
CONF_NUMBER,
|
||||||
CONF_PINS,
|
CONF_PINS,
|
||||||
CONF_RUN_DURATION,
|
CONF_RUN_DURATION,
|
||||||
|
CONF_SECOND,
|
||||||
CONF_SLEEP_DURATION,
|
CONF_SLEEP_DURATION,
|
||||||
|
CONF_TIME_ID,
|
||||||
CONF_WAKEUP_PIN,
|
CONF_WAKEUP_PIN,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,6 +117,7 @@ CONF_TOUCH_WAKEUP = "touch_wakeup"
|
||||||
CONF_DEFAULT = "default"
|
CONF_DEFAULT = "default"
|
||||||
CONF_GPIO_WAKEUP_REASON = "gpio_wakeup_reason"
|
CONF_GPIO_WAKEUP_REASON = "gpio_wakeup_reason"
|
||||||
CONF_TOUCH_WAKEUP_REASON = "touch_wakeup_reason"
|
CONF_TOUCH_WAKEUP_REASON = "touch_wakeup_reason"
|
||||||
|
CONF_UNTIL = "until"
|
||||||
|
|
||||||
WAKEUP_CAUSES_SCHEMA = cv.Schema(
|
WAKEUP_CAUSES_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
|
@ -202,13 +208,19 @@ async def to_code(config):
|
||||||
cg.add_define("USE_DEEP_SLEEP")
|
cg.add_define("USE_DEEP_SLEEP")
|
||||||
|
|
||||||
|
|
||||||
DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id(
|
DEEP_SLEEP_ENTER_SCHEMA = cv.All(
|
||||||
|
automation.maybe_simple_id(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(DeepSleepComponent),
|
cv.GenerateID(): cv.use_id(DeepSleepComponent),
|
||||||
cv.Optional(CONF_SLEEP_DURATION): cv.templatable(
|
cv.Exclusive(CONF_SLEEP_DURATION, "time"): cv.templatable(
|
||||||
cv.positive_time_period_milliseconds
|
cv.positive_time_period_milliseconds
|
||||||
),
|
),
|
||||||
|
# Only on ESP32 due to how long the RTC on ESP8266 can stay asleep
|
||||||
|
cv.Exclusive(CONF_UNTIL, "time"): cv.All(cv.only_on_esp32, cv.time_of_day),
|
||||||
|
cv.Optional(CONF_TIME_ID): cv.use_id(time.RealTimeClock),
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
cv.has_none_or_all_keys(CONF_UNTIL, CONF_TIME_ID),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,6 +240,14 @@ async def deep_sleep_enter_to_code(config, action_id, template_arg, args):
|
||||||
if CONF_SLEEP_DURATION in config:
|
if CONF_SLEEP_DURATION in config:
|
||||||
template_ = await cg.templatable(config[CONF_SLEEP_DURATION], args, cg.int32)
|
template_ = await cg.templatable(config[CONF_SLEEP_DURATION], args, cg.int32)
|
||||||
cg.add(var.set_sleep_duration(template_))
|
cg.add(var.set_sleep_duration(template_))
|
||||||
|
|
||||||
|
if CONF_UNTIL in config:
|
||||||
|
until = config[CONF_UNTIL]
|
||||||
|
cg.add(var.set_until(until[CONF_HOUR], until[CONF_MINUTE], until[CONF_SECOND]))
|
||||||
|
|
||||||
|
time_ = await cg.get_variable(config[CONF_TIME_ID])
|
||||||
|
cg.add(var.set_time(time_))
|
||||||
|
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "deep_sleep_component.h"
|
#include "deep_sleep_component.h"
|
||||||
#include "esphome/core/log.h"
|
#include <cinttypes>
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <Esp.h>
|
#include <Esp.h>
|
||||||
|
@ -101,6 +102,8 @@ void DeepSleepComponent::begin_sleep(bool manual) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Beginning Deep Sleep");
|
ESP_LOGI(TAG, "Beginning Deep Sleep");
|
||||||
|
if (this->sleep_duration_.has_value())
|
||||||
|
ESP_LOGI(TAG, "Sleeping for %" PRId64 "us", *this->sleep_duration_);
|
||||||
|
|
||||||
App.run_safe_shutdown_hooks();
|
App.run_safe_shutdown_hooks();
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
#include <esp_sleep.h>
|
#include <esp_sleep.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
|
#include "esphome/components/time/real_time_clock.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace deep_sleep {
|
namespace deep_sleep {
|
||||||
|
|
||||||
|
@ -116,15 +120,71 @@ template<typename... Ts> class EnterDeepSleepAction : public Action<Ts...> {
|
||||||
EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {}
|
EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {}
|
||||||
TEMPLATABLE_VALUE(uint32_t, sleep_duration);
|
TEMPLATABLE_VALUE(uint32_t, sleep_duration);
|
||||||
|
|
||||||
|
#ifdef USE_TIME
|
||||||
|
void set_until(uint8_t hour, uint8_t minute, uint8_t second) {
|
||||||
|
this->hour_ = hour;
|
||||||
|
this->minute_ = minute;
|
||||||
|
this->second_ = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_time(time::RealTimeClock *time) { this->time_ = time; }
|
||||||
|
#endif
|
||||||
|
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
if (this->sleep_duration_.has_value()) {
|
if (this->sleep_duration_.has_value()) {
|
||||||
this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
|
this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
|
||||||
}
|
}
|
||||||
|
#ifdef USE_TIME
|
||||||
|
|
||||||
|
if (this->hour_.has_value()) {
|
||||||
|
auto time = this->time_->now();
|
||||||
|
const uint32_t timestamp_now = time.timestamp;
|
||||||
|
|
||||||
|
bool after_time = false;
|
||||||
|
if (time.hour > this->hour_) {
|
||||||
|
after_time = true;
|
||||||
|
} else {
|
||||||
|
if (time.hour == this->hour_) {
|
||||||
|
if (time.minute > this->minute_) {
|
||||||
|
after_time = true;
|
||||||
|
} else {
|
||||||
|
if (time.minute == this->minute_) {
|
||||||
|
if (time.second > this->second_) {
|
||||||
|
after_time = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.hour = *this->hour_;
|
||||||
|
time.minute = *this->minute_;
|
||||||
|
time.second = *this->second_;
|
||||||
|
time.recalc_timestamp_utc();
|
||||||
|
|
||||||
|
time_t timestamp = time.timestamp; // timestamp in local time zone
|
||||||
|
|
||||||
|
if (after_time)
|
||||||
|
timestamp += 60 * 60 * 24;
|
||||||
|
|
||||||
|
int32_t offset = time::ESPTime::timezone_offset();
|
||||||
|
timestamp -= offset; // Change timestamp to utc
|
||||||
|
const uint32_t ms_left = (timestamp - timestamp_now) * 1000;
|
||||||
|
this->deep_sleep_->set_sleep_duration(ms_left);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
this->deep_sleep_->begin_sleep(true);
|
this->deep_sleep_->begin_sleep(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DeepSleepComponent *deep_sleep_;
|
DeepSleepComponent *deep_sleep_;
|
||||||
|
#ifdef USE_TIME
|
||||||
|
optional<uint8_t> hour_;
|
||||||
|
optional<uint8_t> minute_;
|
||||||
|
optional<uint8_t> second_;
|
||||||
|
|
||||||
|
time::RealTimeClock *time_;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class PreventDeepSleepAction : public Action<Ts...> {
|
template<typename... Ts> class PreventDeepSleepAction : public Action<Ts...> {
|
||||||
|
|
|
@ -176,6 +176,31 @@ void ESPTime::recalc_timestamp_utc(bool use_day_of_year) {
|
||||||
res += this->second;
|
res += this->second;
|
||||||
this->timestamp = res;
|
this->timestamp = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t ESPTime::timezone_offset() {
|
||||||
|
int32_t offset = 0;
|
||||||
|
time_t now = ::time(nullptr);
|
||||||
|
auto local = ESPTime::from_epoch_local(now);
|
||||||
|
auto utc = ESPTime::from_epoch_utc(now);
|
||||||
|
bool negative = utc.hour > local.hour && local.day_of_year <= utc.day_of_year;
|
||||||
|
|
||||||
|
if (utc.minute > local.minute) {
|
||||||
|
local.minute += 60;
|
||||||
|
local.hour -= 1;
|
||||||
|
}
|
||||||
|
offset += (local.minute - utc.minute) * 60;
|
||||||
|
|
||||||
|
if (negative) {
|
||||||
|
offset -= (utc.hour - local.hour) * 3600;
|
||||||
|
} else {
|
||||||
|
if (utc.hour > local.hour) {
|
||||||
|
local.hour += 24;
|
||||||
|
}
|
||||||
|
offset += (local.hour - utc.hour) * 3600;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
bool ESPTime::operator<(ESPTime other) { return this->timestamp < other.timestamp; }
|
bool ESPTime::operator<(ESPTime other) { return this->timestamp < other.timestamp; }
|
||||||
bool ESPTime::operator<=(ESPTime other) { return this->timestamp <= other.timestamp; }
|
bool ESPTime::operator<=(ESPTime other) { return this->timestamp <= other.timestamp; }
|
||||||
bool ESPTime::operator==(ESPTime other) { return this->timestamp == other.timestamp; }
|
bool ESPTime::operator==(ESPTime other) { return this->timestamp == other.timestamp; }
|
||||||
|
|
|
@ -88,6 +88,8 @@ struct ESPTime {
|
||||||
/// Convert this ESPTime instance back to a tm struct.
|
/// Convert this ESPTime instance back to a tm struct.
|
||||||
struct tm to_c_tm();
|
struct tm to_c_tm();
|
||||||
|
|
||||||
|
static int32_t timezone_offset();
|
||||||
|
|
||||||
/// Increment this clock instance by one second.
|
/// Increment this clock instance by one second.
|
||||||
void increment_second();
|
void increment_second();
|
||||||
/// Increment this clock instance by one day.
|
/// Increment this clock instance by one day.
|
||||||
|
|
Loading…
Reference in a new issue