mirror of
https://github.com/esphome/esphome.git
synced 2026-11-15 09:36:04 +00:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41a1689cda | ||
|
|
58463a3959 | ||
|
|
dd469cd927 | ||
|
|
1faed06c61 | ||
|
|
cf03fc25eb | ||
|
|
f1643effd4 | ||
|
|
c743924982 | ||
|
|
94a68fc939 | ||
|
|
defe20e84e | ||
|
|
6b907952f5 | ||
|
|
b314955fde | ||
|
|
702ef4952f | ||
|
|
500f62d8bd |
No files matched your search
@@ -41,8 +41,7 @@ from esphome.const import (
|
||||
PLATFORM_NRF52,
|
||||
PlatformFramework,
|
||||
)
|
||||
from esphome.core import CORE, ID
|
||||
from esphome.cpp_generator import MockObj, TemplateArgsType
|
||||
from esphome.core import CORE
|
||||
from esphome.types import ConfigType
|
||||
|
||||
WAKEUP_PINS = {
|
||||
@@ -266,7 +265,6 @@ def _validate_sleep_duration(value: core.TimePeriod) -> core.TimePeriod:
|
||||
|
||||
deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
|
||||
DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
|
||||
EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action)
|
||||
|
||||
WakeupPinMode = deep_sleep_ns.enum("WakeupPinMode")
|
||||
WAKEUP_PIN_MODES = WakeupPinMode.options(
|
||||
@@ -498,31 +496,21 @@ DEEP_SLEEP_ENTER_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
automation.register_apply_action(
|
||||
"deep_sleep.enter",
|
||||
EnterDeepSleepAction,
|
||||
DEEP_SLEEP_ENTER_SCHEMA,
|
||||
synchronous=True,
|
||||
automation.ApplyField(CONF_SLEEP_DURATION, "set_sleep_duration", cg.uint32),
|
||||
automation.ApplyCall(
|
||||
"set_sleep_until({}, {}, {}, {})",
|
||||
(
|
||||
(CONF_TIME_ID, time.RealTimeClock),
|
||||
((CONF_UNTIL, CONF_HOUR), cg.uint8),
|
||||
((CONF_UNTIL, CONF_MINUTE), cg.uint8),
|
||||
((CONF_UNTIL, CONF_SECOND), cg.uint8),
|
||||
),
|
||||
),
|
||||
automation.ApplyCall("begin_sleep(true)"),
|
||||
)
|
||||
async def deep_sleep_enter_to_code(
|
||||
config: ConfigType,
|
||||
action_id: ID,
|
||||
template_arg: cg.TemplateArguments,
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
if CONF_SLEEP_DURATION in config:
|
||||
template_ = await cg.templatable(config[CONF_SLEEP_DURATION], args, cg.uint32)
|
||||
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]))
|
||||
|
||||
await cg.add_variable(var.set_time, config[CONF_TIME_ID])
|
||||
|
||||
return var
|
||||
|
||||
|
||||
for _name, _call in (
|
||||
|
||||
@@ -27,6 +27,45 @@ void DeepSleepComponent::schedule_sleep_() {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_TIME
|
||||
void DeepSleepComponent::set_sleep_until(time::RealTimeClock *clock, uint8_t hour, uint8_t minute, uint8_t second) {
|
||||
auto time = clock->now();
|
||||
const uint32_t timestamp_now = time.timestamp;
|
||||
|
||||
bool after_time = false;
|
||||
if (time.hour > hour) {
|
||||
after_time = true;
|
||||
} else {
|
||||
if (time.hour == hour) {
|
||||
if (time.minute > minute) {
|
||||
after_time = true;
|
||||
} else {
|
||||
if (time.minute == minute) {
|
||||
if (time.second > second) {
|
||||
after_time = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.hour = hour;
|
||||
time.minute = minute;
|
||||
time.second = 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 = ESPTime::timezone_offset();
|
||||
timestamp -= offset; // Change timestamp to utc
|
||||
const uint32_t ms_left = (timestamp - timestamp_now) * 1000;
|
||||
this->set_sleep_duration(ms_left);
|
||||
}
|
||||
#endif
|
||||
|
||||
void DeepSleepComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Deep sleep:");
|
||||
if (this->sleep_duration_.has_value()) {
|
||||
|
||||
@@ -123,8 +123,6 @@ class Ext1WakeTrigger : public Trigger<>, public Component {
|
||||
|
||||
#endif // USE_DEEP_SLEEP_ON_WAKE
|
||||
|
||||
template<typename... Ts> class EnterDeepSleepAction;
|
||||
|
||||
/** This component allows setting up the node to go into deep sleep mode to conserve battery.
|
||||
*
|
||||
* To set this component up, first set *when* the deep sleep should trigger using set_run_cycles
|
||||
@@ -135,6 +133,10 @@ class DeepSleepComponent final : public Component {
|
||||
public:
|
||||
/// Set the duration in ms the component should sleep once it's in deep sleep mode.
|
||||
void set_sleep_duration(uint32_t time_ms) { this->sleep_duration_ = uint64_t(time_ms) * 1000; }
|
||||
#ifdef USE_TIME
|
||||
/// Set the sleep duration to end at the next hour:minute:second local time on the clock.
|
||||
void set_sleep_until(time::RealTimeClock *clock, uint8_t hour, uint8_t minute, uint8_t second);
|
||||
#endif
|
||||
#if defined(USE_ESP32)
|
||||
/** Set the pin to wake up to on the ESP32 once it's in deep sleep mode.
|
||||
* Use the inverted property to set the wakeup level.
|
||||
@@ -228,76 +230,4 @@ class DeepSleepComponent final : public Component {
|
||||
|
||||
extern bool global_has_deep_sleep; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
template<typename... Ts> class EnterDeepSleepAction final : public Action<Ts...> {
|
||||
public:
|
||||
EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {}
|
||||
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(const Ts &...x) override {
|
||||
if (this->sleep_duration_.has_value()) {
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
protected:
|
||||
DeepSleepComponent *deep_sleep_;
|
||||
#ifdef USE_TIME
|
||||
optional<uint8_t> hour_;
|
||||
optional<uint8_t> minute_;
|
||||
optional<uint8_t> second_;
|
||||
|
||||
time::RealTimeClock *time_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome::deep_sleep
|
||||
@@ -243,59 +243,73 @@ def test_deep_sleep_enter_without_duration(
|
||||
main_cpp = generate_main(ENTER_YAML)
|
||||
|
||||
assert (
|
||||
"new(deep_sleep_enterdeepsleepaction_id) "
|
||||
"deep_sleep::EnterDeepSleepAction<>(deepsleep);\n"
|
||||
"new(automation_id) Automation<>(sleep_now);"
|
||||
"static void esphome__applyaction_id__fn() {\n"
|
||||
" ::deepsleep->begin_sleep(true);\n"
|
||||
"}\n"
|
||||
) in main_cpp
|
||||
assert "new(applyaction_id) ApplyAction<esphome__applyaction_id__fn>();" in main_cpp
|
||||
assert "EnterDeepSleepAction" not in main_cpp
|
||||
|
||||
|
||||
def test_deep_sleep_enter_constant_duration(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""A constant sleep_duration is set in milliseconds."""
|
||||
"""A constant sleep_duration is set in milliseconds before the sleep begins."""
|
||||
main_cpp = generate_main(ENTER_YAML)
|
||||
|
||||
assert (
|
||||
"deep_sleep_enterdeepsleepaction_id_2->set_sleep_duration([]() -> uint32_t {\n"
|
||||
" return 3600000;\n"
|
||||
"});"
|
||||
"static void esphome__applyaction_id_2__fn() {\n"
|
||||
" ::deepsleep->set_sleep_duration(3600000);\n"
|
||||
" ::deepsleep->begin_sleep(true);\n"
|
||||
"}\n"
|
||||
) in main_cpp
|
||||
|
||||
|
||||
def test_deep_sleep_enter_lambda_duration(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""A lambda sleep_duration is computed from the script parameters."""
|
||||
"""A lambda sleep_duration is computed from the script parameters before the sleep begins."""
|
||||
main_cpp = generate_main(ENTER_YAML)
|
||||
|
||||
assert re.search(
|
||||
r"new\(deep_sleep_enterdeepsleepaction_id_3\) "
|
||||
r"deep_sleep::EnterDeepSleepAction<uint32_t>\(deepsleep\);\n"
|
||||
r"deep_sleep_enterdeepsleepaction_id_3->set_sleep_duration\("
|
||||
r"\[\]\(uint32_t seconds\) -> uint32_t \{\n(?: #line [^\n]*\n)?"
|
||||
r" return seconds \* 1000;\n",
|
||||
r"static void esphome__applyaction_id_3__fn\("
|
||||
r"const std::remove_cvref_t<uint32_t> & seconds\) \{\n"
|
||||
r" ::deepsleep->set_sleep_duration\(\[\]\(uint32_t seconds\) -> uint32_t \{\n"
|
||||
r"(?: #line [^\n]*\n)?"
|
||||
r" return seconds \* 1000;\n"
|
||||
r"(?: // ESPHOME_LINE_DIRECTIVE_RESET\n)?"
|
||||
r" \}\(seconds\)\);\n"
|
||||
r" ::deepsleep->begin_sleep\(true\);\n"
|
||||
r"\}\n",
|
||||
main_cpp,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("action_id", "body"),
|
||||
("fn_id", "body"),
|
||||
[
|
||||
("deep_sleep_enterdeepsleepaction_id_4", "seconds *= 1000; return seconds;"),
|
||||
("deep_sleep_enterdeepsleepaction_id_5", "return seconds *= 1000;"),
|
||||
("applyaction_id_4", "seconds *= 1000; return seconds;"),
|
||||
("applyaction_id_5", "return seconds *= 1000;"),
|
||||
],
|
||||
)
|
||||
def test_deep_sleep_enter_lambda_takes_args_by_value(
|
||||
generate_main: Callable[[str | Path], str], action_id: str, body: str
|
||||
generate_main: Callable[[str | Path], str], fn_id: str, body: str
|
||||
) -> None:
|
||||
"""The sleep_duration lambda gets the script parameters by value, so it may modify them."""
|
||||
"""The sleep_duration lambda gets the script parameters by value, so it may modify them.
|
||||
|
||||
It is called rather than reduced, so even a single return modifies only its own copy.
|
||||
"""
|
||||
main_cpp = generate_main(ENTER_YAML)
|
||||
|
||||
assert re.search(
|
||||
action_id + r"->set_sleep_duration\("
|
||||
r"\[\]\(uint32_t seconds\) -> uint32_t \{\n(?: #line [^\n]*\n)? "
|
||||
r"static void esphome__"
|
||||
+ fn_id
|
||||
+ r"__fn\(const std::remove_cvref_t<uint32_t> & seconds\) \{\n"
|
||||
r" ::deepsleep->set_sleep_duration\(\[\]\(uint32_t seconds\) -> uint32_t \{\n"
|
||||
r"(?: #line [^\n]*\n)? "
|
||||
+ re.escape(body)
|
||||
+ "\n",
|
||||
+ r"\n(?: // ESPHOME_LINE_DIRECTIVE_RESET\n)? \}\(seconds\)\);\n"
|
||||
r" ::deepsleep->begin_sleep\(true\);\n\}\n",
|
||||
main_cpp,
|
||||
)
|
||||
|
||||
@@ -307,11 +321,34 @@ def test_deep_sleep_enter_until(
|
||||
main_cpp = generate_main(ENTER_YAML)
|
||||
|
||||
assert (
|
||||
"deep_sleep_enterdeepsleepaction_id_6->set_until(6, 30, 0);\n"
|
||||
"deep_sleep_enterdeepsleepaction_id_6->set_time(sntp_time);"
|
||||
"static void esphome__applyaction_id_6__fn() {\n"
|
||||
" ::deepsleep->set_sleep_until(::sntp_time, 6, 30, 0);\n"
|
||||
" ::deepsleep->begin_sleep(true);\n"
|
||||
"}\n"
|
||||
) in main_cpp
|
||||
|
||||
|
||||
def test_deep_sleep_enter_merges_with_adjacent_actions(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
) -> None:
|
||||
"""deep_sleep.enter shares one generated function with the actions around it."""
|
||||
main_cpp = generate_main(ENTER_YAML)
|
||||
|
||||
start = main_cpp.index(
|
||||
"static void esphome__applyaction_id_7__fn(const int & esphome__running) {"
|
||||
)
|
||||
body = main_cpp[start : main_cpp.index("\n}\n", start)]
|
||||
assert (
|
||||
body.index("::deepsleep->allow_deep_sleep();")
|
||||
< body.index("::deepsleep->begin_sleep(true);")
|
||||
< body.index("::deepsleep->prevent_deep_sleep();")
|
||||
)
|
||||
assert (
|
||||
"new(applyaction_id_7) ApplyRunAction<esphome__applyaction_id_7__fn>();"
|
||||
in main_cpp
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"variant", [VARIANT_ESP32H2, VARIANT_ESP32H21, VARIANT_ESP32S31]
|
||||
)
|
||||
|
||||
@@ -46,3 +46,8 @@ script:
|
||||
- deep_sleep.enter:
|
||||
until: "06:30:00"
|
||||
time_id: sntp_time
|
||||
- id: allow_and_sleep
|
||||
then:
|
||||
- deep_sleep.allow: deepsleep
|
||||
- deep_sleep.enter: deepsleep
|
||||
- deep_sleep.prevent: deepsleep
|
||||
Reference in new issue
Block a user