diff --git a/CODEOWNERS b/CODEOWNERS index dc6c8caa87..22e581275b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -306,7 +306,7 @@ esphome/components/rp2040_pwm/* @jesserockz esphome/components/rpi_dpi_rgb/* @clydebarrow esphome/components/rtl87xx/* @kuba2k2 esphome/components/rtttl/* @glmnet -esphome/components/safe_mode/* @jsuanet @paulmonigatti +esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti esphome/components/scd4x/* @martgras @sjtrny esphome/components/script/* @esphome/core esphome/components/sdm_meter/* @jesserockz @polyfaces diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index abe9323b53..c5903974c2 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -1,19 +1,16 @@ -from esphome.cpp_generator import RawExpression import esphome.codegen as cg import esphome.config_validation as cv from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code, OTAComponent from esphome.const import ( CONF_ID, CONF_NUM_ATTEMPTS, - CONF_OTA, CONF_PASSWORD, CONF_PORT, CONF_REBOOT_TIMEOUT, CONF_SAFE_MODE, CONF_VERSION, - KEY_PAST_SAFE_MODE, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import coroutine_with_priority CODEOWNERS = ["@esphome/core"] @@ -28,7 +25,6 @@ CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(ESPHomeOTAComponent), - cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean, cv.Optional(CONF_VERSION, default=2): cv.one_of(1, 2, int=True), cv.SplitDefault( CONF_PORT, @@ -39,10 +35,15 @@ CONFIG_SCHEMA = ( rtl87xx=8892, ): cv.port, cv.Optional(CONF_PASSWORD): cv.string, - cv.Optional( - CONF_REBOOT_TIMEOUT, default="5min" - ): cv.positive_time_period_milliseconds, - cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int, + cv.Optional(CONF_NUM_ATTEMPTS): cv.invalid( + f"'{CONF_SAFE_MODE}' (and its related configuration variables) has moved from 'ota' to its own component. See https://esphome.io/components/safe_mode" + ), + cv.Optional(CONF_REBOOT_TIMEOUT): cv.invalid( + f"'{CONF_SAFE_MODE}' (and its related configuration variables) has moved from 'ota' to its own component. See https://esphome.io/components/safe_mode" + ), + cv.Optional(CONF_SAFE_MODE): cv.invalid( + f"'{CONF_SAFE_MODE}' (and its related configuration variables) has moved from 'ota' to its own component. See https://esphome.io/components/safe_mode" + ), } ) .extend(BASE_OTA_SCHEMA) @@ -50,10 +51,8 @@ CONFIG_SCHEMA = ( ) -@coroutine_with_priority(50.0) +@coroutine_with_priority(52.0) async def to_code(config): - CORE.data[CONF_OTA] = {} - var = cg.new_Pvariable(config[CONF_ID]) await ota_to_code(var, config) cg.add(var.set_port(config[CONF_PORT])) @@ -63,10 +62,3 @@ async def to_code(config): cg.add_define("USE_OTA_VERSION", config[CONF_VERSION]) await cg.register_component(var, config) - - if config[CONF_SAFE_MODE]: - condition = var.should_enter_safe_mode( - config[CONF_NUM_ATTEMPTS], config[CONF_REBOOT_TIMEOUT] - ) - cg.add(RawExpression(f"if ({condition}) return")) - CORE.data[CONF_OTA][KEY_PAST_SAFE_MODE] = True diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index f2f1cfc6a8..9d5044aaeb 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -78,23 +78,9 @@ void ESPHomeOTAComponent::dump_config() { ESP_LOGCONFIG(TAG, " Password configured"); } #endif - if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 && - this->safe_mode_rtc_value_ != ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC) { - ESP_LOGW(TAG, "Last reset occurred too quickly; safe mode will be invoked in %" PRIu32 " restarts", - this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_); - } } -void ESPHomeOTAComponent::loop() { - this->handle_(); - - if (this->has_safe_mode_ && (millis() - this->safe_mode_start_time_) > this->safe_mode_enable_time_) { - this->has_safe_mode_ = false; - // successful boot, reset counter - ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter"); - this->clean_rtc(); - } -} +void ESPHomeOTAComponent::loop() { this->handle_(); } static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01; @@ -423,86 +409,4 @@ bool ESPHomeOTAComponent::writeall_(const uint8_t *buf, size_t len) { float ESPHomeOTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } uint16_t ESPHomeOTAComponent::get_port() const { return this->port_; } void ESPHomeOTAComponent::set_port(uint16_t port) { this->port_ = port; } - -void ESPHomeOTAComponent::set_safe_mode_pending(const bool &pending) { - if (!this->has_safe_mode_) - return; - - uint32_t current_rtc = this->read_rtc_(); - - if (pending && current_rtc != ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC) { - ESP_LOGI(TAG, "Device will enter safe mode on next boot"); - this->write_rtc_(ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC); - } - - if (!pending && current_rtc == ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC) { - ESP_LOGI(TAG, "Safe mode pending has been cleared"); - this->clean_rtc(); - } -} -bool ESPHomeOTAComponent::get_safe_mode_pending() { - return this->has_safe_mode_ && this->read_rtc_() == ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC; -} - -bool ESPHomeOTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) { - this->has_safe_mode_ = true; - this->safe_mode_start_time_ = millis(); - this->safe_mode_enable_time_ = enable_time; - this->safe_mode_num_attempts_ = num_attempts; - this->rtc_ = global_preferences->make_preference(233825507UL, false); - this->safe_mode_rtc_value_ = this->read_rtc_(); - - bool is_manual_safe_mode = this->safe_mode_rtc_value_ == ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC; - - if (is_manual_safe_mode) { - ESP_LOGI(TAG, "Safe mode has been entered manually"); - } else { - ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts", this->safe_mode_rtc_value_); - } - - if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) { - this->clean_rtc(); - - if (!is_manual_safe_mode) { - ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode"); - } - - this->status_set_error(); - this->set_timeout(enable_time, []() { - ESP_LOGE(TAG, "No OTA attempt made, restarting"); - App.reboot(); - }); - - // Delay here to allow power to stabilise before Wi-Fi/Ethernet is initialised. - delay(300); // NOLINT - App.setup(); - - ESP_LOGI(TAG, "Waiting for OTA attempt"); - - return true; - } else { - // increment counter - this->write_rtc_(this->safe_mode_rtc_value_ + 1); - return false; - } -} - -void ESPHomeOTAComponent::write_rtc_(uint32_t val) { - this->rtc_.save(&val); - global_preferences->sync(); -} - -uint32_t ESPHomeOTAComponent::read_rtc_() { - uint32_t val; - if (!this->rtc_.load(&val)) - return 0; - return val; -} - -void ESPHomeOTAComponent::clean_rtc() { this->write_rtc_(0); } - -void ESPHomeOTAComponent::on_safe_shutdown() { - if (this->has_safe_mode_ && this->read_rtc_() != ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC) - this->clean_rtc(); -} } // namespace esphome diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index e8f36f05ca..42629b4346 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -15,17 +15,9 @@ class ESPHomeOTAComponent : public ota::OTAComponent { void set_auth_password(const std::string &password) { password_ = password; } #endif // USE_OTA_PASSWORD - /// Manually set the port OTA should listen on. + /// Manually set the port OTA should listen on void set_port(uint16_t port); - bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time); - - /// Set to true if the next startup will enter safe mode - void set_safe_mode_pending(const bool &pending); - bool get_safe_mode_pending(); - - // ========== INTERNAL METHODS ========== - // (In most use cases you won't need these) void setup() override; void dump_config() override; float get_setup_priority() const override; @@ -33,14 +25,7 @@ class ESPHomeOTAComponent : public ota::OTAComponent { uint16_t get_port() const; - void clean_rtc(); - - void on_safe_shutdown() override; - protected: - void write_rtc_(uint32_t val); - uint32_t read_rtc_(); - void handle_(); bool readall_(uint8_t *buf, size_t len); bool writeall_(const uint8_t *buf, size_t len); @@ -53,16 +38,6 @@ class ESPHomeOTAComponent : public ota::OTAComponent { std::unique_ptr server_; std::unique_ptr client_; - - bool has_safe_mode_{false}; ///< stores whether safe mode can be enabled - uint32_t safe_mode_start_time_; ///< stores when safe mode was enabled - uint32_t safe_mode_enable_time_{60000}; ///< The time safe mode should be on for - uint32_t safe_mode_rtc_value_; - uint8_t safe_mode_num_attempts_; - ESPPreferenceObject rtc_; - - static const uint32_t ENTER_SAFE_MODE_MAGIC = - 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot }; } // namespace esphome diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 3d2956931c..4e447bfb2d 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -6,7 +6,7 @@ from esphome.core import CORE, coroutine_with_priority from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID CODEOWNERS = ["@esphome/core"] -AUTO_LOAD = ["md5"] +AUTO_LOAD = ["md5", "safe_mode"] IS_PLATFORM_COMPONENT = True @@ -76,7 +76,7 @@ BASE_OTA_SCHEMA = cv.Schema( ) -@coroutine_with_priority(51.0) +@coroutine_with_priority(54.0) async def to_code(config): cg.add_define("USE_OTA") diff --git a/esphome/components/safe_mode/__init__.py b/esphome/components/safe_mode/__init__.py index ab884bfee4..7f227d7dd1 100644 --- a/esphome/components/safe_mode/__init__.py +++ b/esphome/components/safe_mode/__init__.py @@ -1,5 +1,56 @@ +from esphome.cpp_generator import RawExpression import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import ( + CONF_DISABLED, + CONF_ID, + CONF_NUM_ATTEMPTS, + CONF_REBOOT_TIMEOUT, + CONF_SAFE_MODE, + KEY_PAST_SAFE_MODE, +) +from esphome.core import CORE, coroutine_with_priority -CODEOWNERS = ["@paulmonigatti", "@jsuanet"] + +CODEOWNERS = ["@paulmonigatti", "@jsuanet", "@kbx81"] safe_mode_ns = cg.esphome_ns.namespace("safe_mode") +SafeModeComponent = safe_mode_ns.class_("SafeModeComponent", cg.Component) + + +def _remove_id_if_disabled(value): + value = value.copy() + if value[CONF_DISABLED]: + value.pop(CONF_ID) + return value + + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(SafeModeComponent), + cv.Optional(CONF_DISABLED, default=False): cv.boolean, + cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int, + cv.Optional( + CONF_REBOOT_TIMEOUT, default="5min" + ): cv.positive_time_period_milliseconds, + } + ).extend(cv.COMPONENT_SCHEMA), + _remove_id_if_disabled, +) + + +@coroutine_with_priority(50.0) +async def to_code(config): + if config[CONF_DISABLED]: + return + + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + + condition = var.should_enter_safe_mode( + config[CONF_NUM_ATTEMPTS], config[CONF_REBOOT_TIMEOUT] + ) + cg.add(RawExpression(f"if ({condition}) return")) + CORE.data[CONF_SAFE_MODE] = {} + CORE.data[CONF_SAFE_MODE][KEY_PAST_SAFE_MODE] = True diff --git a/esphome/components/safe_mode/button/__init__.py b/esphome/components/safe_mode/button/__init__.py index bd51d2e038..5662db8f7e 100644 --- a/esphome/components/safe_mode/button/__init__.py +++ b/esphome/components/safe_mode/button/__init__.py @@ -1,16 +1,15 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import button -from esphome.components.esphome.ota import ESPHomeOTAComponent from esphome.const import ( - CONF_ESPHOME, + CONF_SAFE_MODE, DEVICE_CLASS_RESTART, ENTITY_CATEGORY_CONFIG, ICON_RESTART_ALERT, ) -from .. import safe_mode_ns +from .. import safe_mode_ns, SafeModeComponent -DEPENDENCIES = ["ota.esphome"] +DEPENDENCIES = ["safe_mode"] SafeModeButton = safe_mode_ns.class_("SafeModeButton", button.Button, cg.Component) @@ -21,7 +20,7 @@ CONFIG_SCHEMA = ( entity_category=ENTITY_CATEGORY_CONFIG, icon=ICON_RESTART_ALERT, ) - .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(ESPHomeOTAComponent)}) + .extend({cv.GenerateID(CONF_SAFE_MODE): cv.use_id(SafeModeComponent)}) .extend(cv.COMPONENT_SCHEMA) ) @@ -30,5 +29,5 @@ async def to_code(config): var = await button.new_button(config) await cg.register_component(var, config) - ota = await cg.get_variable(config[CONF_ESPHOME]) - cg.add(var.set_ota(ota)) + safe_mode_component = await cg.get_variable(config[CONF_SAFE_MODE]) + cg.add(var.set_safe_mode(safe_mode_component)) diff --git a/esphome/components/safe_mode/button/safe_mode_button.cpp b/esphome/components/safe_mode/button/safe_mode_button.cpp index d513b79c12..261688807a 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.cpp +++ b/esphome/components/safe_mode/button/safe_mode_button.cpp @@ -8,11 +8,13 @@ namespace safe_mode { static const char *const TAG = "safe_mode.button"; -void SafeModeButton::set_ota(esphome::ESPHomeOTAComponent *ota) { this->ota_ = ota; } +void SafeModeButton::set_safe_mode(SafeModeComponent *safe_mode_component) { + this->safe_mode_component_ = safe_mode_component; +} void SafeModeButton::press_action() { ESP_LOGI(TAG, "Restarting device in safe mode..."); - this->ota_->set_safe_mode_pending(true); + this->safe_mode_component_->set_safe_mode_pending(true); // Let MQTT settle a bit delay(100); // NOLINT diff --git a/esphome/components/safe_mode/button/safe_mode_button.h b/esphome/components/safe_mode/button/safe_mode_button.h index a306735b7f..fea0955abb 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.h +++ b/esphome/components/safe_mode/button/safe_mode_button.h @@ -1,7 +1,7 @@ #pragma once #include "esphome/components/button/button.h" -#include "esphome/components/esphome/ota/ota_esphome.h" +#include "esphome/components/safe_mode/safe_mode.h" #include "esphome/core/component.h" namespace esphome { @@ -10,10 +10,10 @@ namespace safe_mode { class SafeModeButton : public button::Button, public Component { public: void dump_config() override; - void set_ota(esphome::ESPHomeOTAComponent *ota); + void set_safe_mode(SafeModeComponent *safe_mode_component); protected: - esphome::ESPHomeOTAComponent *ota_; + SafeModeComponent *safe_mode_component_; void press_action() override; }; diff --git a/esphome/components/safe_mode/safe_mode.cpp b/esphome/components/safe_mode/safe_mode.cpp new file mode 100644 index 0000000000..06772ae1e0 --- /dev/null +++ b/esphome/components/safe_mode/safe_mode.cpp @@ -0,0 +1,125 @@ +#include "safe_mode.h" + +#include "esphome/core/application.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" + +#include +#include +#include + +namespace esphome { +namespace safe_mode { + +static const char *const TAG = "safe_mode"; + +void SafeModeComponent::dump_config() { + ESP_LOGCONFIG(TAG, "Safe Mode:"); + ESP_LOGCONFIG(TAG, " Invoke after %u boot attempts", this->safe_mode_num_attempts_); + ESP_LOGCONFIG(TAG, " Remain in safe mode for %" PRIu32 " seconds", + this->safe_mode_enable_time_ / 1000); // because milliseconds + + if (this->safe_mode_rtc_value_ > 1 && this->safe_mode_rtc_value_ != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { + auto remaining_restarts = this->safe_mode_num_attempts_ - this->safe_mode_rtc_value_; + if (remaining_restarts) { + ESP_LOGW(TAG, "Last reset occurred too quickly; safe mode will be invoked in %" PRIu32 " restarts", + remaining_restarts); + } else { + ESP_LOGW(TAG, "SAFE MODE IS ACTIVE"); + } + } +} + +float SafeModeComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } + +void SafeModeComponent::loop() { + if (!this->boot_successful_ && (millis() - this->safe_mode_start_time_) > this->safe_mode_enable_time_) { + // successful boot, reset counter + ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter"); + this->clean_rtc(); + this->boot_successful_ = true; + } +} + +void SafeModeComponent::set_safe_mode_pending(const bool &pending) { + uint32_t current_rtc = this->read_rtc_(); + + if (pending && current_rtc != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { + ESP_LOGI(TAG, "Device will enter safe mode on next boot"); + this->write_rtc_(SafeModeComponent::ENTER_SAFE_MODE_MAGIC); + } + + if (!pending && current_rtc == SafeModeComponent::ENTER_SAFE_MODE_MAGIC) { + ESP_LOGI(TAG, "Safe mode pending has been cleared"); + this->clean_rtc(); + } +} + +bool SafeModeComponent::get_safe_mode_pending() { + return this->read_rtc_() == SafeModeComponent::ENTER_SAFE_MODE_MAGIC; +} + +bool SafeModeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) { + this->safe_mode_start_time_ = millis(); + this->safe_mode_enable_time_ = enable_time; + this->safe_mode_num_attempts_ = num_attempts; + this->rtc_ = global_preferences->make_preference(233825507UL, false); + this->safe_mode_rtc_value_ = this->read_rtc_(); + + bool is_manual_safe_mode = this->safe_mode_rtc_value_ == SafeModeComponent::ENTER_SAFE_MODE_MAGIC; + + if (is_manual_safe_mode) { + ESP_LOGI(TAG, "Safe mode invoked manually"); + } else { + ESP_LOGCONFIG(TAG, "There have been %" PRIu32 " suspected unsuccessful boot attempts", this->safe_mode_rtc_value_); + } + + if (this->safe_mode_rtc_value_ >= num_attempts || is_manual_safe_mode) { + this->clean_rtc(); + + if (!is_manual_safe_mode) { + ESP_LOGE(TAG, "Boot loop detected. Proceeding to safe mode"); + } + + this->status_set_error(); + this->set_timeout(enable_time, []() { + ESP_LOGW(TAG, "Safe mode enable time has elapsed -- restarting"); + App.reboot(); + }); + + // Delay here to allow power to stabilize before Wi-Fi/Ethernet is initialised + delay(300); // NOLINT + App.setup(); + + ESP_LOGW(TAG, "SAFE MODE IS ACTIVE"); + + return true; + } else { + // increment counter + this->write_rtc_(this->safe_mode_rtc_value_ + 1); + return false; + } +} + +void SafeModeComponent::write_rtc_(uint32_t val) { + this->rtc_.save(&val); + global_preferences->sync(); +} + +uint32_t SafeModeComponent::read_rtc_() { + uint32_t val; + if (!this->rtc_.load(&val)) + return 0; + return val; +} + +void SafeModeComponent::clean_rtc() { this->write_rtc_(0); } + +void SafeModeComponent::on_safe_shutdown() { + if (this->read_rtc_() != SafeModeComponent::ENTER_SAFE_MODE_MAGIC) + this->clean_rtc(); +} + +} // namespace safe_mode +} // namespace esphome diff --git a/esphome/components/safe_mode/safe_mode.h b/esphome/components/safe_mode/safe_mode.h new file mode 100644 index 0000000000..6c7450a6ff --- /dev/null +++ b/esphome/components/safe_mode/safe_mode.h @@ -0,0 +1,44 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace safe_mode { + +/// SafeModeComponent provides a safe way to recover from repeated boot failures +class SafeModeComponent : public Component { + public: + bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time); + + /// Set to true if the next startup will enter safe mode + void set_safe_mode_pending(const bool &pending); + bool get_safe_mode_pending(); + + void dump_config() override; + float get_setup_priority() const override; + void loop() override; + + void clean_rtc(); + + void on_safe_shutdown() override; + + protected: + void write_rtc_(uint32_t val); + uint32_t read_rtc_(); + + bool boot_successful_{false}; ///< set to true after boot is considered successful + uint32_t safe_mode_start_time_; ///< stores when safe mode was enabled + uint32_t safe_mode_enable_time_{60000}; ///< The time safe mode should remain active for + uint32_t safe_mode_rtc_value_; + uint8_t safe_mode_num_attempts_; + ESPPreferenceObject rtc_; + + static const uint32_t ENTER_SAFE_MODE_MAGIC = + 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot +}; + +} // namespace safe_mode +} // namespace esphome diff --git a/esphome/components/safe_mode/switch/__init__.py b/esphome/components/safe_mode/switch/__init__.py index 0f8e500482..7271358149 100644 --- a/esphome/components/safe_mode/switch/__init__.py +++ b/esphome/components/safe_mode/switch/__init__.py @@ -1,15 +1,14 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch -from esphome.components.esphome.ota import ESPHomeOTAComponent from esphome.const import ( - CONF_ESPHOME, + CONF_SAFE_MODE, ENTITY_CATEGORY_CONFIG, ICON_RESTART_ALERT, ) -from .. import safe_mode_ns +from .. import safe_mode_ns, SafeModeComponent -DEPENDENCIES = ["ota.esphome"] +DEPENDENCIES = ["safe_mode"] SafeModeSwitch = safe_mode_ns.class_("SafeModeSwitch", switch.Switch, cg.Component) @@ -20,7 +19,7 @@ CONFIG_SCHEMA = ( entity_category=ENTITY_CATEGORY_CONFIG, icon=ICON_RESTART_ALERT, ) - .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(ESPHomeOTAComponent)}) + .extend({cv.GenerateID(CONF_SAFE_MODE): cv.use_id(SafeModeComponent)}) .extend(cv.COMPONENT_SCHEMA) ) @@ -29,5 +28,5 @@ async def to_code(config): var = await switch.new_switch(config) await cg.register_component(var, config) - ota = await cg.get_variable(config[CONF_ESPHOME]) - cg.add(var.set_ota(ota)) + safe_mode_component = await cg.get_variable(config[CONF_SAFE_MODE]) + cg.add(var.set_safe_mode(safe_mode_component)) diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.cpp b/esphome/components/safe_mode/switch/safe_mode_switch.cpp index 71408df140..13b35ed210 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.cpp +++ b/esphome/components/safe_mode/switch/safe_mode_switch.cpp @@ -6,9 +6,11 @@ namespace esphome { namespace safe_mode { -static const char *const TAG = "safe_mode_switch"; +static const char *const TAG = "safe_mode.switch"; -void SafeModeSwitch::set_ota(esphome::ESPHomeOTAComponent *ota) { this->ota_ = ota; } +void SafeModeSwitch::set_safe_mode(SafeModeComponent *safe_mode_component) { + this->safe_mode_component_ = safe_mode_component; +} void SafeModeSwitch::write_state(bool state) { // Acknowledge @@ -16,13 +18,14 @@ void SafeModeSwitch::write_state(bool state) { if (state) { ESP_LOGI(TAG, "Restarting device in safe mode..."); - this->ota_->set_safe_mode_pending(true); + this->safe_mode_component_->set_safe_mode_pending(true); // Let MQTT settle a bit delay(100); // NOLINT App.safe_reboot(); } } + void SafeModeSwitch::dump_config() { LOG_SWITCH("", "Safe Mode Switch", this); } } // namespace safe_mode diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.h b/esphome/components/safe_mode/switch/safe_mode_switch.h index 5bd15a44de..24e660c803 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.h +++ b/esphome/components/safe_mode/switch/safe_mode_switch.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/components/esphome/ota/ota_esphome.h" +#include "esphome/components/safe_mode/safe_mode.h" #include "esphome/components/switch/switch.h" #include "esphome/core/component.h" @@ -10,10 +10,10 @@ namespace safe_mode { class SafeModeSwitch : public switch_::Switch, public Component { public: void dump_config() override; - void set_ota(esphome::ESPHomeOTAComponent *ota); + void set_safe_mode(SafeModeComponent *safe_mode_component); protected: - esphome::ESPHomeOTAComponent *ota_; + SafeModeComponent *safe_mode_component_; void write_state(bool state) override; }; diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index ce494e5d9d..825224bb9d 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -3,12 +3,9 @@ import logging from esphome.const import ( CONF_DISABLED_BY_DEFAULT, CONF_ENTITY_CATEGORY, - CONF_ESPHOME, CONF_ICON, CONF_INTERNAL, CONF_NAME, - CONF_OTA, - CONF_PLATFORM, CONF_SAFE_MODE, CONF_SETUP_PRIORITY, CONF_TYPE_ID, @@ -141,22 +138,12 @@ async def build_registry_list(registry, config): async def past_safe_mode(): - ota_conf = {} - for ota_item in CORE.config.get(CONF_OTA, []): - if ota_item[CONF_PLATFORM] == CONF_ESPHOME: - ota_conf = ota_item - break - - if not ota_conf: - return - - safe_mode_enabled = ota_conf[CONF_SAFE_MODE] - if not safe_mode_enabled: + if CONF_SAFE_MODE not in CORE.config: return def _safe_mode_generator(): while True: - if CORE.data.get(CONF_OTA, {}).get(KEY_PAST_SAFE_MODE, False): + if CORE.data.get(CONF_SAFE_MODE, {}).get(KEY_PAST_SAFE_MODE, False): return yield diff --git a/tests/components/ota/common.yaml b/tests/components/ota/common.yaml index 4910e2d891..1433dada1f 100644 --- a/tests/components/ota/common.yaml +++ b/tests/components/ota/common.yaml @@ -4,11 +4,8 @@ wifi: ota: - platform: esphome - safe_mode: true password: "superlongpasswordthatnoonewillknow" port: 3286 - reboot_timeout: 2min - num_attempts: 5 on_begin: then: - logger.log: "OTA start" diff --git a/tests/components/safe_mode/common.yaml b/tests/components/safe_mode/common.yaml index 1dfc516254..9c1d1ad3f9 100644 --- a/tests/components/safe_mode/common.yaml +++ b/tests/components/safe_mode/common.yaml @@ -2,9 +2,9 @@ wifi: ssid: MySSID password: password1 -ota: - - platform: esphome - safe_mode: true +safe_mode: + num_attempts: 3 + reboot_timeout: 2min button: - platform: safe_mode diff --git a/tests/test1.yaml b/tests/test1.yaml index dc46b55c44..2a20a1bb45 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -264,13 +264,14 @@ uart: parity: EVEN baud_rate: 9600 +safe_mode: + num_attempts: 3 + reboot_timeout: 2min + ota: - platform: esphome - safe_mode: true password: "superlongpasswordthatnoonewillknow" port: 3286 - reboot_timeout: 2min - num_attempts: 5 on_state_change: then: lambda: >- diff --git a/tests/test2.yaml b/tests/test2.yaml index 54ff4807a3..92977697c1 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -79,11 +79,11 @@ uart: sequence: - lambda: UARTDebug::log_hex(direction, bytes, ':'); +safe_mode: + ota: - platform: esphome - safe_mode: true port: 3286 - num_attempts: 15 logger: level: DEBUG diff --git a/tests/test3.yaml b/tests/test3.yaml index 7554d4bcb2..d10413b142 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -327,11 +327,13 @@ modbus: vbus: uart_id: uart_4 +safe_mode: + num_attempts: 5 + reboot_timeout: 10min + ota: - platform: esphome - safe_mode: true port: 3286 - reboot_timeout: 15min logger: hardware_uart: UART1 diff --git a/tests/test4.yaml b/tests/test4.yaml index 86beee81c6..c9e8a27317 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -102,9 +102,10 @@ uart: baud_rate: 1200 parity: EVEN +safe_mode: + ota: - platform: esphome - safe_mode: true port: 3286 logger: