diff --git a/esphome/components/safe_mode/__init__.py b/esphome/components/safe_mode/__init__.py index 7f227d7dd1..92b285e279 100644 --- a/esphome/components/safe_mode/__init__.py +++ b/esphome/components/safe_mode/__init__.py @@ -7,15 +7,20 @@ from esphome.const import ( CONF_NUM_ATTEMPTS, CONF_REBOOT_TIMEOUT, CONF_SAFE_MODE, + CONF_TRIGGER_ID, KEY_PAST_SAFE_MODE, ) from esphome.core import CORE, coroutine_with_priority +from esphome import automation CODEOWNERS = ["@paulmonigatti", "@jsuanet", "@kbx81"] +CONF_ON_SAFE_MODE = "on_safe_mode" + safe_mode_ns = cg.esphome_ns.namespace("safe_mode") SafeModeComponent = safe_mode_ns.class_("SafeModeComponent", cg.Component) +SafeModeTrigger = safe_mode_ns.class_("SafeModeTrigger", automation.Trigger.template()) def _remove_id_if_disabled(value): @@ -34,6 +39,11 @@ CONFIG_SCHEMA = cv.All( cv.Optional( CONF_REBOOT_TIMEOUT, default="5min" ): cv.positive_time_period_milliseconds, + cv.Optional(CONF_ON_SAFE_MODE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SafeModeTrigger), + } + ), } ).extend(cv.COMPONENT_SCHEMA), _remove_id_if_disabled, @@ -48,6 +58,10 @@ async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) + for conf in config.get(CONF_ON_SAFE_MODE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + condition = var.should_enter_safe_mode( config[CONF_NUM_ATTEMPTS], config[CONF_REBOOT_TIMEOUT] ) diff --git a/esphome/components/safe_mode/automation.h b/esphome/components/safe_mode/automation.h new file mode 100644 index 0000000000..d1388449ee --- /dev/null +++ b/esphome/components/safe_mode/automation.h @@ -0,0 +1,17 @@ +#pragma once +#include "safe_mode.h" + +#include "esphome/core/automation.h" + +namespace esphome { +namespace safe_mode { + +class SafeModeTrigger : public Trigger<> { + public: + explicit SafeModeTrigger(SafeModeComponent *parent) { + parent->add_on_safe_mode_callback([this, parent]() { trigger(); }); + } +}; + +} // namespace safe_mode +} // namespace esphome diff --git a/esphome/components/safe_mode/safe_mode.cpp b/esphome/components/safe_mode/safe_mode.cpp index 06772ae1e0..6934dcb9d9 100644 --- a/esphome/components/safe_mode/safe_mode.cpp +++ b/esphome/components/safe_mode/safe_mode.cpp @@ -94,6 +94,8 @@ bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t en ESP_LOGW(TAG, "SAFE MODE IS ACTIVE"); + this->safe_mode_callback_.call(); + return true; } else { // increment counter diff --git a/esphome/components/safe_mode/safe_mode.h b/esphome/components/safe_mode/safe_mode.h index 6c7450a6ff..0ec3c29529 100644 --- a/esphome/components/safe_mode/safe_mode.h +++ b/esphome/components/safe_mode/safe_mode.h @@ -25,6 +25,10 @@ class SafeModeComponent : public Component { void on_safe_shutdown() override; + void add_on_safe_mode_callback(std::function &&callback) { + this->safe_mode_callback_.add(std::move(callback)); + } + protected: void write_rtc_(uint32_t val); uint32_t read_rtc_(); @@ -35,6 +39,7 @@ class SafeModeComponent : public Component { uint32_t safe_mode_rtc_value_; uint8_t safe_mode_num_attempts_; ESPPreferenceObject rtc_; + CallbackManager safe_mode_callback_{}; static const uint32_t ENTER_SAFE_MODE_MAGIC = 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot diff --git a/tests/components/safe_mode/common.yaml b/tests/components/safe_mode/common.yaml index 9c1d1ad3f9..ce8bf2f0cf 100644 --- a/tests/components/safe_mode/common.yaml +++ b/tests/components/safe_mode/common.yaml @@ -5,6 +5,8 @@ wifi: safe_mode: num_attempts: 3 reboot_timeout: 2min + on_safe_mode: + - logger.log: Time for safe mode button: - platform: safe_mode