From d88bee1a2854624c556e704e28ebe4c0b9805585 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Mon, 1 Apr 2024 21:47:56 -0500 Subject: [PATCH 01/26] Separate OTABackend from OTA component --- esphome/__main__.py | 28 +-- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 13 +- esphome/components/esphome/ota/__init__.py | 152 +++++++++++++++ esphome/components/esphome/ota/automation.h | 71 +++++++ .../ota/ota_esphome.cpp} | 182 +++++++++--------- .../ota/ota_esphome.h} | 57 ++---- esphome/components/ota/__init__.py | 136 +------------ esphome/components/ota/automation.h | 71 ------- esphome/components/ota/ota_backend.h | 31 ++- .../ota/ota_backend_arduino_esp32.cpp | 3 +- .../ota/ota_backend_arduino_esp32.h | 6 +- .../ota/ota_backend_arduino_esp8266.cpp | 7 +- .../ota/ota_backend_arduino_esp8266.h | 5 +- .../ota/ota_backend_arduino_libretiny.cpp | 7 +- .../ota/ota_backend_arduino_libretiny.h | 5 +- .../ota/ota_backend_arduino_rp2040.cpp | 7 +- .../ota/ota_backend_arduino_rp2040.h | 7 +- .../components/ota/ota_backend_esp_idf.cpp | 11 +- esphome/components/ota/ota_backend_esp_idf.h | 8 +- .../components/safe_mode/button/__init__.py | 14 +- .../safe_mode/button/safe_mode_button.cpp | 2 +- .../safe_mode/button/safe_mode_button.h | 8 +- .../components/safe_mode/switch/__init__.py | 12 +- .../safe_mode/switch/safe_mode_switch.cpp | 4 +- .../safe_mode/switch/safe_mode_switch.h | 8 +- esphome/cpp_helpers.py | 22 ++- tests/dummy_main.cpp | 4 +- tests/test1.yaml | 47 ++--- tests/test11.5.yaml | 1 + tests/test2.yaml | 7 +- tests/test3.1.yaml | 3 +- tests/test3.yaml | 7 +- tests/test4.yaml | 5 +- tests/test5.yaml | 1 + tests/test6.yaml | 1 + tests/test9.1.yaml | 1 + tests/test9.yaml | 1 + 37 files changed, 502 insertions(+), 453 deletions(-) create mode 100644 esphome/components/esphome/ota/__init__.py create mode 100644 esphome/components/esphome/ota/automation.h rename esphome/components/{ota/ota_component.cpp => esphome/ota/ota_esphome.cpp} (69%) rename esphome/components/{ota/ota_component.h => esphome/ota/ota_esphome.h} (53%) delete mode 100644 esphome/components/ota/automation.h diff --git a/esphome/__main__.py b/esphome/__main__.py index dcd2dddb4b..43550ec8df 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -18,22 +18,23 @@ from esphome.const import ( CONF_BAUD_RATE, CONF_BROKER, CONF_DEASSERT_RTS_DTR, + CONF_DISABLED, + CONF_ESPHOME, CONF_LOGGER, + CONF_MDNS, + CONF_MQTT, CONF_NAME, CONF_OTA, - CONF_MQTT, - CONF_MDNS, - CONF_DISABLED, CONF_PASSWORD, - CONF_PORT, - CONF_ESPHOME, + CONF_PLATFORM, CONF_PLATFORMIO_OPTIONS, + CONF_PORT, CONF_SUBSTITUTIONS, PLATFORM_BK72XX, - PLATFORM_RTL87XX, PLATFORM_ESP32, PLATFORM_ESP8266, PLATFORM_RP2040, + PLATFORM_RTL87XX, SECRETS_FILES, ) from esphome.core import CORE, EsphomeError, coroutine @@ -65,7 +66,7 @@ def choose_prompt(options, purpose: str = None): f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:' ) for i, (desc, _) in enumerate(options): - safe_print(f" [{i+1}] {desc}") + safe_print(f" [{i + 1}] {desc}") while True: opt = input("(number): ") @@ -330,15 +331,20 @@ def upload_program(config, args, host): return 1 # Unknown target platform - if CONF_OTA not in config: + ota_conf = {} + if CONF_OTA in config: + for ota_item in config.get(CONF_OTA): + if ota_item[CONF_PLATFORM] == CONF_ESPHOME: + ota_conf = ota_item + break + + if not ota_conf: raise EsphomeError( - "Cannot upload Over the Air as the config does not include the ota: " - "component" + f"Cannot upload Over the Air as the {CONF_OTA} configuration is not present or does not include {CONF_PLATFORM}: {CONF_ESPHOME}" ) from esphome import espota2 - ota_conf = config[CONF_OTA] remote_port = ota_conf[CONF_PORT] password = ota_conf.get(CONF_PASSWORD, "") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index a5bbd85b47..e102fe41c4 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -18,7 +18,7 @@ #include <cinttypes> #ifdef USE_OTA -#include "esphome/components/ota/ota_component.h" +#include "esphome/components/esphome/ota/ota_esphome.h" #endif #ifdef USE_ARDUINO @@ -58,11 +58,12 @@ void ESP32BLETracker::setup() { this->scanner_idle_ = true; #ifdef USE_OTA - ota::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) { - if (state == ota::OTA_STARTED) { - this->stop_scan(); - } - }); + ota_esphome::global_ota_component->add_on_state_callback( + [this](ota_esphome::OTAESPHomeState state, float progress, uint8_t error) { + if (state == ota_esphome::OTA_STARTED) { + this->stop_scan(); + } + }); #endif } diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py new file mode 100644 index 0000000000..3143d5408d --- /dev/null +++ b/esphome/components/esphome/ota/__init__.py @@ -0,0 +1,152 @@ +from esphome.cpp_generator import RawExpression +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.const import ( + CONF_ID, + CONF_NUM_ATTEMPTS, + CONF_OTA, + CONF_PASSWORD, + CONF_PORT, + CONF_REBOOT_TIMEOUT, + CONF_SAFE_MODE, + CONF_TRIGGER_ID, + CONF_VERSION, + KEY_PAST_SAFE_MODE, +) +from esphome.core import CORE, coroutine_with_priority + + +CODEOWNERS = ["@esphome/core"] +AUTO_LOAD = ["md5", "socket"] +DEPENDENCIES = ["network", "ota"] + +CONF_ON_BEGIN = "on_begin" +CONF_ON_END = "on_end" +CONF_ON_ERROR = "on_error" +CONF_ON_PROGRESS = "on_progress" +CONF_ON_STATE_CHANGE = "on_state_change" + +ota_esphome = cg.esphome_ns.namespace("ota_esphome") + +OTAESPHomeComponent = ota_esphome.class_("OTAESPHomeComponent", cg.Component) +OTAESPHomeEndTrigger = ota_esphome.class_( + "OTAESPHomeEndTrigger", automation.Trigger.template() +) +OTAESPHomeErrorTrigger = ota_esphome.class_( + "OTAESPHomeErrorTrigger", automation.Trigger.template() +) +OTAESPHomeProgressTrigger = ota_esphome.class_( + "OTAESPHomeProgressTrigger", automation.Trigger.template() +) +OTAESPHomeStartTrigger = ota_esphome.class_( + "OTAESPHomeStartTrigger", automation.Trigger.template() +) +OTAESPHomeStateChangeTrigger = ota_esphome.class_( + "OTAESPHomeStateChangeTrigger", automation.Trigger.template() +) + +OTAESPHomeState = ota_esphome.enum("OTAESPHomeState") + + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(): cv.declare_id(OTAESPHomeComponent), + 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, + esp8266=8266, + esp32=3232, + rp2040=2040, + bk72xx=8892, + 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_ON_STATE_CHANGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + OTAESPHomeStateChangeTrigger + ), + } + ), + cv.Optional(CONF_ON_BEGIN): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAESPHomeStartTrigger), + } + ), + cv.Optional(CONF_ON_END): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAESPHomeEndTrigger), + } + ), + cv.Optional(CONF_ON_ERROR): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAESPHomeErrorTrigger), + } + ), + cv.Optional(CONF_ON_PROGRESS): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( + OTAESPHomeProgressTrigger + ), + } + ), + } +).extend(cv.COMPONENT_SCHEMA) + + +@coroutine_with_priority(50.0) +async def to_code(config): + CORE.data[CONF_OTA] = {} + + var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_port(config[CONF_PORT])) + cg.add_define("USE_OTA") + if CONF_PASSWORD in config: + cg.add(var.set_auth_password(config[CONF_PASSWORD])) + cg.add_define("USE_OTA_PASSWORD") + 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 + + if CORE.is_esp32 and CORE.using_arduino: + cg.add_library("Update", None) + + if CORE.is_rp2040 and CORE.using_arduino: + cg.add_library("Updater", None) + + use_state_callback = False + for conf in config.get(CONF_ON_STATE_CHANGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(OTAESPHomeState, "state")], conf) + use_state_callback = True + for conf in config.get(CONF_ON_BEGIN, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_PROGRESS, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(float, "x")], conf) + use_state_callback = True + for conf in config.get(CONF_ON_END, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_ERROR, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.uint8, "x")], conf) + use_state_callback = True + if use_state_callback: + cg.add_define("USE_OTA_STATE_CALLBACK") diff --git a/esphome/components/esphome/ota/automation.h b/esphome/components/esphome/ota/automation.h new file mode 100644 index 0000000000..a23b1c5590 --- /dev/null +++ b/esphome/components/esphome/ota/automation.h @@ -0,0 +1,71 @@ +#pragma once + +#ifdef USE_OTA_STATE_CALLBACK +#include "ota_esphome.h" + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/core/automation.h" + +namespace esphome { +namespace ota_esphome { + +class OTAESPHomeStateChangeTrigger : public Trigger<OTAESPHomeState> { + public: + explicit OTAESPHomeStateChangeTrigger(OTAESPHomeComponent *parent) { + parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { + if (!parent->is_failed()) { + return trigger(state); + } + }); + } +}; + +class OTAESPHomeStartTrigger : public Trigger<> { + public: + explicit OTAESPHomeStartTrigger(OTAESPHomeComponent *parent) { + parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { + if (state == OTA_STARTED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAESPHomeProgressTrigger : public Trigger<float> { + public: + explicit OTAESPHomeProgressTrigger(OTAESPHomeComponent *parent) { + parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { + if (state == OTA_IN_PROGRESS && !parent->is_failed()) { + trigger(progress); + } + }); + } +}; + +class OTAESPHomeEndTrigger : public Trigger<> { + public: + explicit OTAESPHomeEndTrigger(OTAESPHomeComponent *parent) { + parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { + if (state == OTA_COMPLETED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAESPHomeErrorTrigger : public Trigger<uint8_t> { + public: + explicit OTAESPHomeErrorTrigger(OTAESPHomeComponent *parent) { + parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { + if (state == OTA_ERROR && !parent->is_failed()) { + trigger(error); + } + }); + } +}; + +} // namespace ota_esphome +} // namespace esphome + +#endif // USE_OTA_STATE_CALLBACK diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/esphome/ota/ota_esphome.cpp similarity index 69% rename from esphome/components/ota/ota_component.cpp rename to esphome/components/esphome/ota/ota_esphome.cpp index 15af14ff1a..447f0587f8 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -1,55 +1,55 @@ -#include "ota_component.h" -#include "ota_backend.h" -#include "ota_backend_arduino_esp32.h" -#include "ota_backend_arduino_esp8266.h" -#include "ota_backend_arduino_rp2040.h" -#include "ota_backend_arduino_libretiny.h" -#include "ota_backend_esp_idf.h" +#include "ota_esphome.h" -#include "esphome/core/log.h" -#include "esphome/core/application.h" -#include "esphome/core/hal.h" -#include "esphome/core/util.h" #include "esphome/components/md5/md5.h" #include "esphome/components/network/util.h" +#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/ota/ota_backend_arduino_esp32.h" +#include "esphome/components/ota/ota_backend_arduino_esp8266.h" +#include "esphome/components/ota/ota_backend_arduino_libretiny.h" +#include "esphome/components/ota/ota_backend_arduino_rp2040.h" +#include "esphome/components/ota/ota_backend_esp_idf.h" +#include "esphome/core/application.h" +#include "esphome/core/hal.h" +#include "esphome/core/log.h" +#include "esphome/core/util.h" #include <cerrno> #include <cstdio> namespace esphome { -namespace ota { +namespace ota_esphome { -static const char *const TAG = "ota"; +static const char *const TAG = "esphome.ota"; static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; -OTAComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +OTAESPHomeComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -std::unique_ptr<OTABackend> make_ota_backend() { +std::unique_ptr<ota::OTABackend> make_ota_backend() { #ifdef USE_ARDUINO #ifdef USE_ESP8266 - return make_unique<ArduinoESP8266OTABackend>(); + return make_unique<ota::ArduinoESP8266OTABackend>(); #endif // USE_ESP8266 #ifdef USE_ESP32 - return make_unique<ArduinoESP32OTABackend>(); + return make_unique<ota::ArduinoESP32OTABackend>(); #endif // USE_ESP32 #endif // USE_ARDUINO #ifdef USE_ESP_IDF - return make_unique<IDFOTABackend>(); + return make_unique<ota::IDFOTABackend>(); #endif // USE_ESP_IDF #ifdef USE_RP2040 - return make_unique<ArduinoRP2040OTABackend>(); + return make_unique<ota::ArduinoRP2040OTABackend>(); #endif // USE_RP2040 #ifdef USE_LIBRETINY - return make_unique<ArduinoLibreTinyOTABackend>(); + return make_unique<ota::ArduinoLibreTinyOTABackend>(); #endif } -OTAComponent::OTAComponent() { global_ota_component = this; } +OTAESPHomeComponent::OTAESPHomeComponent() { global_ota_component = this; } -void OTAComponent::setup() { +void OTAESPHomeComponent::setup() { server_ = socket::socket_ip(SOCK_STREAM, 0); if (server_ == nullptr) { - ESP_LOGW(TAG, "Could not create socket."); + ESP_LOGW(TAG, "Could not create socket"); this->mark_failed(); return; } @@ -88,41 +88,39 @@ void OTAComponent::setup() { this->mark_failed(); return; } - - this->dump_config(); } -void OTAComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Over-The-Air Updates:"); +void OTAESPHomeComponent::dump_config() { + ESP_LOGCONFIG(TAG, "Over-The-Air updates:"); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); + ESP_LOGCONFIG(TAG, " OTA version: %d", USE_OTA_VERSION); #ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { - ESP_LOGCONFIG(TAG, " Using Password."); + ESP_LOGCONFIG(TAG, " Password configured"); } #endif - ESP_LOGCONFIG(TAG, " OTA version: %d.", USE_OTA_VERSION); if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 && - this->safe_mode_rtc_value_ != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { - ESP_LOGW(TAG, "Last Boot was an unhandled reset, will proceed to safe mode in %" PRIu32 " restarts", + this->safe_mode_rtc_value_ != OTAESPHomeComponent::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 OTAComponent::loop() { +void OTAESPHomeComponent::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."); + ESP_LOGI(TAG, "Boot seems successful; resetting boot loop counter"); this->clean_rtc(); } } static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01; -void OTAComponent::handle_() { - OTAResponseTypes error_code = OTA_RESPONSE_ERROR_UNKNOWN; +void OTAESPHomeComponent::handle_() { + ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN; bool update_started = false; size_t total = 0; uint32_t last_progress = 0; @@ -130,7 +128,7 @@ void OTAComponent::handle_() { char *sbuf = reinterpret_cast<char *>(buf); size_t ota_size; uint8_t ota_features; - std::unique_ptr<OTABackend> backend; + std::unique_ptr<ota::OTABackend> backend; (void) ota_features; #if USE_OTA_VERSION == 2 size_t size_acknowledged = 0; @@ -147,30 +145,30 @@ void OTAComponent::handle_() { int enable = 1; int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int)); if (err != 0) { - ESP_LOGW(TAG, "Socket could not enable tcp nodelay, errno: %d", errno); + ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno); return; } - ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_->getpeername().c_str()); + ESP_LOGD(TAG, "Starting OTA update from %s...", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_STARTED, 0.0f, 0); #endif if (!this->readall_(buf, 5)) { - ESP_LOGW(TAG, "Reading magic bytes failed!"); + ESP_LOGW(TAG, "Reading magic bytes failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } // 0x6C, 0x26, 0xF7, 0x5C, 0x45 if (buf[0] != 0x6C || buf[1] != 0x26 || buf[2] != 0xF7 || buf[3] != 0x5C || buf[4] != 0x45) { ESP_LOGW(TAG, "Magic bytes do not match! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", buf[0], buf[1], buf[2], buf[3], buf[4]); - error_code = OTA_RESPONSE_ERROR_MAGIC; + error_code = ota::OTA_RESPONSE_ERROR_MAGIC; goto error; // NOLINT(cppcoreguidelines-avoid-goto) } // Send OK and version - 2 bytes - buf[0] = OTA_RESPONSE_OK; + buf[0] = ota::OTA_RESPONSE_OK; buf[1] = USE_OTA_VERSION; this->writeall_(buf, 2); @@ -178,23 +176,23 @@ void OTAComponent::handle_() { // Read features - 1 byte if (!this->readall_(buf, 1)) { - ESP_LOGW(TAG, "Reading features failed!"); + ESP_LOGW(TAG, "Reading features failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } ota_features = buf[0]; // NOLINT ESP_LOGV(TAG, "OTA features is 0x%02X", ota_features); // Acknowledge header - 1 byte - buf[0] = OTA_RESPONSE_HEADER_OK; + buf[0] = ota::OTA_RESPONSE_HEADER_OK; if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) { - buf[0] = OTA_RESPONSE_SUPPORTS_COMPRESSION; + buf[0] = ota::OTA_RESPONSE_SUPPORTS_COMPRESSION; } this->writeall_(buf, 1); #ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { - buf[0] = OTA_RESPONSE_REQUEST_AUTH; + buf[0] = ota::OTA_RESPONSE_REQUEST_AUTH; this->writeall_(buf, 1); md5::MD5Digest md5{}; md5.init(); @@ -206,7 +204,7 @@ void OTAComponent::handle_() { // Send nonce, 32 bytes hex MD5 if (!this->writeall_(reinterpret_cast<uint8_t *>(sbuf), 32)) { - ESP_LOGW(TAG, "Auth: Writing nonce failed!"); + ESP_LOGW(TAG, "Auth: Writing nonce failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } @@ -218,7 +216,7 @@ void OTAComponent::handle_() { // Receive cnonce, 32 bytes hex MD5 if (!this->readall_(buf, 32)) { - ESP_LOGW(TAG, "Auth: Reading cnonce failed!"); + ESP_LOGW(TAG, "Auth: Reading cnonce failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } sbuf[32] = '\0'; @@ -233,7 +231,7 @@ void OTAComponent::handle_() { // Receive result, 32 bytes hex MD5 if (!this->readall_(buf + 64, 32)) { - ESP_LOGW(TAG, "Auth: Reading response failed!"); + ESP_LOGW(TAG, "Auth: Reading response failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } sbuf[64 + 32] = '\0'; @@ -244,20 +242,20 @@ void OTAComponent::handle_() { matches = matches && buf[i] == buf[64 + i]; if (!matches) { - ESP_LOGW(TAG, "Auth failed! Passwords do not match!"); - error_code = OTA_RESPONSE_ERROR_AUTH_INVALID; + ESP_LOGW(TAG, "Auth failed! Passwords do not match"); + error_code = ota::OTA_RESPONSE_ERROR_AUTH_INVALID; goto error; // NOLINT(cppcoreguidelines-avoid-goto) } } #endif // USE_OTA_PASSWORD // Acknowledge auth OK - 1 byte - buf[0] = OTA_RESPONSE_AUTH_OK; + buf[0] = ota::OTA_RESPONSE_AUTH_OK; this->writeall_(buf, 1); // Read size, 4 bytes MSB first if (!this->readall_(buf, 4)) { - ESP_LOGW(TAG, "Reading size failed!"); + ESP_LOGW(TAG, "Reading size failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } ota_size = 0; @@ -268,17 +266,17 @@ void OTAComponent::handle_() { ESP_LOGV(TAG, "OTA size is %u bytes", ota_size); error_code = backend->begin(ota_size); - if (error_code != OTA_RESPONSE_OK) + if (error_code != ota::OTA_RESPONSE_OK) goto error; // NOLINT(cppcoreguidelines-avoid-goto) update_started = true; // Acknowledge prepare OK - 1 byte - buf[0] = OTA_RESPONSE_UPDATE_PREPARE_OK; + buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK; this->writeall_(buf, 1); // Read binary MD5, 32 bytes if (!this->readall_(buf, 32)) { - ESP_LOGW(TAG, "Reading binary MD5 checksum failed!"); + ESP_LOGW(TAG, "Reading binary MD5 checksum failed"); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } sbuf[32] = '\0'; @@ -286,7 +284,7 @@ void OTAComponent::handle_() { backend->set_update_md5(sbuf); // Acknowledge MD5 OK - 1 byte - buf[0] = OTA_RESPONSE_BIN_MD5_OK; + buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK; this->writeall_(buf, 1); while (total < ota_size) { @@ -299,7 +297,7 @@ void OTAComponent::handle_() { delay(1); continue; } - ESP_LOGW(TAG, "Error receiving data for update, errno: %d", errno); + ESP_LOGW(TAG, "Error receiving data for update, errno %d", errno); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } else if (read == 0) { // $ man recv @@ -310,14 +308,14 @@ void OTAComponent::handle_() { } error_code = backend->write(buf, read); - if (error_code != OTA_RESPONSE_OK) { + if (error_code != ota::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Error writing binary data to flash!, error_code: %d", error_code); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } total += read; #if USE_OTA_VERSION == 2 while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) { - buf[0] = OTA_RESPONSE_CHUNK_OK; + buf[0] = ota::OTA_RESPONSE_CHUNK_OK; this->writeall_(buf, 1); size_acknowledged += OTA_BLOCK_SIZE; } @@ -338,29 +336,29 @@ void OTAComponent::handle_() { } // Acknowledge receive OK - 1 byte - buf[0] = OTA_RESPONSE_RECEIVE_OK; + buf[0] = ota::OTA_RESPONSE_RECEIVE_OK; this->writeall_(buf, 1); error_code = backend->end(); - if (error_code != OTA_RESPONSE_OK) { + if (error_code != ota::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Error ending OTA!, error_code: %d", error_code); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } // Acknowledge Update end OK - 1 byte - buf[0] = OTA_RESPONSE_UPDATE_END_OK; + buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK; this->writeall_(buf, 1); // Read ACK - if (!this->readall_(buf, 1) || buf[0] != OTA_RESPONSE_OK) { - ESP_LOGW(TAG, "Reading back acknowledgement failed!"); + if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) { + ESP_LOGW(TAG, "Reading back acknowledgement failed"); // do not go to error, this is not fatal } this->client_->close(); this->client_ = nullptr; delay(10); - ESP_LOGI(TAG, "OTA update finished!"); + ESP_LOGI(TAG, "OTA update finished"); this->status_clear_warning(); #ifdef USE_OTA_STATE_CALLBACK this->state_callback_.call(OTA_COMPLETED, 100.0f, 0); @@ -384,7 +382,7 @@ error: #endif } -bool OTAComponent::readall_(uint8_t *buf, size_t len) { +bool OTAESPHomeComponent::readall_(uint8_t *buf, size_t len) { uint32_t start = millis(); uint32_t at = 0; while (len - at > 0) { @@ -401,7 +399,7 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) { delay(1); continue; } - ESP_LOGW(TAG, "Failed to read %d bytes of data, errno: %d", len, errno); + ESP_LOGW(TAG, "Failed to read %d bytes of data, errno %d", len, errno); return false; } else if (read == 0) { ESP_LOGW(TAG, "Remote closed connection"); @@ -415,7 +413,7 @@ bool OTAComponent::readall_(uint8_t *buf, size_t len) { return true; } -bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { +bool OTAESPHomeComponent::writeall_(const uint8_t *buf, size_t len) { uint32_t start = millis(); uint32_t at = 0; while (len - at > 0) { @@ -432,7 +430,7 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { delay(1); continue; } - ESP_LOGW(TAG, "Failed to write %d bytes of data, errno: %d", len, errno); + ESP_LOGW(TAG, "Failed to write %d bytes of data, errno %d", len, errno); return false; } else { at += written; @@ -443,31 +441,31 @@ bool OTAComponent::writeall_(const uint8_t *buf, size_t len) { return true; } -float OTAComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } -uint16_t OTAComponent::get_port() const { return this->port_; } -void OTAComponent::set_port(uint16_t port) { this->port_ = port; } +float OTAESPHomeComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } +uint16_t OTAESPHomeComponent::get_port() const { return this->port_; } +void OTAESPHomeComponent::set_port(uint16_t port) { this->port_ = port; } -void OTAComponent::set_safe_mode_pending(const bool &pending) { +void OTAESPHomeComponent::set_safe_mode_pending(const bool &pending) { if (!this->has_safe_mode_) return; uint32_t current_rtc = this->read_rtc_(); - if (pending && current_rtc != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { - ESP_LOGI(TAG, "Device will enter safe mode on next boot."); - this->write_rtc_(esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC); + if (pending && current_rtc != OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC) { + ESP_LOGI(TAG, "Device will enter safe mode on next boot"); + this->write_rtc_(OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC); } - if (!pending && current_rtc == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) { + if (!pending && current_rtc == OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC) { ESP_LOGI(TAG, "Safe mode pending has been cleared"); this->clean_rtc(); } } -bool OTAComponent::get_safe_mode_pending() { - return this->has_safe_mode_ && this->read_rtc_() == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC; +bool OTAESPHomeComponent::get_safe_mode_pending() { + return this->has_safe_mode_ && this->read_rtc_() == OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC; } -bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) { +bool OTAESPHomeComponent::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; @@ -475,24 +473,24 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false); this->safe_mode_rtc_value_ = this->read_rtc_(); - bool is_manual_safe_mode = this->safe_mode_rtc_value_ == esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC; + bool is_manual_safe_mode = this->safe_mode_rtc_value_ == OTAESPHomeComponent::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_); + 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."); + 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."); + ESP_LOGE(TAG, "No OTA attempt made, restarting"); App.reboot(); }); @@ -500,7 +498,7 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ delay(300); // NOLINT App.setup(); - ESP_LOGI(TAG, "Waiting for OTA attempt."); + ESP_LOGI(TAG, "Waiting for OTA attempt"); return true; } else { @@ -509,27 +507,27 @@ bool OTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_ return false; } } -void OTAComponent::write_rtc_(uint32_t val) { +void OTAESPHomeComponent::write_rtc_(uint32_t val) { this->rtc_.save(&val); global_preferences->sync(); } -uint32_t OTAComponent::read_rtc_() { +uint32_t OTAESPHomeComponent::read_rtc_() { uint32_t val; if (!this->rtc_.load(&val)) return 0; return val; } -void OTAComponent::clean_rtc() { this->write_rtc_(0); } -void OTAComponent::on_safe_shutdown() { - if (this->has_safe_mode_ && this->read_rtc_() != esphome::ota::OTAComponent::ENTER_SAFE_MODE_MAGIC) +void OTAESPHomeComponent::clean_rtc() { this->write_rtc_(0); } +void OTAESPHomeComponent::on_safe_shutdown() { + if (this->has_safe_mode_ && this->read_rtc_() != OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC) this->clean_rtc(); } #ifdef USE_OTA_STATE_CALLBACK -void OTAComponent::add_on_state_callback(std::function<void(OTAState, float, uint8_t)> &&callback) { +void OTAESPHomeComponent::add_on_state_callback(std::function<void(OTAESPHomeState, float, uint8_t)> &&callback) { this->state_callback_.add(std::move(callback)); } #endif -} // namespace ota +} // namespace ota_esphome } // namespace esphome diff --git a/esphome/components/ota/ota_component.h b/esphome/components/esphome/ota/ota_esphome.h similarity index 53% rename from esphome/components/ota/ota_component.h rename to esphome/components/esphome/ota/ota_esphome.h index c20f4f0709..19008d458e 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -2,48 +2,19 @@ #include "esphome/components/socket/socket.h" #include "esphome/core/component.h" -#include "esphome/core/preferences.h" -#include "esphome/core/helpers.h" #include "esphome/core/defines.h" +#include "esphome/core/helpers.h" +#include "esphome/core/preferences.h" namespace esphome { -namespace ota { +namespace ota_esphome { -enum OTAResponseTypes { - OTA_RESPONSE_OK = 0x00, - OTA_RESPONSE_REQUEST_AUTH = 0x01, +enum OTAESPHomeState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; - OTA_RESPONSE_HEADER_OK = 0x40, - OTA_RESPONSE_AUTH_OK = 0x41, - OTA_RESPONSE_UPDATE_PREPARE_OK = 0x42, - OTA_RESPONSE_BIN_MD5_OK = 0x43, - OTA_RESPONSE_RECEIVE_OK = 0x44, - OTA_RESPONSE_UPDATE_END_OK = 0x45, - OTA_RESPONSE_SUPPORTS_COMPRESSION = 0x46, - OTA_RESPONSE_CHUNK_OK = 0x47, - - OTA_RESPONSE_ERROR_MAGIC = 0x80, - OTA_RESPONSE_ERROR_UPDATE_PREPARE = 0x81, - OTA_RESPONSE_ERROR_AUTH_INVALID = 0x82, - OTA_RESPONSE_ERROR_WRITING_FLASH = 0x83, - OTA_RESPONSE_ERROR_UPDATE_END = 0x84, - OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 0x85, - OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 0x86, - OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 0x87, - OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 0x88, - OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 0x89, - OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A, - OTA_RESPONSE_ERROR_MD5_MISMATCH = 0x8B, - OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C, - OTA_RESPONSE_ERROR_UNKNOWN = 0xFF, -}; - -enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; - -/// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. -class OTAComponent : public Component { +/// OTAESPHomeComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. +class OTAESPHomeComponent : public Component { public: - OTAComponent(); + OTAESPHomeComponent(); #ifdef USE_OTA_PASSWORD void set_auth_password(const std::string &password) { password_ = password; } #endif // USE_OTA_PASSWORD @@ -58,7 +29,7 @@ class OTAComponent : public Component { bool get_safe_mode_pending(); #ifdef USE_OTA_STATE_CALLBACK - void add_on_state_callback(std::function<void(OTAState, float, uint8_t)> &&callback); + void add_on_state_callback(std::function<void(OTAESPHomeState, float, uint8_t)> &&callback); #endif // ========== INTERNAL METHODS ========== @@ -91,9 +62,9 @@ class OTAComponent : public Component { std::unique_ptr<socket::Socket> server_; std::unique_ptr<socket::Socket> 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. + 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_; @@ -102,11 +73,11 @@ class OTAComponent : public Component { 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot #ifdef USE_OTA_STATE_CALLBACK - CallbackManager<void(OTAState, float, uint8_t)> state_callback_{}; + CallbackManager<void(OTAESPHomeState, float, uint8_t)> state_callback_{}; #endif }; -extern OTAComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +extern OTAESPHomeComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -} // namespace ota +} // namespace ota_esphome } // namespace esphome diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 3c845490dc..aed0b87f85 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,137 +1,19 @@ -from esphome.cpp_generator import RawExpression -import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation -from esphome.const import ( - CONF_ID, - CONF_NUM_ATTEMPTS, - CONF_PASSWORD, - CONF_PORT, - CONF_REBOOT_TIMEOUT, - CONF_SAFE_MODE, - CONF_TRIGGER_ID, - CONF_OTA, - KEY_PAST_SAFE_MODE, - CONF_VERSION, -) -from esphome.core import CORE, coroutine_with_priority + +from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM CODEOWNERS = ["@esphome/core"] +AUTO_LOAD = ["md5"] DEPENDENCIES = ["network"] -AUTO_LOAD = ["socket", "md5"] -CONF_ON_STATE_CHANGE = "on_state_change" -CONF_ON_BEGIN = "on_begin" -CONF_ON_PROGRESS = "on_progress" -CONF_ON_END = "on_end" -CONF_ON_ERROR = "on_error" - -ota_ns = cg.esphome_ns.namespace("ota") -OTAState = ota_ns.enum("OTAState") -OTAComponent = ota_ns.class_("OTAComponent", cg.Component) -OTAStateChangeTrigger = ota_ns.class_( - "OTAStateChangeTrigger", automation.Trigger.template() -) -OTAStartTrigger = ota_ns.class_("OTAStartTrigger", automation.Trigger.template()) -OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.template()) -OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) -OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) +IS_PLATFORM_COMPONENT = True -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(OTAComponent), - 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, - esp8266=8266, - esp32=3232, - rp2040=2040, - bk72xx=8892, - 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_ON_STATE_CHANGE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStateChangeTrigger), - } - ), - cv.Optional(CONF_ON_BEGIN): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStartTrigger), - } - ), - cv.Optional(CONF_ON_ERROR): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAErrorTrigger), - } - ), - cv.Optional(CONF_ON_PROGRESS): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAProgressTrigger), - } - ), - cv.Optional(CONF_ON_END): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAEndTrigger), - } - ), - } -).extend(cv.COMPONENT_SCHEMA) - - -@coroutine_with_priority(50.0) -async def to_code(config): - CORE.data[CONF_OTA] = {} - - var = cg.new_Pvariable(config[CONF_ID]) - cg.add(var.set_port(config[CONF_PORT])) - cg.add_define("USE_OTA") - if CONF_PASSWORD in config: - cg.add(var.set_auth_password(config[CONF_PASSWORD])) - cg.add_define("USE_OTA_PASSWORD") - 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] +def _ota_final_validate(config): + if len(config) < 1: + raise cv.Invalid( + f"At least one platform must be specified for '{CONF_OTA}'; add '{CONF_PLATFORM}: {CONF_ESPHOME}' for original OTA functionality" ) - cg.add(RawExpression(f"if ({condition}) return")) - CORE.data[CONF_OTA][KEY_PAST_SAFE_MODE] = True - if CORE.is_esp32 and CORE.using_arduino: - cg.add_library("Update", None) - if CORE.is_rp2040 and CORE.using_arduino: - cg.add_library("Updater", None) - - use_state_callback = False - for conf in config.get(CONF_ON_STATE_CHANGE, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(OTAState, "state")], conf) - use_state_callback = True - for conf in config.get(CONF_ON_BEGIN, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) - use_state_callback = True - for conf in config.get(CONF_ON_PROGRESS, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(float, "x")], conf) - use_state_callback = True - for conf in config.get(CONF_ON_END, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) - use_state_callback = True - for conf in config.get(CONF_ON_ERROR, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(cg.uint8, "x")], conf) - use_state_callback = True - if use_state_callback: - cg.add_define("USE_OTA_STATE_CALLBACK") +FINAL_VALIDATE_SCHEMA = _ota_final_validate diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h deleted file mode 100644 index 0c77a18ce1..0000000000 --- a/esphome/components/ota/automation.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -#include "esphome/core/defines.h" -#ifdef USE_OTA_STATE_CALLBACK - -#include "esphome/core/component.h" -#include "esphome/core/automation.h" -#include "esphome/components/ota/ota_component.h" - -namespace esphome { -namespace ota { - -class OTAStateChangeTrigger : public Trigger<OTAState> { - public: - explicit OTAStateChangeTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (!parent->is_failed()) { - return trigger(state); - } - }); - } -}; - -class OTAStartTrigger : public Trigger<> { - public: - explicit OTAStartTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_STARTED && !parent->is_failed()) { - trigger(); - } - }); - } -}; - -class OTAProgressTrigger : public Trigger<float> { - public: - explicit OTAProgressTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_IN_PROGRESS && !parent->is_failed()) { - trigger(progress); - } - }); - } -}; - -class OTAEndTrigger : public Trigger<> { - public: - explicit OTAEndTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_COMPLETED && !parent->is_failed()) { - trigger(); - } - }); - } -}; - -class OTAErrorTrigger : public Trigger<uint8_t> { - public: - explicit OTAErrorTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_ERROR && !parent->is_failed()) { - trigger(error); - } - }); - } -}; - -} // namespace ota -} // namespace esphome - -#endif // USE_OTA_STATE_CALLBACK diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index 5c5b61a278..f7646dbdea 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -1,9 +1,38 @@ #pragma once -#include "ota_component.h" +#include "esphome/core/helpers.h" namespace esphome { namespace ota { +enum OTAResponseTypes { + OTA_RESPONSE_OK = 0x00, + OTA_RESPONSE_REQUEST_AUTH = 0x01, + + OTA_RESPONSE_HEADER_OK = 0x40, + OTA_RESPONSE_AUTH_OK = 0x41, + OTA_RESPONSE_UPDATE_PREPARE_OK = 0x42, + OTA_RESPONSE_BIN_MD5_OK = 0x43, + OTA_RESPONSE_RECEIVE_OK = 0x44, + OTA_RESPONSE_UPDATE_END_OK = 0x45, + OTA_RESPONSE_SUPPORTS_COMPRESSION = 0x46, + OTA_RESPONSE_CHUNK_OK = 0x47, + + OTA_RESPONSE_ERROR_MAGIC = 0x80, + OTA_RESPONSE_ERROR_UPDATE_PREPARE = 0x81, + OTA_RESPONSE_ERROR_AUTH_INVALID = 0x82, + OTA_RESPONSE_ERROR_WRITING_FLASH = 0x83, + OTA_RESPONSE_ERROR_UPDATE_END = 0x84, + OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 0x85, + OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 0x86, + OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 0x87, + OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 0x88, + OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 0x89, + OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A, + OTA_RESPONSE_ERROR_MD5_MISMATCH = 0x8B, + OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C, + OTA_RESPONSE_ERROR_UNKNOWN = 0xFF, +}; + class OTABackend { public: virtual ~OTABackend() = default; diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota/ota_backend_arduino_esp32.cpp index 4759737dbd..35d8ac9665 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp32.cpp @@ -1,8 +1,7 @@ -#include "esphome/core/defines.h" #ifdef USE_ESP32_FRAMEWORK_ARDUINO +#include "esphome/core/defines.h" #include "ota_backend_arduino_esp32.h" -#include "ota_component.h" #include "ota_backend.h" #include <Update.h> diff --git a/esphome/components/ota/ota_backend_arduino_esp32.h b/esphome/components/ota/ota_backend_arduino_esp32.h index f86a70d678..ac7fe9f14f 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.h +++ b/esphome/components/ota/ota_backend_arduino_esp32.h @@ -1,10 +1,10 @@ #pragma once -#include "esphome/core/defines.h" #ifdef USE_ESP32_FRAMEWORK_ARDUINO - -#include "ota_component.h" #include "ota_backend.h" +#include "esphome/core/defines.h" +#include "esphome/core/helpers.h" + namespace esphome { namespace ota { diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota/ota_backend_arduino_esp8266.cpp index 23dc0d4e21..068c0ffeaa 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp8266.cpp @@ -1,10 +1,9 @@ -#include "esphome/core/defines.h" #ifdef USE_ARDUINO #ifdef USE_ESP8266 - -#include "ota_backend_arduino_esp8266.h" -#include "ota_component.h" #include "ota_backend.h" +#include "ota_backend_arduino_esp8266.h" + +#include "esphome/core/defines.h" #include "esphome/components/esp8266/preferences.h" #include <Updater.h> diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota/ota_backend_arduino_esp8266.h index 7937c665b0..7f44d7c965 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota/ota_backend_arduino_esp8266.h @@ -1,10 +1,9 @@ #pragma once -#include "esphome/core/defines.h" #ifdef USE_ARDUINO #ifdef USE_ESP8266 - -#include "ota_component.h" #include "ota_backend.h" + +#include "esphome/core/defines.h" #include "esphome/core/macros.h" namespace esphome { diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.cpp b/esphome/components/ota/ota_backend_arduino_libretiny.cpp index dbf6c97988..f845ec7466 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota/ota_backend_arduino_libretiny.cpp @@ -1,9 +1,8 @@ -#include "esphome/core/defines.h" #ifdef USE_LIBRETINY - -#include "ota_backend_arduino_libretiny.h" -#include "ota_component.h" #include "ota_backend.h" +#include "ota_backend_arduino_libretiny.h" + +#include "esphome/core/defines.h" #include <Update.h> diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.h b/esphome/components/ota/ota_backend_arduino_libretiny.h index 79656bb353..11deb6e2f2 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.h +++ b/esphome/components/ota/ota_backend_arduino_libretiny.h @@ -1,10 +1,9 @@ #pragma once -#include "esphome/core/defines.h" #ifdef USE_LIBRETINY - -#include "ota_component.h" #include "ota_backend.h" +#include "esphome/core/defines.h" + namespace esphome { namespace ota { diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.cpp b/esphome/components/ota/ota_backend_arduino_rp2040.cpp index 260387cec1..78935c5bfd 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota/ota_backend_arduino_rp2040.cpp @@ -1,11 +1,10 @@ -#include "esphome/core/defines.h" #ifdef USE_ARDUINO #ifdef USE_RP2040 - -#include "esphome/components/rp2040/preferences.h" #include "ota_backend.h" #include "ota_backend_arduino_rp2040.h" -#include "ota_component.h" + +#include "esphome/components/rp2040/preferences.h" +#include "esphome/core/defines.h" #include <Updater.h> diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.h b/esphome/components/ota/ota_backend_arduino_rp2040.h index 5aa2ec9435..b189964ab3 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.h +++ b/esphome/components/ota/ota_backend_arduino_rp2040.h @@ -1,11 +1,10 @@ #pragma once -#include "esphome/core/defines.h" #ifdef USE_ARDUINO #ifdef USE_RP2040 - -#include "esphome/core/macros.h" #include "ota_backend.h" -#include "ota_component.h" + +#include "esphome/core/defines.h" +#include "esphome/core/macros.h" namespace esphome { namespace ota { diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index 319a1482f1..a551bb6b32 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -1,12 +1,11 @@ -#include "esphome/core/defines.h" #ifdef USE_ESP_IDF - -#include <esp_task_wdt.h> - #include "ota_backend_esp_idf.h" -#include "ota_component.h" -#include <esp_ota_ops.h> + #include "esphome/components/md5/md5.h" +#include "esphome/core/defines.h" + +#include <esp_ota_ops.h> +#include <esp_task_wdt.h> #if ESP_IDF_VERSION_MAJOR >= 5 #include <spi_flash_mmap.h> diff --git a/esphome/components/ota/ota_backend_esp_idf.h b/esphome/components/ota/ota_backend_esp_idf.h index af09d0d693..ed66d9b970 100644 --- a/esphome/components/ota/ota_backend_esp_idf.h +++ b/esphome/components/ota/ota_backend_esp_idf.h @@ -1,11 +1,11 @@ #pragma once -#include "esphome/core/defines.h" #ifdef USE_ESP_IDF - -#include "ota_component.h" #include "ota_backend.h" -#include <esp_ota_ops.h> + #include "esphome/components/md5/md5.h" +#include "esphome/core/defines.h" + +#include <esp_ota_ops.h> namespace esphome { namespace ota { diff --git a/esphome/components/safe_mode/button/__init__.py b/esphome/components/safe_mode/button/__init__.py index 307e4e372e..ffd2b4e6ee 100644 --- a/esphome/components/safe_mode/button/__init__.py +++ b/esphome/components/safe_mode/button/__init__.py @@ -1,18 +1,17 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import button -from esphome.components.ota import OTAComponent +from esphome.components.esphome.ota import OTAESPHomeComponent from esphome.const import ( - CONF_ID, - CONF_OTA, + CONF_ESPHOME, DEVICE_CLASS_RESTART, ENTITY_CATEGORY_CONFIG, ICON_RESTART_ALERT, ) +from .. import safe_mode_ns DEPENDENCIES = ["ota"] -safe_mode_ns = cg.esphome_ns.namespace("safe_mode") SafeModeButton = safe_mode_ns.class_("SafeModeButton", button.Button, cg.Component) CONFIG_SCHEMA = ( @@ -22,15 +21,14 @@ CONFIG_SCHEMA = ( entity_category=ENTITY_CATEGORY_CONFIG, icon=ICON_RESTART_ALERT, ) - .extend({cv.GenerateID(CONF_OTA): cv.use_id(OTAComponent)}) + .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(OTAESPHomeComponent)}) .extend(cv.COMPONENT_SCHEMA) ) async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) + var = await button.new_button(config) await cg.register_component(var, config) - await button.register_button(var, config) - ota = await cg.get_variable(config[CONF_OTA]) + ota = await cg.get_variable(config[CONF_ESPHOME]) cg.add(var.set_ota(ota)) diff --git a/esphome/components/safe_mode/button/safe_mode_button.cpp b/esphome/components/safe_mode/button/safe_mode_button.cpp index 2b8654de46..673af02c20 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.cpp +++ b/esphome/components/safe_mode/button/safe_mode_button.cpp @@ -8,7 +8,7 @@ namespace safe_mode { static const char *const TAG = "safe_mode.button"; -void SafeModeButton::set_ota(ota::OTAComponent *ota) { this->ota_ = ota; } +void SafeModeButton::set_ota(ota_esphome::OTAESPHomeComponent *ota) { this->ota_ = ota; } void SafeModeButton::press_action() { ESP_LOGI(TAG, "Restarting device in safe mode..."); diff --git a/esphome/components/safe_mode/button/safe_mode_button.h b/esphome/components/safe_mode/button/safe_mode_button.h index 63e0d1755e..4ac0d2d177 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.h +++ b/esphome/components/safe_mode/button/safe_mode_button.h @@ -1,8 +1,8 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/components/ota/ota_component.h" #include "esphome/components/button/button.h" +#include "esphome/components/esphome/ota/ota_esphome.h" +#include "esphome/core/component.h" namespace esphome { namespace safe_mode { @@ -10,10 +10,10 @@ namespace safe_mode { class SafeModeButton : public button::Button, public Component { public: void dump_config() override; - void set_ota(ota::OTAComponent *ota); + void set_ota(ota_esphome::OTAESPHomeComponent *ota); protected: - ota::OTAComponent *ota_; + ota_esphome::OTAESPHomeComponent *ota_; void press_action() override; }; diff --git a/esphome/components/safe_mode/switch/__init__.py b/esphome/components/safe_mode/switch/__init__.py index a6fcdfbece..c576cc7403 100644 --- a/esphome/components/safe_mode/switch/__init__.py +++ b/esphome/components/safe_mode/switch/__init__.py @@ -1,9 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch -from esphome.components.ota import OTAComponent +from esphome.components.esphome.ota import OTAESPHomeComponent from esphome.const import ( - CONF_OTA, + CONF_ESPHOME, ENTITY_CATEGORY_CONFIG, ICON_RESTART_ALERT, ) @@ -16,11 +16,11 @@ SafeModeSwitch = safe_mode_ns.class_("SafeModeSwitch", switch.Switch, cg.Compone CONFIG_SCHEMA = ( switch.switch_schema( SafeModeSwitch, - icon=ICON_RESTART_ALERT, - entity_category=ENTITY_CATEGORY_CONFIG, block_inverted=True, + entity_category=ENTITY_CATEGORY_CONFIG, + icon=ICON_RESTART_ALERT, ) - .extend({cv.GenerateID(CONF_OTA): cv.use_id(OTAComponent)}) + .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(OTAESPHomeComponent)}) .extend(cv.COMPONENT_SCHEMA) ) @@ -29,5 +29,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_OTA]) + ota = await cg.get_variable(config[CONF_ESPHOME]) cg.add(var.set_ota(ota)) diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.cpp b/esphome/components/safe_mode/switch/safe_mode_switch.cpp index a3979eec06..a145a71386 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.cpp +++ b/esphome/components/safe_mode/switch/safe_mode_switch.cpp @@ -1,14 +1,14 @@ #include "safe_mode_switch.h" +#include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" namespace esphome { namespace safe_mode { static const char *const TAG = "safe_mode_switch"; -void SafeModeSwitch::set_ota(ota::OTAComponent *ota) { this->ota_ = ota; } +void SafeModeSwitch::set_ota(ota_esphome::OTAESPHomeComponent *ota) { this->ota_ = ota; } void SafeModeSwitch::write_state(bool state) { // Acknowledge diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.h b/esphome/components/safe_mode/switch/safe_mode_switch.h index 2772db3d84..7fc8927c80 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.h +++ b/esphome/components/safe_mode/switch/safe_mode_switch.h @@ -1,8 +1,8 @@ #pragma once -#include "esphome/core/component.h" -#include "esphome/components/ota/ota_component.h" +#include "esphome/components/esphome/ota/ota_esphome.h" #include "esphome/components/switch/switch.h" +#include "esphome/core/component.h" namespace esphome { namespace safe_mode { @@ -10,10 +10,10 @@ namespace safe_mode { class SafeModeSwitch : public switch_::Switch, public Component { public: void dump_config() override; - void set_ota(ota::OTAComponent *ota); + void set_ota(ota_esphome::OTAESPHomeComponent *ota); protected: - ota::OTAComponent *ota_; + ota_esphome::OTAESPHomeComponent *ota_; void write_state(bool state) override; }; diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 4b3716e223..402f60284e 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -3,14 +3,16 @@ import logging from esphome.const import ( CONF_DISABLED_BY_DEFAULT, CONF_ENTITY_CATEGORY, + CONF_ESPHOME, CONF_ICON, CONF_INTERNAL, CONF_NAME, - CONF_SETUP_PRIORITY, - CONF_UPDATE_INTERVAL, - CONF_TYPE_ID, CONF_OTA, + CONF_PLATFORM, CONF_SAFE_MODE, + CONF_SETUP_PRIORITY, + CONF_TYPE_ID, + CONF_UPDATE_INTERVAL, KEY_PAST_SAFE_MODE, ) @@ -139,9 +141,17 @@ async def build_registry_list(registry, config): async def past_safe_mode(): - safe_mode_enabled = ( - CONF_OTA in CORE.config and CORE.config[CONF_OTA][CONF_SAFE_MODE] - ) + ota_conf = {} + if CONF_OTA in CORE.config: + 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: return diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index da5c6d10d0..42af4eb47f 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -5,7 +5,7 @@ #include <esphome/components/gpio/switch/gpio_switch.h> #include <esphome/components/logger/logger.h> -#include <esphome/components/ota/ota_component.h> +#include <esphome/components/esphome/ota/ota_esphome.h> #include <esphome/components/wifi/wifi_component.h> #include <esphome/core/application.h> @@ -25,7 +25,7 @@ void setup() { ap.set_password("password1"); wifi->add_sta(ap); - auto *ota = new ota::OTAComponent(); // NOLINT + auto *ota = new ota_esphome::OTAESPHomeComponent(); // NOLINT ota->set_port(8266); App.setup(); diff --git a/tests/test1.yaml b/tests/test1.yaml index c8ae9691c2..6d93900b47 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -265,29 +265,30 @@ uart: baud_rate: 9600 ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); - on_begin: - then: - logger.log: OTA begin - on_progress: - then: - lambda: >- - ESP_LOGD("ota", "Got progress %f", x); - on_end: - then: - logger.log: OTA end - on_error: - then: - lambda: >- - ESP_LOGD("ota", "Got error code %d", x); + - platform: esphome + safe_mode: true + password: "superlongpasswordthatnoonewillknow" + port: 3286 + reboot_timeout: 2min + num_attempts: 5 + on_state_change: + then: + lambda: >- + ESP_LOGD("ota", "State %d", state); + on_begin: + then: + logger.log: OTA begin + on_progress: + then: + lambda: >- + ESP_LOGD("ota", "Got progress %f", x); + on_end: + then: + logger.log: OTA end + on_error: + then: + lambda: >- + ESP_LOGD("ota", "Got error code %d", x); logger: baud_rate: 0 diff --git a/tests/test11.5.yaml b/tests/test11.5.yaml index ef260d79c0..e7eadbae8d 100644 --- a/tests/test11.5.yaml +++ b/tests/test11.5.yaml @@ -31,6 +31,7 @@ network: api: ota: + - platform: esphome logger: diff --git a/tests/test2.yaml b/tests/test2.yaml index 2fdef72c08..1cad026ed2 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -78,9 +78,10 @@ uart: - lambda: UARTDebug::log_hex(direction, bytes, ':'); ota: - safe_mode: true - port: 3286 - num_attempts: 15 + - platform: esphome + safe_mode: true + port: 3286 + num_attempts: 15 logger: level: DEBUG diff --git a/tests/test3.1.yaml b/tests/test3.1.yaml index 2bddd6f4d7..18d92289cd 100644 --- a/tests/test3.1.yaml +++ b/tests/test3.1.yaml @@ -49,7 +49,8 @@ spi: number: GPIO14 ota: - version: 2 + - platform: esphome + version: 2 logger: diff --git a/tests/test3.yaml b/tests/test3.yaml index 61d814385b..7554d4bcb2 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -328,9 +328,10 @@ vbus: uart_id: uart_4 ota: - safe_mode: true - port: 3286 - reboot_timeout: 15min + - 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 993ce126a8..86beee81c6 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -103,8 +103,9 @@ uart: parity: EVEN ota: - safe_mode: true - port: 3286 + - platform: esphome + safe_mode: true + port: 3286 logger: level: DEBUG diff --git a/tests/test5.yaml b/tests/test5.yaml index 81615b24b0..914fa65c20 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -28,6 +28,7 @@ network: api: ota: + - platform: esphome logger: diff --git a/tests/test6.yaml b/tests/test6.yaml index 2c5aa30aad..b1103eb126 100644 --- a/tests/test6.yaml +++ b/tests/test6.yaml @@ -22,6 +22,7 @@ network: api: ota: + - platform: esphome logger: diff --git a/tests/test9.1.yaml b/tests/test9.1.yaml index f7455b7668..2d205ef4e6 100644 --- a/tests/test9.1.yaml +++ b/tests/test9.1.yaml @@ -12,6 +12,7 @@ esphome: logger: ota: + - platform: esphome captive_portal: diff --git a/tests/test9.yaml b/tests/test9.yaml index d660b4f24a..5017ccc5ed 100644 --- a/tests/test9.yaml +++ b/tests/test9.yaml @@ -12,6 +12,7 @@ esphome: logger: ota: + - platform: esphome captive_portal: From 91198556f639bfba3d6a1fd5648218604f334829 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Thu, 11 Apr 2024 00:18:16 -0500 Subject: [PATCH 02/26] Move make_ota_backend into the OTA backend --- .../components/esphome/ota/ota_esphome.cpp | 22 +------------------ esphome/components/ota/ota_backend.h | 2 ++ .../ota/ota_backend_arduino_esp32.cpp | 2 ++ .../ota/ota_backend_arduino_esp8266.cpp | 2 ++ .../ota/ota_backend_arduino_libretiny.cpp | 2 ++ .../ota/ota_backend_arduino_rp2040.cpp | 2 ++ .../components/ota/ota_backend_esp_idf.cpp | 2 ++ 7 files changed, 13 insertions(+), 21 deletions(-) diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 447f0587f8..5be1a5944c 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -24,26 +24,6 @@ static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; OTAESPHomeComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -std::unique_ptr<ota::OTABackend> make_ota_backend() { -#ifdef USE_ARDUINO -#ifdef USE_ESP8266 - return make_unique<ota::ArduinoESP8266OTABackend>(); -#endif // USE_ESP8266 -#ifdef USE_ESP32 - return make_unique<ota::ArduinoESP32OTABackend>(); -#endif // USE_ESP32 -#endif // USE_ARDUINO -#ifdef USE_ESP_IDF - return make_unique<ota::IDFOTABackend>(); -#endif // USE_ESP_IDF -#ifdef USE_RP2040 - return make_unique<ota::ArduinoRP2040OTABackend>(); -#endif // USE_RP2040 -#ifdef USE_LIBRETINY - return make_unique<ota::ArduinoLibreTinyOTABackend>(); -#endif -} - OTAESPHomeComponent::OTAESPHomeComponent() { global_ota_component = this; } void OTAESPHomeComponent::setup() { @@ -172,7 +152,7 @@ void OTAESPHomeComponent::handle_() { buf[1] = USE_OTA_VERSION; this->writeall_(buf, 2); - backend = make_ota_backend(); + backend = ota::make_ota_backend(); // Read features - 1 byte if (!this->readall_(buf, 1)) { diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index f7646dbdea..471eebb390 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -44,5 +44,7 @@ class OTABackend { virtual bool supports_compression() = 0; }; +std::unique_ptr<ota::OTABackend> make_ota_backend(); + } // namespace ota } // namespace esphome diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota/ota_backend_arduino_esp32.cpp index 35d8ac9665..62c6a72388 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp32.cpp @@ -9,6 +9,8 @@ namespace esphome { namespace ota { +std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP32OTABackend>(); } + OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); if (ret) { diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota/ota_backend_arduino_esp8266.cpp index 068c0ffeaa..b317075bd0 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota/ota_backend_arduino_esp8266.cpp @@ -11,6 +11,8 @@ namespace esphome { namespace ota { +std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP8266OTABackend>(); } + OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); if (ret) { diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.cpp b/esphome/components/ota/ota_backend_arduino_libretiny.cpp index f845ec7466..df4e774ebc 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota/ota_backend_arduino_libretiny.cpp @@ -9,6 +9,8 @@ namespace esphome { namespace ota { +std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoLibreTinyOTABackend>(); } + OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); if (ret) { diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.cpp b/esphome/components/ota/ota_backend_arduino_rp2040.cpp index 78935c5bfd..4448b0c95e 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota/ota_backend_arduino_rp2040.cpp @@ -11,6 +11,8 @@ namespace esphome { namespace ota { +std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoRP2040OTABackend>(); } + OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); if (ret) { diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index a551bb6b32..6f45fb75e4 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -14,6 +14,8 @@ namespace esphome { namespace ota { +std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::IDFOTABackend>(); } + OTAResponseTypes IDFOTABackend::begin(size_t image_size) { this->partition_ = esp_ota_get_next_update_partition(nullptr); if (this->partition_ == nullptr) { From a067693774ebd1c4db2c1cddee6d65f2a7117a05 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Mon, 15 Apr 2024 21:09:57 -0500 Subject: [PATCH 03/26] Update wizard.py --- esphome/wizard.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/wizard.py b/esphome/wizard.py index 4ec366bbb9..9680ade044 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -153,10 +153,11 @@ def wizard_file(**kwargs): # Configure OTA config += "\nota:\n" + config += " - platform: esphome\n" if "ota_password" in kwargs: - config += f" password: \"{kwargs['ota_password']}\"" + config += f" password: \"{kwargs['ota_password']}\"" elif "password" in kwargs: - config += f" password: \"{kwargs['password']}\"" + config += f" password: \"{kwargs['password']}\"" # Configuring wifi config += "\n\nwifi:\n" From 05a940572cc3cd49456be0dcb3b2cd4b0c766425 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Wed, 17 Apr 2024 20:55:51 -0500 Subject: [PATCH 04/26] Remove `ota` from esphome.ota dependencies Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esphome/ota/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 3143d5408d..496261c5fc 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -19,7 +19,7 @@ from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5", "socket"] -DEPENDENCIES = ["network", "ota"] +DEPENDENCIES = ["network"] CONF_ON_BEGIN = "on_begin" CONF_ON_END = "on_end" From cc8dd928548bd981385bcee6e6bd6ebf2f193d0a Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Wed, 17 Apr 2024 20:58:13 -0500 Subject: [PATCH 05/26] Rename class to `ESPHomeOTAComponent` Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/esphome/ota/ota_esphome.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index 19008d458e..593a5e9a6d 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -12,7 +12,7 @@ namespace ota_esphome { enum OTAESPHomeState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; /// OTAESPHomeComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. -class OTAESPHomeComponent : public Component { +class ESPHomeOTAComponent : public Component { public: OTAESPHomeComponent(); #ifdef USE_OTA_PASSWORD From af6dffd150212e0865ba90fbc8d0e294efe6802b Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Wed, 17 Apr 2024 23:12:30 -0500 Subject: [PATCH 06/26] Rename class, adjust namespace --- esphome/components/esphome/ota/__init__.py | 40 +++++++------- esphome/components/esphome/ota/automation.h | 2 - .../components/esphome/ota/ota_esphome.cpp | 54 +++++++++---------- esphome/components/esphome/ota/ota_esphome.h | 8 ++- .../components/safe_mode/button/__init__.py | 4 +- .../safe_mode/button/safe_mode_button.cpp | 2 +- .../safe_mode/button/safe_mode_button.h | 4 +- .../components/safe_mode/switch/__init__.py | 4 +- .../safe_mode/switch/safe_mode_switch.cpp | 2 +- .../safe_mode/switch/safe_mode_switch.h | 4 +- 10 files changed, 59 insertions(+), 65 deletions(-) diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 496261c5fc..d94e2ab921 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -27,31 +27,31 @@ CONF_ON_ERROR = "on_error" CONF_ON_PROGRESS = "on_progress" CONF_ON_STATE_CHANGE = "on_state_change" -ota_esphome = cg.esphome_ns.namespace("ota_esphome") +esphome = cg.esphome_ns.namespace("esphome") -OTAESPHomeComponent = ota_esphome.class_("OTAESPHomeComponent", cg.Component) -OTAESPHomeEndTrigger = ota_esphome.class_( - "OTAESPHomeEndTrigger", automation.Trigger.template() +ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", cg.Component) +ESPHomeOTAEndTrigger = esphome.class_( + "ESPHomeOTAEndTrigger", automation.Trigger.template() ) -OTAESPHomeErrorTrigger = ota_esphome.class_( - "OTAESPHomeErrorTrigger", automation.Trigger.template() +ESPHomeOTAErrorTrigger = esphome.class_( + "ESPHomeOTAErrorTrigger", automation.Trigger.template() ) -OTAESPHomeProgressTrigger = ota_esphome.class_( - "OTAESPHomeProgressTrigger", automation.Trigger.template() +ESPHomeOTAProgressTrigger = esphome.class_( + "ESPHomeOTAProgressTrigger", automation.Trigger.template() ) -OTAESPHomeStartTrigger = ota_esphome.class_( - "OTAESPHomeStartTrigger", automation.Trigger.template() +ESPHomeOTAStartTrigger = esphome.class_( + "ESPHomeOTAStartTrigger", automation.Trigger.template() ) -OTAESPHomeStateChangeTrigger = ota_esphome.class_( - "OTAESPHomeStateChangeTrigger", automation.Trigger.template() +ESPHomeOTAStateChangeTrigger = esphome.class_( + "ESPHomeOTAStateChangeTrigger", automation.Trigger.template() ) -OTAESPHomeState = ota_esphome.enum("OTAESPHomeState") +ESPHomeOTAState = esphome.enum("ESPHomeOTAState") CONFIG_SCHEMA = cv.Schema( { - cv.GenerateID(): cv.declare_id(OTAESPHomeComponent), + 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( @@ -70,29 +70,29 @@ CONFIG_SCHEMA = cv.Schema( cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - OTAESPHomeStateChangeTrigger + ESPHomeOTAStateChangeTrigger ), } ), cv.Optional(CONF_ON_BEGIN): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAESPHomeStartTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPHomeOTAStartTrigger), } ), cv.Optional(CONF_ON_END): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAESPHomeEndTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPHomeOTAEndTrigger), } ), cv.Optional(CONF_ON_ERROR): automation.validate_automation( { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAESPHomeErrorTrigger), + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPHomeOTAErrorTrigger), } ), cv.Optional(CONF_ON_PROGRESS): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - OTAESPHomeProgressTrigger + ESPHomeOTAProgressTrigger ), } ), @@ -130,7 +130,7 @@ async def to_code(config): use_state_callback = False for conf in config.get(CONF_ON_STATE_CHANGE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(OTAESPHomeState, "state")], conf) + await automation.build_automation(trigger, [(ESPHomeOTAState, "state")], conf) use_state_callback = True for conf in config.get(CONF_ON_BEGIN, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) diff --git a/esphome/components/esphome/ota/automation.h b/esphome/components/esphome/ota/automation.h index a23b1c5590..b922b6d77b 100644 --- a/esphome/components/esphome/ota/automation.h +++ b/esphome/components/esphome/ota/automation.h @@ -8,7 +8,6 @@ #include "esphome/core/automation.h" namespace esphome { -namespace ota_esphome { class OTAESPHomeStateChangeTrigger : public Trigger<OTAESPHomeState> { public: @@ -65,7 +64,6 @@ class OTAESPHomeErrorTrigger : public Trigger<uint8_t> { } }; -} // namespace ota_esphome } // namespace esphome #endif // USE_OTA_STATE_CALLBACK diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 5be1a5944c..1fa3052e78 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -17,16 +17,15 @@ #include <cstdio> namespace esphome { -namespace ota_esphome { static const char *const TAG = "esphome.ota"; static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; -OTAESPHomeComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +ESPHomeOTAComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -OTAESPHomeComponent::OTAESPHomeComponent() { global_ota_component = this; } +ESPHomeOTAComponent::ESPHomeOTAComponent() { global_ota_component = this; } -void OTAESPHomeComponent::setup() { +void ESPHomeOTAComponent::setup() { server_ = socket::socket_ip(SOCK_STREAM, 0); if (server_ == nullptr) { ESP_LOGW(TAG, "Could not create socket"); @@ -70,7 +69,7 @@ void OTAESPHomeComponent::setup() { } } -void OTAESPHomeComponent::dump_config() { +void ESPHomeOTAComponent::dump_config() { ESP_LOGCONFIG(TAG, "Over-The-Air updates:"); ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_); ESP_LOGCONFIG(TAG, " OTA version: %d", USE_OTA_VERSION); @@ -80,13 +79,13 @@ void OTAESPHomeComponent::dump_config() { } #endif if (this->has_safe_mode_ && this->safe_mode_rtc_value_ > 1 && - this->safe_mode_rtc_value_ != OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC) { + 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 OTAESPHomeComponent::loop() { +void ESPHomeOTAComponent::loop() { this->handle_(); if (this->has_safe_mode_ && (millis() - this->safe_mode_start_time_) > this->safe_mode_enable_time_) { @@ -99,7 +98,7 @@ void OTAESPHomeComponent::loop() { static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01; -void OTAESPHomeComponent::handle_() { +void ESPHomeOTAComponent::handle_() { ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN; bool update_started = false; size_t total = 0; @@ -362,7 +361,7 @@ error: #endif } -bool OTAESPHomeComponent::readall_(uint8_t *buf, size_t len) { +bool ESPHomeOTAComponent::readall_(uint8_t *buf, size_t len) { uint32_t start = millis(); uint32_t at = 0; while (len - at > 0) { @@ -393,7 +392,7 @@ bool OTAESPHomeComponent::readall_(uint8_t *buf, size_t len) { return true; } -bool OTAESPHomeComponent::writeall_(const uint8_t *buf, size_t len) { +bool ESPHomeOTAComponent::writeall_(const uint8_t *buf, size_t len) { uint32_t start = millis(); uint32_t at = 0; while (len - at > 0) { @@ -421,31 +420,31 @@ bool OTAESPHomeComponent::writeall_(const uint8_t *buf, size_t len) { return true; } -float OTAESPHomeComponent::get_setup_priority() const { return setup_priority::AFTER_WIFI; } -uint16_t OTAESPHomeComponent::get_port() const { return this->port_; } -void OTAESPHomeComponent::set_port(uint16_t port) { this->port_ = port; } +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 OTAESPHomeComponent::set_safe_mode_pending(const bool &pending) { +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 != OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC) { + if (pending && current_rtc != ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC) { ESP_LOGI(TAG, "Device will enter safe mode on next boot"); - this->write_rtc_(OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC); + this->write_rtc_(ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC); } - if (!pending && current_rtc == OTAESPHomeComponent::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 OTAESPHomeComponent::get_safe_mode_pending() { - return this->has_safe_mode_ && this->read_rtc_() == OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC; +bool ESPHomeOTAComponent::get_safe_mode_pending() { + return this->has_safe_mode_ && this->read_rtc_() == ESPHomeOTAComponent::ENTER_SAFE_MODE_MAGIC; } -bool OTAESPHomeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time) { +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; @@ -453,7 +452,7 @@ bool OTAESPHomeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t this->rtc_ = global_preferences->make_preference<uint32_t>(233825507UL, false); this->safe_mode_rtc_value_ = this->read_rtc_(); - bool is_manual_safe_mode = this->safe_mode_rtc_value_ == OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC; + 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"); @@ -487,27 +486,26 @@ bool OTAESPHomeComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t return false; } } -void OTAESPHomeComponent::write_rtc_(uint32_t val) { +void ESPHomeOTAComponent::write_rtc_(uint32_t val) { this->rtc_.save(&val); global_preferences->sync(); } -uint32_t OTAESPHomeComponent::read_rtc_() { +uint32_t ESPHomeOTAComponent::read_rtc_() { uint32_t val; if (!this->rtc_.load(&val)) return 0; return val; } -void OTAESPHomeComponent::clean_rtc() { this->write_rtc_(0); } -void OTAESPHomeComponent::on_safe_shutdown() { - if (this->has_safe_mode_ && this->read_rtc_() != OTAESPHomeComponent::ENTER_SAFE_MODE_MAGIC) +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(); } #ifdef USE_OTA_STATE_CALLBACK -void OTAESPHomeComponent::add_on_state_callback(std::function<void(OTAESPHomeState, float, uint8_t)> &&callback) { +void ESPHomeOTAComponent::add_on_state_callback(std::function<void(OTAESPHomeState, float, uint8_t)> &&callback) { this->state_callback_.add(std::move(callback)); } #endif -} // namespace ota_esphome } // namespace esphome diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index 593a5e9a6d..bbe3173ade 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -7,14 +7,13 @@ #include "esphome/core/preferences.h" namespace esphome { -namespace ota_esphome { enum OTAESPHomeState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; -/// OTAESPHomeComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. +/// ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class ESPHomeOTAComponent : public Component { public: - OTAESPHomeComponent(); + ESPHomeOTAComponent(); #ifdef USE_OTA_PASSWORD void set_auth_password(const std::string &password) { password_ = password; } #endif // USE_OTA_PASSWORD @@ -77,7 +76,6 @@ class ESPHomeOTAComponent : public Component { #endif }; -extern OTAESPHomeComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +extern ESPHomeOTAComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) -} // namespace ota_esphome } // namespace esphome diff --git a/esphome/components/safe_mode/button/__init__.py b/esphome/components/safe_mode/button/__init__.py index ffd2b4e6ee..68aecdbc53 100644 --- a/esphome/components/safe_mode/button/__init__.py +++ b/esphome/components/safe_mode/button/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import button -from esphome.components.esphome.ota import OTAESPHomeComponent +from esphome.components.esphome.ota import ESPHomeOTAComponent from esphome.const import ( CONF_ESPHOME, DEVICE_CLASS_RESTART, @@ -21,7 +21,7 @@ CONFIG_SCHEMA = ( entity_category=ENTITY_CATEGORY_CONFIG, icon=ICON_RESTART_ALERT, ) - .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(OTAESPHomeComponent)}) + .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(ESPHomeOTAComponent)}) .extend(cv.COMPONENT_SCHEMA) ) diff --git a/esphome/components/safe_mode/button/safe_mode_button.cpp b/esphome/components/safe_mode/button/safe_mode_button.cpp index 673af02c20..d513b79c12 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.cpp +++ b/esphome/components/safe_mode/button/safe_mode_button.cpp @@ -8,7 +8,7 @@ namespace safe_mode { static const char *const TAG = "safe_mode.button"; -void SafeModeButton::set_ota(ota_esphome::OTAESPHomeComponent *ota) { this->ota_ = ota; } +void SafeModeButton::set_ota(esphome::ESPHomeOTAComponent *ota) { this->ota_ = ota; } void SafeModeButton::press_action() { ESP_LOGI(TAG, "Restarting device in safe mode..."); diff --git a/esphome/components/safe_mode/button/safe_mode_button.h b/esphome/components/safe_mode/button/safe_mode_button.h index 4ac0d2d177..a306735b7f 100644 --- a/esphome/components/safe_mode/button/safe_mode_button.h +++ b/esphome/components/safe_mode/button/safe_mode_button.h @@ -10,10 +10,10 @@ namespace safe_mode { class SafeModeButton : public button::Button, public Component { public: void dump_config() override; - void set_ota(ota_esphome::OTAESPHomeComponent *ota); + void set_ota(esphome::ESPHomeOTAComponent *ota); protected: - ota_esphome::OTAESPHomeComponent *ota_; + esphome::ESPHomeOTAComponent *ota_; void press_action() override; }; diff --git a/esphome/components/safe_mode/switch/__init__.py b/esphome/components/safe_mode/switch/__init__.py index c576cc7403..970e97ae52 100644 --- a/esphome/components/safe_mode/switch/__init__.py +++ b/esphome/components/safe_mode/switch/__init__.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import switch -from esphome.components.esphome.ota import OTAESPHomeComponent +from esphome.components.esphome.ota import ESPHomeOTAComponent from esphome.const import ( CONF_ESPHOME, ENTITY_CATEGORY_CONFIG, @@ -20,7 +20,7 @@ CONFIG_SCHEMA = ( entity_category=ENTITY_CATEGORY_CONFIG, icon=ICON_RESTART_ALERT, ) - .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(OTAESPHomeComponent)}) + .extend({cv.GenerateID(CONF_ESPHOME): cv.use_id(ESPHomeOTAComponent)}) .extend(cv.COMPONENT_SCHEMA) ) diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.cpp b/esphome/components/safe_mode/switch/safe_mode_switch.cpp index a145a71386..71408df140 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.cpp +++ b/esphome/components/safe_mode/switch/safe_mode_switch.cpp @@ -8,7 +8,7 @@ namespace safe_mode { static const char *const TAG = "safe_mode_switch"; -void SafeModeSwitch::set_ota(ota_esphome::OTAESPHomeComponent *ota) { this->ota_ = ota; } +void SafeModeSwitch::set_ota(esphome::ESPHomeOTAComponent *ota) { this->ota_ = ota; } void SafeModeSwitch::write_state(bool state) { // Acknowledge diff --git a/esphome/components/safe_mode/switch/safe_mode_switch.h b/esphome/components/safe_mode/switch/safe_mode_switch.h index 7fc8927c80..5bd15a44de 100644 --- a/esphome/components/safe_mode/switch/safe_mode_switch.h +++ b/esphome/components/safe_mode/switch/safe_mode_switch.h @@ -10,10 +10,10 @@ namespace safe_mode { class SafeModeSwitch : public switch_::Switch, public Component { public: void dump_config() override; - void set_ota(ota_esphome::OTAESPHomeComponent *ota); + void set_ota(esphome::ESPHomeOTAComponent *ota); protected: - ota_esphome::OTAESPHomeComponent *ota_; + esphome::ESPHomeOTAComponent *ota_; void write_state(bool state) override; }; From 8426084ebfe523066855c20dcd636b0dfb04bc5d Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Sun, 21 Apr 2024 04:14:07 -0500 Subject: [PATCH 07/26] Refactor more bits into the base `ota` component --- esphome/components/esphome/ota/__init__.py | 112 +++++------------- esphome/components/esphome/ota/automation.h | 69 ----------- .../components/esphome/ota/ota_esphome.cpp | 19 ++- esphome/components/esphome/ota/ota_esphome.h | 16 +-- esphome/components/ota/__init__.py | 52 +++++++- esphome/components/ota/ota_backend.h | 81 +++++++++++++ 6 files changed, 175 insertions(+), 174 deletions(-) delete mode 100644 esphome/components/esphome/ota/automation.h diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index d94e2ab921..e6f99ba18a 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -2,6 +2,7 @@ from esphome.cpp_generator import RawExpression import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation +from esphome.components import ota from esphome.const import ( CONF_ID, CONF_NUM_ATTEMPTS, @@ -21,83 +22,34 @@ CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5", "socket"] DEPENDENCIES = ["network"] -CONF_ON_BEGIN = "on_begin" -CONF_ON_END = "on_end" -CONF_ON_ERROR = "on_error" -CONF_ON_PROGRESS = "on_progress" -CONF_ON_STATE_CHANGE = "on_state_change" - esphome = cg.esphome_ns.namespace("esphome") - -ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", cg.Component) -ESPHomeOTAEndTrigger = esphome.class_( - "ESPHomeOTAEndTrigger", automation.Trigger.template() -) -ESPHomeOTAErrorTrigger = esphome.class_( - "ESPHomeOTAErrorTrigger", automation.Trigger.template() -) -ESPHomeOTAProgressTrigger = esphome.class_( - "ESPHomeOTAProgressTrigger", automation.Trigger.template() -) -ESPHomeOTAStartTrigger = esphome.class_( - "ESPHomeOTAStartTrigger", automation.Trigger.template() -) -ESPHomeOTAStateChangeTrigger = esphome.class_( - "ESPHomeOTAStateChangeTrigger", automation.Trigger.template() -) - -ESPHomeOTAState = esphome.enum("ESPHomeOTAState") +ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", ota.OTAComponent) -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, - esp8266=8266, - esp32=3232, - rp2040=2040, - bk72xx=8892, - 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_ON_STATE_CHANGE): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESPHomeOTAStateChangeTrigger - ), - } - ), - cv.Optional(CONF_ON_BEGIN): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPHomeOTAStartTrigger), - } - ), - cv.Optional(CONF_ON_END): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPHomeOTAEndTrigger), - } - ), - cv.Optional(CONF_ON_ERROR): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPHomeOTAErrorTrigger), - } - ), - cv.Optional(CONF_ON_PROGRESS): automation.validate_automation( - { - cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id( - ESPHomeOTAProgressTrigger - ), - } - ), - } -).extend(cv.COMPONENT_SCHEMA) +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, + esp8266=8266, + esp32=3232, + rp2040=2040, + bk72xx=8892, + 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, + } + ) + .extend(ota.BASE_OTA_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) +) @coroutine_with_priority(50.0) @@ -128,23 +80,23 @@ async def to_code(config): cg.add_library("Updater", None) use_state_callback = False - for conf in config.get(CONF_ON_STATE_CHANGE, []): + for conf in config.get(ota.CONF_ON_STATE_CHANGE, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(ESPHomeOTAState, "state")], conf) + await automation.build_automation(trigger, [(ota.OTAState, "state")], conf) use_state_callback = True - for conf in config.get(CONF_ON_BEGIN, []): + for conf in config.get(ota.CONF_ON_BEGIN, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) use_state_callback = True - for conf in config.get(CONF_ON_PROGRESS, []): + for conf in config.get(ota.CONF_ON_PROGRESS, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(float, "x")], conf) use_state_callback = True - for conf in config.get(CONF_ON_END, []): + for conf in config.get(ota.CONF_ON_END, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [], conf) use_state_callback = True - for conf in config.get(CONF_ON_ERROR, []): + for conf in config.get(ota.CONF_ON_ERROR, []): trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) await automation.build_automation(trigger, [(cg.uint8, "x")], conf) use_state_callback = True diff --git a/esphome/components/esphome/ota/automation.h b/esphome/components/esphome/ota/automation.h deleted file mode 100644 index b922b6d77b..0000000000 --- a/esphome/components/esphome/ota/automation.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#ifdef USE_OTA_STATE_CALLBACK -#include "ota_esphome.h" - -#include "esphome/core/component.h" -#include "esphome/core/defines.h" -#include "esphome/core/automation.h" - -namespace esphome { - -class OTAESPHomeStateChangeTrigger : public Trigger<OTAESPHomeState> { - public: - explicit OTAESPHomeStateChangeTrigger(OTAESPHomeComponent *parent) { - parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { - if (!parent->is_failed()) { - return trigger(state); - } - }); - } -}; - -class OTAESPHomeStartTrigger : public Trigger<> { - public: - explicit OTAESPHomeStartTrigger(OTAESPHomeComponent *parent) { - parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { - if (state == OTA_STARTED && !parent->is_failed()) { - trigger(); - } - }); - } -}; - -class OTAESPHomeProgressTrigger : public Trigger<float> { - public: - explicit OTAESPHomeProgressTrigger(OTAESPHomeComponent *parent) { - parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { - if (state == OTA_IN_PROGRESS && !parent->is_failed()) { - trigger(progress); - } - }); - } -}; - -class OTAESPHomeEndTrigger : public Trigger<> { - public: - explicit OTAESPHomeEndTrigger(OTAESPHomeComponent *parent) { - parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { - if (state == OTA_COMPLETED && !parent->is_failed()) { - trigger(); - } - }); - } -}; - -class OTAESPHomeErrorTrigger : public Trigger<uint8_t> { - public: - explicit OTAESPHomeErrorTrigger(OTAESPHomeComponent *parent) { - parent->add_on_state_callback([this, parent](OTAESPHomeState state, float progress, uint8_t error) { - if (state == OTA_ERROR && !parent->is_failed()) { - trigger(error); - } - }); - } -}; - -} // namespace esphome - -#endif // USE_OTA_STATE_CALLBACK diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 1fa3052e78..7b76380fb4 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -131,7 +131,7 @@ void ESPHomeOTAComponent::handle_() { ESP_LOGD(TAG, "Starting OTA update from %s...", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(OTA_STARTED, 0.0f, 0); + this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); #endif if (!this->readall_(buf, 5)) { @@ -306,7 +306,7 @@ void ESPHomeOTAComponent::handle_() { float percentage = (total * 100.0f) / ota_size; ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(OTA_IN_PROGRESS, percentage, 0); + this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0); #endif // feed watchdog and give other tasks a chance to run App.feed_wdt(); @@ -340,7 +340,7 @@ void ESPHomeOTAComponent::handle_() { ESP_LOGI(TAG, "OTA update finished"); this->status_clear_warning(); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(OTA_COMPLETED, 100.0f, 0); + this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, 0); #endif delay(100); // NOLINT App.safe_reboot(); @@ -357,7 +357,7 @@ error: this->status_momentary_error("onerror", 5000); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code)); + this->state_callback_.call(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code)); #endif } @@ -486,26 +486,23 @@ bool ESPHomeOTAComponent::should_enter_safe_mode(uint8_t num_attempts, uint32_t 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(); } - -#ifdef USE_OTA_STATE_CALLBACK -void ESPHomeOTAComponent::add_on_state_callback(std::function<void(OTAESPHomeState, float, uint8_t)> &&callback) { - this->state_callback_.add(std::move(callback)); -} -#endif - } // namespace esphome diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index bbe3173ade..f230f2b465 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -1,17 +1,15 @@ #pragma once -#include "esphome/components/socket/socket.h" -#include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" +#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/socket/socket.h" namespace esphome { -enum OTAESPHomeState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; - /// ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. -class ESPHomeOTAComponent : public Component { +class ESPHomeOTAComponent : public ota::OTAComponent { public: ESPHomeOTAComponent(); #ifdef USE_OTA_PASSWORD @@ -27,10 +25,6 @@ class ESPHomeOTAComponent : public Component { void set_safe_mode_pending(const bool &pending); bool get_safe_mode_pending(); -#ifdef USE_OTA_STATE_CALLBACK - void add_on_state_callback(std::function<void(OTAESPHomeState, float, uint8_t)> &&callback); -#endif - // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) void setup() override; @@ -70,10 +64,6 @@ class ESPHomeOTAComponent : public Component { static const uint32_t ENTER_SAFE_MODE_MAGIC = 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot - -#ifdef USE_OTA_STATE_CALLBACK - CallbackManager<void(OTAESPHomeState, float, uint8_t)> state_callback_{}; -#endif }; extern ESPHomeOTAComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index aed0b87f85..719f25082f 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,6 +1,8 @@ +import esphome.codegen as cg import esphome.config_validation as cv +from esphome import automation -from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM +from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5"] @@ -8,6 +10,24 @@ DEPENDENCIES = ["network"] IS_PLATFORM_COMPONENT = True +CONF_ON_BEGIN = "on_begin" +CONF_ON_END = "on_end" +CONF_ON_ERROR = "on_error" +CONF_ON_PROGRESS = "on_progress" +CONF_ON_STATE_CHANGE = "on_state_change" + + +ota = cg.esphome_ns.namespace("ota") +OTAComponent = ota.class_("OTAComponent", cg.Component) +OTAState = ota.enum("OTAState") +OTAEndTrigger = ota.class_("OTAEndTrigger", automation.Trigger.template()) +OTAErrorTrigger = ota.class_("OTAErrorTrigger", automation.Trigger.template()) +OTAProgressTrigger = ota.class_("OTAProgressTrigger", automation.Trigger.template()) +OTAStartTrigger = ota.class_("OTAStartTrigger", automation.Trigger.template()) +OTAStateChangeTrigger = ota.class_( + "OTAStateChangeTrigger", automation.Trigger.template() +) + def _ota_final_validate(config): if len(config) < 1: @@ -17,3 +37,33 @@ def _ota_final_validate(config): FINAL_VALIDATE_SCHEMA = _ota_final_validate + +BASE_OTA_SCHEMA = cv.Schema( + { + cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStateChangeTrigger), + } + ), + cv.Optional(CONF_ON_BEGIN): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStartTrigger), + } + ), + cv.Optional(CONF_ON_END): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAEndTrigger), + } + ), + cv.Optional(CONF_ON_ERROR): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAErrorTrigger), + } + ), + cv.Optional(CONF_ON_PROGRESS): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAProgressTrigger), + } + ), + } +) diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index 471eebb390..9f2a851f5a 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -1,4 +1,10 @@ #pragma once +#ifdef USE_OTA_STATE_CALLBACK +#include "esphome/core/automation.h" +#include "esphome/core/defines.h" +#endif + +#include "esphome/core/component.h" #include "esphome/core/helpers.h" namespace esphome { @@ -33,6 +39,8 @@ enum OTAResponseTypes { OTA_RESPONSE_ERROR_UNKNOWN = 0xFF, }; +enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; + class OTABackend { public: virtual ~OTABackend() = default; @@ -44,7 +52,80 @@ class OTABackend { virtual bool supports_compression() = 0; }; +class OTAComponent : public Component { +#ifdef USE_OTA_STATE_CALLBACK + public: + void add_on_state_callback(std::function<void(ota::OTAState, float, uint8_t)> &&callback) { + this->state_callback_.add(std::move(callback)); + } + + protected: + CallbackManager<void(ota::OTAState, float, uint8_t)> state_callback_{}; +#endif +}; + std::unique_ptr<ota::OTABackend> make_ota_backend(); +/// +/// Automations +/// + +#ifdef USE_OTA_STATE_CALLBACK +class OTAStateChangeTrigger : public Trigger<OTAState> { + public: + explicit OTAStateChangeTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (!parent->is_failed()) { + return trigger(state); + } + }); + } +}; + +class OTAStartTrigger : public Trigger<> { + public: + explicit OTAStartTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_STARTED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAProgressTrigger : public Trigger<float> { + public: + explicit OTAProgressTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_IN_PROGRESS && !parent->is_failed()) { + trigger(progress); + } + }); + } +}; + +class OTAEndTrigger : public Trigger<> { + public: + explicit OTAEndTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_COMPLETED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAErrorTrigger : public Trigger<uint8_t> { + public: + explicit OTAErrorTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_ERROR && !parent->is_failed()) { + trigger(error); + } + }); + } +}; +#endif + } // namespace ota } // namespace esphome From 17f9774418d7ea8ad148df3ae2cb726183a9c852 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Sun, 21 Apr 2024 04:25:17 -0500 Subject: [PATCH 08/26] Fix esp32_ble_tracker --- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index e102fe41c4..f6148a64f9 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -58,12 +58,11 @@ void ESP32BLETracker::setup() { this->scanner_idle_ = true; #ifdef USE_OTA - ota_esphome::global_ota_component->add_on_state_callback( - [this](ota_esphome::OTAESPHomeState state, float progress, uint8_t error) { - if (state == ota_esphome::OTA_STARTED) { - this->stop_scan(); - } - }); + esphome::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) { + if (state == ota::OTA_STARTED) { + this->stop_scan(); + } + }); #endif } From 5d890be1138b4cd1da3920debbe540b43cc09cf2 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Sun, 21 Apr 2024 20:55:53 -0500 Subject: [PATCH 09/26] Fix `dummy_main.cpp`, too --- tests/dummy_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/dummy_main.cpp b/tests/dummy_main.cpp index 42af4eb47f..3ba4c8bd07 100644 --- a/tests/dummy_main.cpp +++ b/tests/dummy_main.cpp @@ -25,7 +25,7 @@ void setup() { ap.set_password("password1"); wifi->add_sta(ap); - auto *ota = new ota_esphome::OTAESPHomeComponent(); // NOLINT + auto *ota = new esphome::ESPHomeOTAComponent(); // NOLINT ota->set_port(8266); App.setup(); From d5885f51a697856665d0b56f2e6de1d63397866d Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Wed, 24 Apr 2024 00:03:32 -0500 Subject: [PATCH 10/26] Update tests --- tests/components/ota/common.yaml | 31 +++++++++++++++++++++ tests/components/ota/test.esp32-c3-idf.yaml | 31 +-------------------- tests/components/ota/test.esp32-c3.yaml | 31 +-------------------- tests/components/ota/test.esp32-idf.yaml | 31 +-------------------- tests/components/ota/test.esp32.yaml | 31 +-------------------- tests/components/ota/test.esp8266.yaml | 31 +-------------------- tests/components/ota/test.rp2040.yaml | 31 +-------------------- 7 files changed, 37 insertions(+), 180 deletions(-) create mode 100644 tests/components/ota/common.yaml diff --git a/tests/components/ota/common.yaml b/tests/components/ota/common.yaml new file mode 100644 index 0000000000..4910e2d891 --- /dev/null +++ b/tests/components/ota/common.yaml @@ -0,0 +1,31 @@ +wifi: + ssid: MySSID + password: password1 + +ota: + - platform: esphome + safe_mode: true + password: "superlongpasswordthatnoonewillknow" + port: 3286 + reboot_timeout: 2min + num_attempts: 5 + on_begin: + then: + - logger.log: "OTA start" + on_progress: + then: + - logger.log: + format: "OTA progress %0.1f%%" + args: ["x"] + on_end: + then: + - logger.log: "OTA end" + on_error: + then: + - logger.log: + format: "OTA update error %d" + args: ["x"] + on_state_change: + then: + lambda: >- + ESP_LOGD("ota", "State %d", state); diff --git a/tests/components/ota/test.esp32-c3-idf.yaml b/tests/components/ota/test.esp32-c3-idf.yaml index 367454995f..dade44d145 100644 --- a/tests/components/ota/test.esp32-c3-idf.yaml +++ b/tests/components/ota/test.esp32-c3-idf.yaml @@ -1,30 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_begin: - then: - - logger.log: "OTA start" - on_progress: - then: - - logger.log: - format: "OTA progress %0.1f%%" - args: ["x"] - on_end: - then: - - logger.log: "OTA end" - on_error: - then: - - logger.log: - format: "OTA update error %d" - args: ["x"] - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); +<<: !include common.yaml diff --git a/tests/components/ota/test.esp32-c3.yaml b/tests/components/ota/test.esp32-c3.yaml index 367454995f..dade44d145 100644 --- a/tests/components/ota/test.esp32-c3.yaml +++ b/tests/components/ota/test.esp32-c3.yaml @@ -1,30 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_begin: - then: - - logger.log: "OTA start" - on_progress: - then: - - logger.log: - format: "OTA progress %0.1f%%" - args: ["x"] - on_end: - then: - - logger.log: "OTA end" - on_error: - then: - - logger.log: - format: "OTA update error %d" - args: ["x"] - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); +<<: !include common.yaml diff --git a/tests/components/ota/test.esp32-idf.yaml b/tests/components/ota/test.esp32-idf.yaml index 367454995f..dade44d145 100644 --- a/tests/components/ota/test.esp32-idf.yaml +++ b/tests/components/ota/test.esp32-idf.yaml @@ -1,30 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_begin: - then: - - logger.log: "OTA start" - on_progress: - then: - - logger.log: - format: "OTA progress %0.1f%%" - args: ["x"] - on_end: - then: - - logger.log: "OTA end" - on_error: - then: - - logger.log: - format: "OTA update error %d" - args: ["x"] - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); +<<: !include common.yaml diff --git a/tests/components/ota/test.esp32.yaml b/tests/components/ota/test.esp32.yaml index 367454995f..dade44d145 100644 --- a/tests/components/ota/test.esp32.yaml +++ b/tests/components/ota/test.esp32.yaml @@ -1,30 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_begin: - then: - - logger.log: "OTA start" - on_progress: - then: - - logger.log: - format: "OTA progress %0.1f%%" - args: ["x"] - on_end: - then: - - logger.log: "OTA end" - on_error: - then: - - logger.log: - format: "OTA update error %d" - args: ["x"] - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); +<<: !include common.yaml diff --git a/tests/components/ota/test.esp8266.yaml b/tests/components/ota/test.esp8266.yaml index 367454995f..dade44d145 100644 --- a/tests/components/ota/test.esp8266.yaml +++ b/tests/components/ota/test.esp8266.yaml @@ -1,30 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_begin: - then: - - logger.log: "OTA start" - on_progress: - then: - - logger.log: - format: "OTA progress %0.1f%%" - args: ["x"] - on_end: - then: - - logger.log: "OTA end" - on_error: - then: - - logger.log: - format: "OTA update error %d" - args: ["x"] - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); +<<: !include common.yaml diff --git a/tests/components/ota/test.rp2040.yaml b/tests/components/ota/test.rp2040.yaml index 367454995f..dade44d145 100644 --- a/tests/components/ota/test.rp2040.yaml +++ b/tests/components/ota/test.rp2040.yaml @@ -1,30 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - safe_mode: true - password: "superlongpasswordthatnoonewillknow" - port: 3286 - reboot_timeout: 2min - num_attempts: 5 - on_begin: - then: - - logger.log: "OTA start" - on_progress: - then: - - logger.log: - format: "OTA progress %0.1f%%" - args: ["x"] - on_end: - then: - - logger.log: "OTA end" - on_error: - then: - - logger.log: - format: "OTA update error %d" - args: ["x"] - on_state_change: - then: - lambda: >- - ESP_LOGD("ota", "State %d", state); +<<: !include common.yaml From 0d8e731e6963056b09accdda5472eea0a8d99c20 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Wed, 24 Apr 2024 01:19:29 -0500 Subject: [PATCH 11/26] Update safe_mode tests, too --- tests/components/safe_mode/common.yaml | 15 +++++++++++++++ tests/components/safe_mode/test.esp32-c3-idf.yaml | 14 +------------- tests/components/safe_mode/test.esp32-c3.yaml | 14 +------------- tests/components/safe_mode/test.esp32-idf.yaml | 14 +------------- tests/components/safe_mode/test.esp32.yaml | 14 +------------- tests/components/safe_mode/test.esp8266.yaml | 14 +------------- tests/components/safe_mode/test.rp2040.yaml | 14 +------------- 7 files changed, 21 insertions(+), 78 deletions(-) create mode 100644 tests/components/safe_mode/common.yaml diff --git a/tests/components/safe_mode/common.yaml b/tests/components/safe_mode/common.yaml new file mode 100644 index 0000000000..1dfc516254 --- /dev/null +++ b/tests/components/safe_mode/common.yaml @@ -0,0 +1,15 @@ +wifi: + ssid: MySSID + password: password1 + +ota: + - platform: esphome + safe_mode: true + +button: + - platform: safe_mode + name: Safe Mode Button + +switch: + - platform: safe_mode + name: Safe Mode Switch diff --git a/tests/components/safe_mode/test.esp32-c3-idf.yaml b/tests/components/safe_mode/test.esp32-c3-idf.yaml index df0abd9aec..dade44d145 100644 --- a/tests/components/safe_mode/test.esp32-c3-idf.yaml +++ b/tests/components/safe_mode/test.esp32-c3-idf.yaml @@ -1,13 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - -button: - - platform: safe_mode - name: Safe Mode Button - -switch: - - platform: safe_mode - name: Safe Mode Switch +<<: !include common.yaml diff --git a/tests/components/safe_mode/test.esp32-c3.yaml b/tests/components/safe_mode/test.esp32-c3.yaml index df0abd9aec..dade44d145 100644 --- a/tests/components/safe_mode/test.esp32-c3.yaml +++ b/tests/components/safe_mode/test.esp32-c3.yaml @@ -1,13 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - -button: - - platform: safe_mode - name: Safe Mode Button - -switch: - - platform: safe_mode - name: Safe Mode Switch +<<: !include common.yaml diff --git a/tests/components/safe_mode/test.esp32-idf.yaml b/tests/components/safe_mode/test.esp32-idf.yaml index df0abd9aec..dade44d145 100644 --- a/tests/components/safe_mode/test.esp32-idf.yaml +++ b/tests/components/safe_mode/test.esp32-idf.yaml @@ -1,13 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - -button: - - platform: safe_mode - name: Safe Mode Button - -switch: - - platform: safe_mode - name: Safe Mode Switch +<<: !include common.yaml diff --git a/tests/components/safe_mode/test.esp32.yaml b/tests/components/safe_mode/test.esp32.yaml index df0abd9aec..dade44d145 100644 --- a/tests/components/safe_mode/test.esp32.yaml +++ b/tests/components/safe_mode/test.esp32.yaml @@ -1,13 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - -button: - - platform: safe_mode - name: Safe Mode Button - -switch: - - platform: safe_mode - name: Safe Mode Switch +<<: !include common.yaml diff --git a/tests/components/safe_mode/test.esp8266.yaml b/tests/components/safe_mode/test.esp8266.yaml index df0abd9aec..dade44d145 100644 --- a/tests/components/safe_mode/test.esp8266.yaml +++ b/tests/components/safe_mode/test.esp8266.yaml @@ -1,13 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - -button: - - platform: safe_mode - name: Safe Mode Button - -switch: - - platform: safe_mode - name: Safe Mode Switch +<<: !include common.yaml diff --git a/tests/components/safe_mode/test.rp2040.yaml b/tests/components/safe_mode/test.rp2040.yaml index df0abd9aec..dade44d145 100644 --- a/tests/components/safe_mode/test.rp2040.yaml +++ b/tests/components/safe_mode/test.rp2040.yaml @@ -1,13 +1 @@ -wifi: - ssid: MySSID - password: password1 - -ota: - -button: - - platform: safe_mode - name: Safe Mode Button - -switch: - - platform: safe_mode - name: Safe Mode Switch +<<: !include common.yaml From f2b6a0909a4e72e4ab597dd1b0410c33751c9886 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:24:10 +1200 Subject: [PATCH 12/26] Update dependencies --- esphome/components/safe_mode/button/__init__.py | 2 +- esphome/components/safe_mode/switch/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/safe_mode/button/__init__.py b/esphome/components/safe_mode/button/__init__.py index 68aecdbc53..bd51d2e038 100644 --- a/esphome/components/safe_mode/button/__init__.py +++ b/esphome/components/safe_mode/button/__init__.py @@ -10,7 +10,7 @@ from esphome.const import ( ) from .. import safe_mode_ns -DEPENDENCIES = ["ota"] +DEPENDENCIES = ["ota.esphome"] SafeModeButton = safe_mode_ns.class_("SafeModeButton", button.Button, cg.Component) diff --git a/esphome/components/safe_mode/switch/__init__.py b/esphome/components/safe_mode/switch/__init__.py index 970e97ae52..0f8e500482 100644 --- a/esphome/components/safe_mode/switch/__init__.py +++ b/esphome/components/safe_mode/switch/__init__.py @@ -9,7 +9,7 @@ from esphome.const import ( ) from .. import safe_mode_ns -DEPENDENCIES = ["ota"] +DEPENDENCIES = ["ota.esphome"] SafeModeSwitch = safe_mode_ns.class_("SafeModeSwitch", switch.Switch, cg.Component) From e234557cf3116a93e5586b883052b71572b1092c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Apr 2024 10:20:03 +1200 Subject: [PATCH 13/26] A wild walrus appeared --- esphome/__main__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index df57cf5ab7..a83cbbfeb1 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -332,8 +332,8 @@ def upload_program(config, args, host): return 1 # Unknown target platform ota_conf = {} - if CONF_OTA in config: - for ota_item in config.get(CONF_OTA): + if ota := config.get(CONF_OTA): + for ota_item in ota: if ota_item[CONF_PLATFORM] == CONF_ESPHOME: ota_conf = ota_item break From 164936c5fb9e65f4dca09bbefddc428874e774f2 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:50:23 +1200 Subject: [PATCH 14/26] The walrus ran away --- esphome/__main__.py | 9 ++++----- esphome/cpp_helpers.py | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/esphome/__main__.py b/esphome/__main__.py index a83cbbfeb1..9930119c86 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -332,11 +332,10 @@ def upload_program(config, args, host): return 1 # Unknown target platform ota_conf = {} - if ota := config.get(CONF_OTA): - for ota_item in ota: - if ota_item[CONF_PLATFORM] == CONF_ESPHOME: - ota_conf = ota_item - break + for ota_item in config.get(CONF_OTA, []): + if ota_item[CONF_PLATFORM] == CONF_ESPHOME: + ota_conf = ota_item + break if not ota_conf: raise EsphomeError( diff --git a/esphome/cpp_helpers.py b/esphome/cpp_helpers.py index 402f60284e..ce494e5d9d 100644 --- a/esphome/cpp_helpers.py +++ b/esphome/cpp_helpers.py @@ -142,11 +142,10 @@ async def build_registry_list(registry, config): async def past_safe_mode(): ota_conf = {} - if CONF_OTA in CORE.config: - for ota_item in CORE.config.get(CONF_OTA): - if ota_item[CONF_PLATFORM] == CONF_ESPHOME: - ota_conf = ota_item - break + 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 From c719b7203cd5e269479a66b15bf18e46aa20b076 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Sun, 28 Apr 2024 21:11:16 -0500 Subject: [PATCH 15/26] Move automations to `automation.h`, remove `network` dependency --- esphome/components/ota/__init__.py | 1 - esphome/components/ota/automation.h | 67 ++++++++++++++++++++++++++++ esphome/components/ota/ota_backend.h | 62 ------------------------- 3 files changed, 67 insertions(+), 63 deletions(-) create mode 100644 esphome/components/ota/automation.h diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 719f25082f..94ba199788 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -6,7 +6,6 @@ from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5"] -DEPENDENCIES = ["network"] IS_PLATFORM_COMPONENT = True diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h new file mode 100644 index 0000000000..91ec123010 --- /dev/null +++ b/esphome/components/ota/automation.h @@ -0,0 +1,67 @@ +#pragma once +#ifdef USE_OTA_STATE_CALLBACK +#include "ota_backend.h" + +#include "esphome/core/automation.h" + +namespace esphome { +namespace ota { + +class OTAStateChangeTrigger : public Trigger<OTAState> { + public: + explicit OTAStateChangeTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (!parent->is_failed()) { + return trigger(state); + } + }); + } +}; + +class OTAStartTrigger : public Trigger<> { + public: + explicit OTAStartTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_STARTED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAProgressTrigger : public Trigger<float> { + public: + explicit OTAProgressTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_IN_PROGRESS && !parent->is_failed()) { + trigger(progress); + } + }); + } +}; + +class OTAEndTrigger : public Trigger<> { + public: + explicit OTAEndTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_COMPLETED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAErrorTrigger : public Trigger<uint8_t> { + public: + explicit OTAErrorTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_ERROR && !parent->is_failed()) { + trigger(error); + } + }); + } +}; + +} // namespace ota +} // namespace esphome +#endif diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index 9f2a851f5a..9899d62fab 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -65,67 +65,5 @@ class OTAComponent : public Component { }; std::unique_ptr<ota::OTABackend> make_ota_backend(); - -/// -/// Automations -/// - -#ifdef USE_OTA_STATE_CALLBACK -class OTAStateChangeTrigger : public Trigger<OTAState> { - public: - explicit OTAStateChangeTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (!parent->is_failed()) { - return trigger(state); - } - }); - } -}; - -class OTAStartTrigger : public Trigger<> { - public: - explicit OTAStartTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_STARTED && !parent->is_failed()) { - trigger(); - } - }); - } -}; - -class OTAProgressTrigger : public Trigger<float> { - public: - explicit OTAProgressTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_IN_PROGRESS && !parent->is_failed()) { - trigger(progress); - } - }); - } -}; - -class OTAEndTrigger : public Trigger<> { - public: - explicit OTAEndTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_COMPLETED && !parent->is_failed()) { - trigger(); - } - }); - } -}; - -class OTAErrorTrigger : public Trigger<uint8_t> { - public: - explicit OTAErrorTrigger(OTAComponent *parent) { - parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_ERROR && !parent->is_failed()) { - trigger(error); - } - }); - } -}; -#endif - } // namespace ota } // namespace esphome From 4f8dd25478df50150e3c01e8a77195387a8dd5c4 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Sun, 28 Apr 2024 21:50:53 -0500 Subject: [PATCH 16/26] Enable `esp32_ble_tracker` to `stop_scan()` for all configured OTA platforms --- esphome/components/esp32_ble_tracker/__init__.py | 6 ++++++ .../esp32_ble_tracker/esp32_ble_tracker.cpp | 16 +++++++--------- .../esp32_ble_tracker/esp32_ble_tracker.h | 13 +++++++++++++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 1edeaadbfd..62efb4a278 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -15,6 +15,7 @@ from esphome.const import ( CONF_ON_BLE_ADVERTISE, CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, CONF_ON_BLE_SERVICE_DATA_ADVERTISE, + CONF_OTA, CONF_SERVICE_UUID, CONF_TRIGGER_ID, KEY_CORE, @@ -272,6 +273,11 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192) add_idf_sdkconfig_option("CONFIG_BT_ACL_CONNECTIONS", 9) + if CONF_OTA in CORE.config: + for ota_config in CORE.config.get(CONF_OTA): + ota_platform = await cg.get_variable(ota_config[CONF_ID]) + cg.add(var.add_ota_component(ota_platform)) + cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts cg.add_define("USE_ESP32_BLE_CLIENT") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 64e9a0b16e..a26ff0c7b5 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -17,10 +17,6 @@ #include <nvs_flash.h> #include <cinttypes> -#ifdef USE_OTA -#include "esphome/components/esphome/ota/ota_esphome.h" -#endif - #ifdef USE_ARDUINO #include <esp32-hal-bt.h> #endif @@ -58,11 +54,13 @@ void ESP32BLETracker::setup() { this->scanner_idle_ = true; #ifdef USE_OTA - esphome::global_ota_component->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) { - if (state == ota::OTA_STARTED) { - this->stop_scan(); - } - }); + for (auto &ota_comp : this->ota_components_) { + ota_comp->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) { + if (state == ota::OTA_STARTED) { + this->stop_scan(); + } + }); + } #endif } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 76dee875c5..c8dae96a97 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -11,6 +11,10 @@ #ifdef USE_ESP32 +#ifdef USE_OTA +#include "esphome/components/ota/ota_backend.h" +#endif + #include <esp_gap_ble_api.h> #include <esp_gattc_api.h> #include <esp_bt_defs.h> @@ -197,6 +201,10 @@ class ESP32BLETracker : public Component, void loop() override; +#ifdef USE_OTA + void add_ota_component(ota::OTAComponent *ota_component) { this->ota_components_.push_back(ota_component); } +#endif + void register_listener(ESPBTDeviceListener *listener); void register_client(ESPBTClient *client); void recalculate_advertisement_parser_types(); @@ -228,6 +236,11 @@ class ESP32BLETracker : public Component, int app_id_; +#ifdef USE_OTA + /// Vector of OTA components we'll hook into so we can stop scanning when an OTA update begins + std::vector<ota::OTAComponent *> ota_components_{}; +#endif + /// Vector of addresses that have already been printed in print_bt_device_info std::vector<uint64_t> already_discovered_; std::vector<ESPBTDeviceListener *> listeners_; From 2da0bcadc70093b9fc234baad5ee88ef104596b3 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Tue, 30 Apr 2024 01:31:59 -0500 Subject: [PATCH 17/26] Refactor `global_ota_component` usage --- .../components/esp32_ble_tracker/__init__.py | 6 ---- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 17 ++++++----- .../esp32_ble_tracker/esp32_ble_tracker.h | 13 --------- .../components/esphome/ota/ota_esphome.cpp | 8 +++--- esphome/components/esphome/ota/ota_esphome.h | 3 -- esphome/components/ota/ota_backend.cpp | 12 ++++++++ esphome/components/ota/ota_backend.h | 28 ++++++++++++++++--- 7 files changed, 50 insertions(+), 37 deletions(-) create mode 100644 esphome/components/ota/ota_backend.cpp diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 62efb4a278..1edeaadbfd 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -15,7 +15,6 @@ from esphome.const import ( CONF_ON_BLE_ADVERTISE, CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, CONF_ON_BLE_SERVICE_DATA_ADVERTISE, - CONF_OTA, CONF_SERVICE_UUID, CONF_TRIGGER_ID, KEY_CORE, @@ -273,11 +272,6 @@ async def to_code(config): add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192) add_idf_sdkconfig_option("CONFIG_BT_ACL_CONNECTIONS", 9) - if CONF_OTA in CORE.config: - for ota_config in CORE.config.get(CONF_OTA): - ota_platform = await cg.get_variable(ota_config[CONF_ID]) - cg.add(var.add_ota_component(ota_platform)) - cg.add_define("USE_OTA_STATE_CALLBACK") # To be notified when an OTA update starts cg.add_define("USE_ESP32_BLE_CLIENT") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index a26ff0c7b5..6b2a1caa13 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -17,6 +17,10 @@ #include <nvs_flash.h> #include <cinttypes> +#ifdef USE_OTA +#include "esphome/components/ota/ota_backend.h" +#endif + #ifdef USE_ARDUINO #include <esp32-hal-bt.h> #endif @@ -54,13 +58,12 @@ void ESP32BLETracker::setup() { this->scanner_idle_ = true; #ifdef USE_OTA - for (auto &ota_comp : this->ota_components_) { - ota_comp->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t error) { - if (state == ota::OTA_STARTED) { - this->stop_scan(); - } - }); - } + ota::global_ota_component->add_on_state_callback( + [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { + if (state == ota::OTA_STARTED) { + this->stop_scan(); + } + }); #endif } diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index c8dae96a97..76dee875c5 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -11,10 +11,6 @@ #ifdef USE_ESP32 -#ifdef USE_OTA -#include "esphome/components/ota/ota_backend.h" -#endif - #include <esp_gap_ble_api.h> #include <esp_gattc_api.h> #include <esp_bt_defs.h> @@ -201,10 +197,6 @@ class ESP32BLETracker : public Component, void loop() override; -#ifdef USE_OTA - void add_ota_component(ota::OTAComponent *ota_component) { this->ota_components_.push_back(ota_component); } -#endif - void register_listener(ESPBTDeviceListener *listener); void register_client(ESPBTClient *client); void recalculate_advertisement_parser_types(); @@ -236,11 +228,6 @@ class ESP32BLETracker : public Component, int app_id_; -#ifdef USE_OTA - /// Vector of OTA components we'll hook into so we can stop scanning when an OTA update begins - std::vector<ota::OTAComponent *> ota_components_{}; -#endif - /// Vector of addresses that have already been printed in print_bt_device_info std::vector<uint64_t> already_discovered_; std::vector<ESPBTDeviceListener *> listeners_; diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 7b76380fb4..26bc6b82f9 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -21,11 +21,11 @@ namespace esphome { static const char *const TAG = "esphome.ota"; static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; -ESPHomeOTAComponent *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -ESPHomeOTAComponent::ESPHomeOTAComponent() { global_ota_component = this; } - void ESPHomeOTAComponent::setup() { +#ifdef USE_OTA_STATE_CALLBACK + ota::global_ota_component->register_ota(this); +#endif + server_ = socket::socket_ip(SOCK_STREAM, 0); if (server_ == nullptr) { ESP_LOGW(TAG, "Could not create socket"); diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index f230f2b465..e8f36f05ca 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -11,7 +11,6 @@ namespace esphome { /// ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class ESPHomeOTAComponent : public ota::OTAComponent { public: - ESPHomeOTAComponent(); #ifdef USE_OTA_PASSWORD void set_auth_password(const std::string &password) { password_ = password; } #endif // USE_OTA_PASSWORD @@ -66,6 +65,4 @@ class ESPHomeOTAComponent : public ota::OTAComponent { 0x5afe5afe; ///< a magic number to indicate that safe mode should be entered on next boot }; -extern ESPHomeOTAComponent *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - } // namespace esphome diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp new file mode 100644 index 0000000000..bc7755fa83 --- /dev/null +++ b/esphome/components/ota/ota_backend.cpp @@ -0,0 +1,12 @@ +#include "ota_backend.h" + +namespace esphome { +namespace ota { + +#ifdef USE_OTA_STATE_CALLBACK +OTAGlobalCallback *global_ota_component = + new OTAGlobalCallback; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +#endif + +} // namespace ota +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index 9899d62fab..f72a63d0e4 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -1,12 +1,13 @@ #pragma once -#ifdef USE_OTA_STATE_CALLBACK -#include "esphome/core/automation.h" -#include "esphome/core/defines.h" -#endif #include "esphome/core/component.h" +#include "esphome/core/defines.h" #include "esphome/core/helpers.h" +#ifdef USE_OTA_STATE_CALLBACK +#include "esphome/core/automation.h" +#endif + namespace esphome { namespace ota { @@ -64,6 +65,25 @@ class OTAComponent : public Component { #endif }; +#ifdef USE_OTA_STATE_CALLBACK +class OTAGlobalCallback { + public: + void register_ota(OTAComponent *ota_caller) { + ota_caller->add_on_state_callback([this, ota_caller](OTAState state, float progress, uint8_t error) { + this->state_callback_.call(state, progress, error, ota_caller); + }); + } + void add_on_state_callback(std::function<void(OTAState, float, uint8_t, OTAComponent *)> &&callback) { + this->state_callback_.add(std::move(callback)); + } + + protected: + CallbackManager<void(OTAState, float, uint8_t, OTAComponent *)> state_callback_{}; +}; + +extern OTAGlobalCallback *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +#endif std::unique_ptr<ota::OTABackend> make_ota_backend(); + } // namespace ota } // namespace esphome From d1ccee8f4350876754d32ffdc740eb390e35c74a Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Tue, 30 Apr 2024 01:34:56 -0500 Subject: [PATCH 18/26] Add newline --- esphome/components/ota/ota_backend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index bc7755fa83..16a5d56554 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -9,4 +9,4 @@ OTAGlobalCallback *global_ota_component = #endif } // namespace ota -} // namespace esphome \ No newline at end of file +} // namespace esphome From 68cb348a8b6eb61e36efe69e6cb9c5fcf95bd3c8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Tue, 30 Apr 2024 02:07:47 -0500 Subject: [PATCH 19/26] No walruses were harmed in the committing of this commit --- esphome/components/ota/ota_backend.cpp | 5 +++-- esphome/components/ota/ota_backend.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index 16a5d56554..bad3148e16 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,8 +4,9 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_component = - new OTAGlobalCallback; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +OTAGlobalCallback *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + +OTAGlobalCallback::OTAGlobalCallback() { global_ota_component = this; } #endif } // namespace ota diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index f72a63d0e4..271d709bde 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -68,6 +68,7 @@ class OTAComponent : public Component { #ifdef USE_OTA_STATE_CALLBACK class OTAGlobalCallback { public: + OTAGlobalCallback(); void register_ota(OTAComponent *ota_caller) { ota_caller->add_on_state_callback([this, ota_caller](OTAState state, float progress, uint8_t error) { this->state_callback_.call(state, progress, error, ota_caller); From f82ca372a0a4bc4cef6e7fa705c3c639362ef315 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Tue, 30 Apr 2024 02:11:42 -0500 Subject: [PATCH 20/26] Revert "No walruses were harmed in the committing of this commit" This reverts commit 68cb348a8b6eb61e36efe69e6cb9c5fcf95bd3c8. --- esphome/components/ota/ota_backend.cpp | 5 ++--- esphome/components/ota/ota_backend.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index bad3148e16..16a5d56554 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,9 +4,8 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_component = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) - -OTAGlobalCallback::OTAGlobalCallback() { global_ota_component = this; } +OTAGlobalCallback *global_ota_component = + new OTAGlobalCallback; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #endif } // namespace ota diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index 271d709bde..f72a63d0e4 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -68,7 +68,6 @@ class OTAComponent : public Component { #ifdef USE_OTA_STATE_CALLBACK class OTAGlobalCallback { public: - OTAGlobalCallback(); void register_ota(OTAComponent *ota_caller) { ota_caller->add_on_state_callback([this, ota_caller](OTAState state, float progress, uint8_t error) { this->state_callback_.call(state, progress, error, ota_caller); From 437b2540e9a784b80933638e75eee99df0bf0ca9 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Tue, 30 Apr 2024 02:13:10 -0500 Subject: [PATCH 21/26] Update nolint --- esphome/components/ota/ota_backend.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index 16a5d56554..cb026b033b 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,8 +4,7 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_component = - new OTAGlobalCallback; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +OTAGlobalCallback *global_ota_component = new OTAGlobalCallback; // NOLINT(cppcoreguidelines-owning-memory) #endif } // namespace ota From a7ff9abdae0c231d0c94c8f77496a6d02dd353bd Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Tue, 30 Apr 2024 02:39:00 -0500 Subject: [PATCH 22/26] Update nolint take 2 --- esphome/components/ota/ota_backend.cpp | 2 +- esphome/components/ota/ota_backend.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index cb026b033b..2f2fec2020 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_component = new OTAGlobalCallback; // NOLINT(cppcoreguidelines-owning-memory) +OTAGlobalCallback *global_ota_component = new OTAGlobalCallback; // NOLINT #endif } // namespace ota diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index f72a63d0e4..b76883df74 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -81,7 +81,7 @@ class OTAGlobalCallback { CallbackManager<void(OTAState, float, uint8_t, OTAComponent *)> state_callback_{}; }; -extern OTAGlobalCallback *global_ota_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +extern OTAGlobalCallback *global_ota_component; // NOLINT #endif std::unique_ptr<ota::OTABackend> make_ota_backend(); From 3c70762369ab83ade5d963c80eccd5796b98edd8 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Wed, 1 May 2024 16:00:36 -0500 Subject: [PATCH 23/26] A little more code consolidation --- esphome/components/esphome/ota/__init__.py | 39 +++------------------- esphome/components/ota/__init__.py | 33 ++++++++++++++++++ esphome/components/ota/ota_backend.cpp | 2 +- 3 files changed, 38 insertions(+), 36 deletions(-) diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index e6f99ba18a..9dbf39662e 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -1,8 +1,7 @@ from esphome.cpp_generator import RawExpression import esphome.codegen as cg import esphome.config_validation as cv -from esphome import automation -from esphome.components import ota +from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code, OTAComponent from esphome.const import ( CONF_ID, CONF_NUM_ATTEMPTS, @@ -11,7 +10,6 @@ from esphome.const import ( CONF_PORT, CONF_REBOOT_TIMEOUT, CONF_SAFE_MODE, - CONF_TRIGGER_ID, CONF_VERSION, KEY_PAST_SAFE_MODE, ) @@ -23,7 +21,7 @@ AUTO_LOAD = ["md5", "socket"] DEPENDENCIES = ["network"] esphome = cg.esphome_ns.namespace("esphome") -ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", ota.OTAComponent) +ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", OTAComponent) CONFIG_SCHEMA = ( @@ -47,7 +45,7 @@ CONFIG_SCHEMA = ( cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int, } ) - .extend(ota.BASE_OTA_SCHEMA) + .extend(BASE_OTA_SCHEMA) .extend(cv.COMPONENT_SCHEMA) ) @@ -57,6 +55,7 @@ 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])) cg.add_define("USE_OTA") if CONF_PASSWORD in config: @@ -72,33 +71,3 @@ async def to_code(config): ) cg.add(RawExpression(f"if ({condition}) return")) CORE.data[CONF_OTA][KEY_PAST_SAFE_MODE] = True - - if CORE.is_esp32 and CORE.using_arduino: - cg.add_library("Update", None) - - if CORE.is_rp2040 and CORE.using_arduino: - cg.add_library("Updater", None) - - use_state_callback = False - for conf in config.get(ota.CONF_ON_STATE_CHANGE, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(ota.OTAState, "state")], conf) - use_state_callback = True - for conf in config.get(ota.CONF_ON_BEGIN, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) - use_state_callback = True - for conf in config.get(ota.CONF_ON_PROGRESS, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(float, "x")], conf) - use_state_callback = True - for conf in config.get(ota.CONF_ON_END, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [], conf) - use_state_callback = True - for conf in config.get(ota.CONF_ON_ERROR, []): - trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) - await automation.build_automation(trigger, [(cg.uint8, "x")], conf) - use_state_callback = True - if use_state_callback: - cg.add_define("USE_OTA_STATE_CALLBACK") diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 94ba199788..5a5c69bd7a 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,6 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome import automation +from esphome.core import CORE from esphome.const import CONF_ESPHOME, CONF_OTA, CONF_PLATFORM, CONF_TRIGGER_ID @@ -66,3 +67,35 @@ BASE_OTA_SCHEMA = cv.Schema( ), } ) + + +async def ota_to_code(var, config): + if CORE.is_esp32 and CORE.using_arduino: + cg.add_library("Update", None) + + if CORE.is_rp2040 and CORE.using_arduino: + cg.add_library("Updater", None) + + use_state_callback = False + for conf in config.get(CONF_ON_STATE_CHANGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(OTAState, "state")], conf) + use_state_callback = True + for conf in config.get(CONF_ON_BEGIN, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_PROGRESS, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(float, "x")], conf) + use_state_callback = True + for conf in config.get(CONF_ON_END, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_ERROR, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(cg.uint8, "x")], conf) + use_state_callback = True + if use_state_callback: + cg.add_define("USE_OTA_STATE_CALLBACK") diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index 2f2fec2020..32b9c19164 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_component = new OTAGlobalCallback; // NOLINT +OTAGlobalCallback *global_ota_component = new OTAGlobalCallback(); // NOLINT #endif } // namespace ota From 75d5ba09e786a9f919b20a5aee87d8dc958828ec Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Thu, 2 May 2024 04:09:27 -0500 Subject: [PATCH 24/26] Fix variable name --- esphome/components/ota/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 5a5c69bd7a..e9e63fbff0 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -17,14 +17,14 @@ CONF_ON_PROGRESS = "on_progress" CONF_ON_STATE_CHANGE = "on_state_change" -ota = cg.esphome_ns.namespace("ota") -OTAComponent = ota.class_("OTAComponent", cg.Component) -OTAState = ota.enum("OTAState") -OTAEndTrigger = ota.class_("OTAEndTrigger", automation.Trigger.template()) -OTAErrorTrigger = ota.class_("OTAErrorTrigger", automation.Trigger.template()) -OTAProgressTrigger = ota.class_("OTAProgressTrigger", automation.Trigger.template()) -OTAStartTrigger = ota.class_("OTAStartTrigger", automation.Trigger.template()) -OTAStateChangeTrigger = ota.class_( +ota_ns = cg.esphome_ns.namespace("ota") +OTAComponent = ota_ns.class_("OTAComponent", cg.Component) +OTAState = ota_ns.enum("OTAState") +OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) +OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) +OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.template()) +OTAStartTrigger = ota_ns.class_("OTAStartTrigger", automation.Trigger.template()) +OTAStateChangeTrigger = ota_ns.class_( "OTAStateChangeTrigger", automation.Trigger.template() ) From 8dd03a3dac480e8eda0c8d8e31d1bf62ae23efae Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Mon, 6 May 2024 19:04:52 -0500 Subject: [PATCH 25/26] Manage global pointer better --- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 2 +- esphome/components/esphome/ota/ota_esphome.cpp | 2 +- esphome/components/ota/ota_backend.cpp | 11 ++++++++++- esphome/components/ota/ota_backend.h | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 6b2a1caa13..82855b2038 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -58,7 +58,7 @@ void ESP32BLETracker::setup() { this->scanner_idle_ = true; #ifdef USE_OTA - ota::global_ota_component->add_on_state_callback( + ota::get_global_ota_callback()->add_on_state_callback( [this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) { if (state == ota::OTA_STARTED) { this->stop_scan(); diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 26bc6b82f9..9ce4c8fe27 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -23,7 +23,7 @@ static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; void ESPHomeOTAComponent::setup() { #ifdef USE_OTA_STATE_CALLBACK - ota::global_ota_component->register_ota(this); + ota::register_ota_platform(this); #endif server_ = socket::socket_ip(SOCK_STREAM, 0); diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index 32b9c19164..5e459aa034 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,7 +4,16 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_component = new OTAGlobalCallback(); // NOLINT +OTAGlobalCallback *global_ota_callback{nullptr}; + +OTAGlobalCallback *get_global_ota_callback() { + if (global_ota_callback == nullptr) { + global_ota_callback = new OTAGlobalCallback(); + } + return global_ota_callback; +} + +void register_ota_platform(OTAComponent *ota_caller) { get_global_ota_callback()->register_ota(ota_caller); } #endif } // namespace ota diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota/ota_backend.h index b76883df74..2537cdb67a 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota/ota_backend.h @@ -81,7 +81,8 @@ class OTAGlobalCallback { CallbackManager<void(OTAState, float, uint8_t, OTAComponent *)> state_callback_{}; }; -extern OTAGlobalCallback *global_ota_component; // NOLINT +OTAGlobalCallback *get_global_ota_callback(); +void register_ota_platform(OTAComponent *ota_caller); #endif std::unique_ptr<ota::OTABackend> make_ota_backend(); From 6d335ceeddd69da6163b2f31c7967f0cacd1d4b4 Mon Sep 17 00:00:00 2001 From: Keith Burzinski <kbx81x@gmail.com> Date: Mon, 6 May 2024 19:35:42 -0500 Subject: [PATCH 26/26] Put back a couple nolints --- esphome/components/ota/ota_backend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota_backend.cpp index 5e459aa034..30de4ec4b3 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota_backend.cpp @@ -4,11 +4,11 @@ namespace esphome { namespace ota { #ifdef USE_OTA_STATE_CALLBACK -OTAGlobalCallback *global_ota_callback{nullptr}; +OTAGlobalCallback *global_ota_callback{nullptr}; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) OTAGlobalCallback *get_global_ota_callback() { if (global_ota_callback == nullptr) { - global_ota_callback = new OTAGlobalCallback(); + global_ota_callback = new OTAGlobalCallback(); // NOLINT(cppcoreguidelines-owning-memory) } return global_ota_callback; }