From 9f51546023cbce8f581bbd80cf0ea6e42bc267e8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 10:33:43 -0500 Subject: [PATCH 01/31] Extract OTA backend functionality into separate ota_base component --- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 2 +- .../components/esphome/ota/ota_esphome.cpp | 15 +++--- esphome/components/esphome/ota/ota_esphome.h | 2 +- .../http_request/ota/ota_http_request.cpp | 13 ++--- .../http_request/ota/ota_http_request.h | 2 +- .../micro_wake_word/micro_wake_word.cpp | 2 +- esphome/components/ota/__init__.py | 10 +--- esphome/components/ota/automation.h | 2 +- .../ota/{ota_backend.cpp => ota.cpp} | 2 +- esphome/components/ota/ota.h | 52 +++++++++++++++++++ esphome/components/ota_base/__init__.py | 16 ++++++ esphome/components/ota_base/ota_backend.cpp | 9 ++++ .../{ota => ota_base}/ota_backend.h | 44 ++-------------- .../ota_backend_arduino_esp32.cpp | 6 +-- .../ota_backend_arduino_esp32.h | 4 +- .../ota_backend_arduino_esp8266.cpp | 6 +-- .../ota_backend_arduino_esp8266.h | 4 +- .../ota_backend_arduino_libretiny.cpp | 6 +-- .../ota_backend_arduino_libretiny.h | 4 +- .../ota_backend_arduino_rp2040.cpp | 6 +-- .../ota_backend_arduino_rp2040.h | 4 +- .../{ota => ota_base}/ota_backend_esp_idf.cpp | 6 +-- .../{ota => ota_base}/ota_backend_esp_idf.h | 4 +- .../media_player/speaker_media_player.cpp | 2 +- 24 files changed, 130 insertions(+), 93 deletions(-) rename esphome/components/ota/{ota_backend.cpp => ota.cpp} (95%) create mode 100644 esphome/components/ota/ota.h create mode 100644 esphome/components/ota_base/__init__.py create mode 100644 esphome/components/ota_base/ota_backend.cpp rename esphome/components/{ota => ota_base}/ota_backend.h (56%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_esp32.cpp (90%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_esp32.h (92%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_esp8266.cpp (92%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_esp8266.h (93%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_libretiny.cpp (90%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_libretiny.h (91%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_rp2040.cpp (92%) rename esphome/components/{ota => ota_base}/ota_backend_arduino_rp2040.h (92%) rename esphome/components/{ota => ota_base}/ota_backend_esp_idf.cpp (95%) rename esphome/components/{ota => ota_base}/ota_backend_esp_idf.h (93%) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index d950ccb5f1..290b2ead08 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 #ifdef USE_OTA -#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/ota/ota.h" #endif #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 4cc82b9094..5c662bbcfc 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -2,12 +2,13 @@ #ifdef USE_OTA #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/components/ota/ota.h" // For OTAComponent and callbacks +#include "esphome/components/ota_base/ota_backend.h" // For OTABackend class +#include "esphome/components/ota_base/ota_backend_arduino_esp32.h" +#include "esphome/components/ota_base/ota_backend_arduino_esp8266.h" +#include "esphome/components/ota_base/ota_backend_arduino_libretiny.h" +#include "esphome/components/ota_base/ota_backend_arduino_rp2040.h" +#include "esphome/components/ota_base/ota_backend_esp_idf.h" #include "esphome/core/application.h" #include "esphome/core/hal.h" #include "esphome/core/log.h" @@ -149,7 +150,7 @@ void ESPHomeOTAComponent::handle_() { buf[1] = USE_OTA_VERSION; this->writeall_(buf, 2); - backend = ota::make_ota_backend(); + backend = ota_base::make_ota_backend(); // Read features - 1 byte if (!this->readall_(buf, 1)) { diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index e0d09ff37e..ce5f2a59b9 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -4,7 +4,7 @@ #ifdef USE_OTA #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" -#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/ota/ota.h" #include "esphome/components/socket/socket.h" namespace esphome { diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index 4d9e868c74..ce7c5a6b88 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -6,11 +6,12 @@ #include "esphome/components/md5/md5.h" #include "esphome/components/watchdog/watchdog.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_rp2040.h" -#include "esphome/components/ota/ota_backend_esp_idf.h" +#include "esphome/components/ota/ota.h" // For OTAComponent and callbacks +#include "esphome/components/ota_base/ota_backend.h" // For OTABackend class +#include "esphome/components/ota_base/ota_backend_arduino_esp32.h" +#include "esphome/components/ota_base/ota_backend_arduino_esp8266.h" +#include "esphome/components/ota_base/ota_backend_arduino_rp2040.h" +#include "esphome/components/ota_base/ota_backend_esp_idf.h" namespace esphome { namespace http_request { @@ -115,7 +116,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { ESP_LOGV(TAG, "MD5Digest initialized"); ESP_LOGV(TAG, "OTA backend begin"); - auto backend = ota::make_ota_backend(); + auto backend = ota_base::make_ota_backend(); auto error_code = backend->begin(container->content_length); if (error_code != ota::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "backend->begin error: %d", error_code); diff --git a/esphome/components/http_request/ota/ota_http_request.h b/esphome/components/http_request/ota/ota_http_request.h index 6a86b4ab43..20a7abba71 100644 --- a/esphome/components/http_request/ota/ota_http_request.h +++ b/esphome/components/http_request/ota/ota_http_request.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/ota/ota.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 201d956a37..5b5d92aa59 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -9,7 +9,7 @@ #include "esphome/components/audio/audio_transfer_buffer.h" #ifdef USE_OTA -#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/ota/ota.h" #endif namespace esphome { diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 627c55e910..e990256969 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -8,10 +8,10 @@ from esphome.const import ( CONF_PLATFORM, CONF_TRIGGER_ID, ) -from esphome.core import CORE, coroutine_with_priority +from esphome.core import coroutine_with_priority CODEOWNERS = ["@esphome/core"] -AUTO_LOAD = ["md5", "safe_mode"] +AUTO_LOAD = ["safe_mode", "ota_base"] IS_PLATFORM_COMPONENT = True @@ -84,12 +84,6 @@ BASE_OTA_SCHEMA = cv.Schema( async def to_code(config): cg.add_define("USE_OTA") - 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) - async def ota_to_code(var, config): await cg.past_safe_mode() diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h index 7e1a60f3ce..c3ff8e33d7 100644 --- a/esphome/components/ota/automation.h +++ b/esphome/components/ota/automation.h @@ -1,6 +1,6 @@ #pragma once #ifdef USE_OTA_STATE_CALLBACK -#include "ota_backend.h" +#include "ota.h" #include "esphome/core/automation.h" diff --git a/esphome/components/ota/ota_backend.cpp b/esphome/components/ota/ota.cpp similarity index 95% rename from esphome/components/ota/ota_backend.cpp rename to esphome/components/ota/ota.cpp index 30de4ec4b3..a98170ab1d 100644 --- a/esphome/components/ota/ota_backend.cpp +++ b/esphome/components/ota/ota.cpp @@ -1,4 +1,4 @@ -#include "ota_backend.h" +#include "ota.h" namespace esphome { namespace ota { diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h new file mode 100644 index 0000000000..99bb3a61f8 --- /dev/null +++ b/esphome/components/ota/ota.h @@ -0,0 +1,52 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/defines.h" +#include "esphome/components/ota_base/ota_backend.h" + +#ifdef USE_OTA_STATE_CALLBACK +#include "esphome/core/automation.h" +#endif + +namespace esphome { +namespace ota { + +// Import types from ota_base namespace for backward compatibility +using ota_base::OTABackend; +using ota_base::OTAResponseTypes; +using ota_base::OTAState; + +class OTAComponent : public Component { +#ifdef USE_OTA_STATE_CALLBACK + public: + void add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); + } + + protected: + CallbackManager state_callback_{}; +#endif +}; + +#ifdef USE_OTA_STATE_CALLBACK +class OTAGlobalCallback { + public: + void register_ota(OTAComponent *ota_caller) { + ota_caller->add_on_state_callback([this, ota_caller](ota_base::OTAState state, float progress, uint8_t error) { + this->state_callback_.call(state, progress, error, ota_caller); + }); + } + void add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); + } + + protected: + CallbackManager state_callback_{}; +}; + +OTAGlobalCallback *get_global_ota_callback(); +void register_ota_platform(OTAComponent *ota_caller); +#endif + +} // namespace ota +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ota_base/__init__.py b/esphome/components/ota_base/__init__.py new file mode 100644 index 0000000000..1a562b3d2f --- /dev/null +++ b/esphome/components/ota_base/__init__.py @@ -0,0 +1,16 @@ +import esphome.codegen as cg +from esphome.core import CORE, coroutine_with_priority + +CODEOWNERS = ["@esphome/core"] +AUTO_LOAD = ["md5"] + +ota_base_ns = cg.esphome_ns.namespace("ota_base") + + +@coroutine_with_priority(52.0) +async def to_code(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) diff --git a/esphome/components/ota_base/ota_backend.cpp b/esphome/components/ota_base/ota_backend.cpp new file mode 100644 index 0000000000..d43974e37f --- /dev/null +++ b/esphome/components/ota_base/ota_backend.cpp @@ -0,0 +1,9 @@ +#include "ota_backend.h" + +namespace esphome { +namespace ota_base { + +// The make_ota_backend() implementation is provided by each platform-specific backend + +} // namespace ota_base +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ota/ota_backend.h b/esphome/components/ota_base/ota_backend.h similarity index 56% rename from esphome/components/ota/ota_backend.h rename to esphome/components/ota_base/ota_backend.h index bc8ab46643..3112245c88 100644 --- a/esphome/components/ota/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -1,15 +1,10 @@ #pragma once -#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 { +namespace ota_base { enum OTAResponseTypes { OTA_RESPONSE_OK = 0x00, @@ -59,38 +54,7 @@ class OTABackend { virtual bool supports_compression() = 0; }; -class OTAComponent : public Component { -#ifdef USE_OTA_STATE_CALLBACK - public: - void add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); - } +std::unique_ptr make_ota_backend(); - protected: - CallbackManager state_callback_{}; -#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 &&callback) { - this->state_callback_.add(std::move(callback)); - } - - protected: - CallbackManager state_callback_{}; -}; - -OTAGlobalCallback *get_global_ota_callback(); -void register_ota_platform(OTAComponent *ota_caller); -#endif -std::unique_ptr make_ota_backend(); - -} // namespace ota -} // namespace esphome +} // namespace ota_base +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ota/ota_backend_arduino_esp32.cpp b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp similarity index 90% rename from esphome/components/ota/ota_backend_arduino_esp32.cpp rename to esphome/components/ota_base/ota_backend_arduino_esp32.cpp index 15dfc98a6c..34ba3ae6ff 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp @@ -8,11 +8,11 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_esp32"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); @@ -56,7 +56,7 @@ OTAResponseTypes ArduinoESP32OTABackend::end() { void ArduinoESP32OTABackend::abort() { Update.abort(); } -} // namespace ota +} // namespace ota_base } // namespace esphome #endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/ota/ota_backend_arduino_esp32.h b/esphome/components/ota_base/ota_backend_arduino_esp32.h similarity index 92% rename from esphome/components/ota/ota_backend_arduino_esp32.h rename to esphome/components/ota_base/ota_backend_arduino_esp32.h index ac7fe9f14f..6fb9454c64 100644 --- a/esphome/components/ota/ota_backend_arduino_esp32.h +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.h @@ -6,7 +6,7 @@ #include "esphome/core/helpers.h" namespace esphome { -namespace ota { +namespace ota_base { class ArduinoESP32OTABackend : public OTABackend { public: @@ -18,7 +18,7 @@ class ArduinoESP32OTABackend : public OTABackend { bool supports_compression() override { return false; } }; -} // namespace ota +} // namespace ota_base } // namespace esphome #endif // USE_ESP32_FRAMEWORK_ARDUINO diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.cpp b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp similarity index 92% rename from esphome/components/ota/ota_backend_arduino_esp8266.cpp rename to esphome/components/ota_base/ota_backend_arduino_esp8266.cpp index 42edbf5d2b..38d0ad96c3 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp @@ -10,11 +10,11 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_esp8266"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); @@ -68,7 +68,7 @@ void ArduinoESP8266OTABackend::abort() { esp8266::preferences_prevent_write(false); } -} // namespace ota +} // namespace ota_base } // namespace esphome #endif diff --git a/esphome/components/ota/ota_backend_arduino_esp8266.h b/esphome/components/ota_base/ota_backend_arduino_esp8266.h similarity index 93% rename from esphome/components/ota/ota_backend_arduino_esp8266.h rename to esphome/components/ota_base/ota_backend_arduino_esp8266.h index 7f44d7c965..3f9982a514 100644 --- a/esphome/components/ota/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.h @@ -7,7 +7,7 @@ #include "esphome/core/macros.h" namespace esphome { -namespace ota { +namespace ota_base { class ArduinoESP8266OTABackend : public OTABackend { public: @@ -23,7 +23,7 @@ class ArduinoESP8266OTABackend : public OTABackend { #endif }; -} // namespace ota +} // namespace ota_base } // namespace esphome #endif diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.cpp b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp similarity index 90% rename from esphome/components/ota/ota_backend_arduino_libretiny.cpp rename to esphome/components/ota_base/ota_backend_arduino_libretiny.cpp index 6b2cf80684..12d4b677a3 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp @@ -8,11 +8,11 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_libretiny"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); @@ -56,7 +56,7 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::end() { void ArduinoLibreTinyOTABackend::abort() { Update.abort(); } -} // namespace ota +} // namespace ota_base } // namespace esphome #endif // USE_LIBRETINY diff --git a/esphome/components/ota/ota_backend_arduino_libretiny.h b/esphome/components/ota_base/ota_backend_arduino_libretiny.h similarity index 91% rename from esphome/components/ota/ota_backend_arduino_libretiny.h rename to esphome/components/ota_base/ota_backend_arduino_libretiny.h index 11deb6e2f2..b1cf1df738 100644 --- a/esphome/components/ota/ota_backend_arduino_libretiny.h +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.h @@ -5,7 +5,7 @@ #include "esphome/core/defines.h" namespace esphome { -namespace ota { +namespace ota_base { class ArduinoLibreTinyOTABackend : public OTABackend { public: @@ -17,7 +17,7 @@ class ArduinoLibreTinyOTABackend : public OTABackend { bool supports_compression() override { return false; } }; -} // namespace ota +} // namespace ota_base } // namespace esphome #endif // USE_LIBRETINY diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.cpp b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp similarity index 92% rename from esphome/components/ota/ota_backend_arduino_rp2040.cpp rename to esphome/components/ota_base/ota_backend_arduino_rp2040.cpp index ffeab2e93f..7276381919 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp @@ -10,11 +10,11 @@ #include namespace esphome { -namespace ota { +namespace ota_base { static const char *const TAG = "ota.arduino_rp2040"; -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { bool ret = Update.begin(image_size, U_FLASH); @@ -68,7 +68,7 @@ void ArduinoRP2040OTABackend::abort() { rp2040::preferences_prevent_write(false); } -} // namespace ota +} // namespace ota_base } // namespace esphome #endif // USE_RP2040 diff --git a/esphome/components/ota/ota_backend_arduino_rp2040.h b/esphome/components/ota_base/ota_backend_arduino_rp2040.h similarity index 92% rename from esphome/components/ota/ota_backend_arduino_rp2040.h rename to esphome/components/ota_base/ota_backend_arduino_rp2040.h index b189964ab3..fb6e90bb53 100644 --- a/esphome/components/ota/ota_backend_arduino_rp2040.h +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.h @@ -7,7 +7,7 @@ #include "esphome/core/macros.h" namespace esphome { -namespace ota { +namespace ota_base { class ArduinoRP2040OTABackend : public OTABackend { public: @@ -19,7 +19,7 @@ class ArduinoRP2040OTABackend : public OTABackend { bool supports_compression() override { return false; } }; -} // namespace ota +} // namespace ota_base } // namespace esphome #endif // USE_RP2040 diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota_base/ota_backend_esp_idf.cpp similarity index 95% rename from esphome/components/ota/ota_backend_esp_idf.cpp rename to esphome/components/ota_base/ota_backend_esp_idf.cpp index 6f45fb75e4..eef4cb8026 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota_base/ota_backend_esp_idf.cpp @@ -12,9 +12,9 @@ #endif namespace esphome { -namespace ota { +namespace ota_base { -std::unique_ptr make_ota_backend() { return make_unique(); } +std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes IDFOTABackend::begin(size_t image_size) { this->partition_ = esp_ota_get_next_update_partition(nullptr); @@ -111,6 +111,6 @@ void IDFOTABackend::abort() { this->update_handle_ = 0; } -} // namespace ota +} // namespace ota_base } // namespace esphome #endif diff --git a/esphome/components/ota/ota_backend_esp_idf.h b/esphome/components/ota_base/ota_backend_esp_idf.h similarity index 93% rename from esphome/components/ota/ota_backend_esp_idf.h rename to esphome/components/ota_base/ota_backend_esp_idf.h index ed66d9b970..a7e34cb5ae 100644 --- a/esphome/components/ota/ota_backend_esp_idf.h +++ b/esphome/components/ota_base/ota_backend_esp_idf.h @@ -8,7 +8,7 @@ #include namespace esphome { -namespace ota { +namespace ota_base { class IDFOTABackend : public OTABackend { public: @@ -26,6 +26,6 @@ class IDFOTABackend : public OTABackend { char expected_bin_md5_[32]; }; -} // namespace ota +} // namespace ota_base } // namespace esphome #endif diff --git a/esphome/components/speaker/media_player/speaker_media_player.cpp b/esphome/components/speaker/media_player/speaker_media_player.cpp index 2c30f17c78..2cebebd523 100644 --- a/esphome/components/speaker/media_player/speaker_media_player.cpp +++ b/esphome/components/speaker/media_player/speaker_media_player.cpp @@ -6,7 +6,7 @@ #include "esphome/components/audio/audio.h" #ifdef USE_OTA -#include "esphome/components/ota/ota_backend.h" +#include "esphome/components/ota/ota.h" #endif namespace esphome { From 47ad206ccd6957d6862f1781b98316f25831c644 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 10:35:19 -0500 Subject: [PATCH 02/31] Extract OTA backend functionality into separate ota_base component --- esphome/components/ota/ota.h | 2 +- esphome/components/ota_base/ota_backend.cpp | 2 +- esphome/components/ota_base/ota_backend.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h index 99bb3a61f8..c089cae006 100644 --- a/esphome/components/ota/ota.h +++ b/esphome/components/ota/ota.h @@ -49,4 +49,4 @@ void register_ota_platform(OTAComponent *ota_caller); #endif } // namespace ota -} // namespace esphome \ No newline at end of file +} // namespace esphome diff --git a/esphome/components/ota_base/ota_backend.cpp b/esphome/components/ota_base/ota_backend.cpp index d43974e37f..a2b2575f41 100644 --- a/esphome/components/ota_base/ota_backend.cpp +++ b/esphome/components/ota_base/ota_backend.cpp @@ -6,4 +6,4 @@ namespace ota_base { // The make_ota_backend() implementation is provided by each platform-specific backend } // namespace ota_base -} // namespace esphome \ No newline at end of file +} // namespace esphome diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index 3112245c88..b028f3605f 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -57,4 +57,4 @@ class OTABackend { std::unique_ptr make_ota_backend(); } // namespace ota_base -} // namespace esphome \ No newline at end of file +} // namespace esphome From 902f08c1bc2088cdfa1499fa9fd58ed800e5755d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 10:38:31 -0500 Subject: [PATCH 03/31] Extract OTA backend functionality into separate ota_base component --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 68c8684024..bcf2b7aca5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -327,6 +327,7 @@ esphome/components/opentherm/* @olegtarasov esphome/components/openthread/* @mrene esphome/components/opt3001/* @ccutrer esphome/components/ota/* @esphome/core +esphome/components/ota_base/* @esphome/core esphome/components/output/* @esphome/core esphome/components/packet_transport/* @clydebarrow esphome/components/pca6416a/* @Mat931 From 36350f179eeb7625d390284b5c09701ed62a0dee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 10:49:59 -0500 Subject: [PATCH 04/31] split --- .../esp32_ble_tracker/esp32_ble_tracker.cpp | 8 +-- .../components/esphome/ota/ota_esphome.cpp | 2 +- .../http_request/ota/ota_http_request.cpp | 2 +- .../micro_wake_word/micro_wake_word.cpp | 10 ++-- esphome/components/ota/ota.cpp | 16 ++---- esphome/components/ota/ota.h | 53 ++++++++----------- esphome/components/ota_base/__init__.py | 5 ++ esphome/components/ota_base/ota_backend.cpp | 13 +++++ esphome/components/ota_base/ota_backend.h | 37 +++++++++++++ .../media_player/speaker_media_player.cpp | 10 ++-- 10 files changed, 95 insertions(+), 61 deletions(-) diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 290b2ead08..8e785da4be 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 #ifdef USE_OTA -#include "esphome/components/ota/ota.h" +#include "esphome/components/ota_base/ota_backend.h" #endif #ifdef USE_ESP32_BLE_SOFTWARE_COEXISTENCE @@ -61,9 +61,9 @@ void ESP32BLETracker::setup() { global_esp32_ble_tracker = this; #ifdef USE_OTA - 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) { + ota_base::get_global_ota_callback()->add_on_state_callback( + [this](ota_base::OTAState state, float progress, uint8_t error, ota_base::OTAComponent *comp) { + if (state == ota_base::OTA_STARTED) { this->stop_scan(); for (auto *client : this->clients_) { client->disconnect(); diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index 5c662bbcfc..cfa8364059 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -24,7 +24,7 @@ static constexpr u_int16_t OTA_BLOCK_SIZE = 8192; void ESPHomeOTAComponent::setup() { #ifdef USE_OTA_STATE_CALLBACK - ota::register_ota_platform(this); + ota_base::register_ota_platform(this); #endif this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index ce7c5a6b88..57e65e6c03 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -20,7 +20,7 @@ static const char *const TAG = "http_request.ota"; void OtaHttpRequestComponent::setup() { #ifdef USE_OTA_STATE_CALLBACK - ota::register_ota_platform(this); + ota_base::register_ota_platform(this); #endif } diff --git a/esphome/components/micro_wake_word/micro_wake_word.cpp b/esphome/components/micro_wake_word/micro_wake_word.cpp index 5b5d92aa59..583a4b2fe2 100644 --- a/esphome/components/micro_wake_word/micro_wake_word.cpp +++ b/esphome/components/micro_wake_word/micro_wake_word.cpp @@ -9,7 +9,7 @@ #include "esphome/components/audio/audio_transfer_buffer.h" #ifdef USE_OTA -#include "esphome/components/ota/ota.h" +#include "esphome/components/ota_base/ota_backend.h" #endif namespace esphome { @@ -121,11 +121,11 @@ void MicroWakeWord::setup() { }); #ifdef USE_OTA - 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) { + ota_base::get_global_ota_callback()->add_on_state_callback( + [this](ota_base::OTAState state, float progress, uint8_t error, ota_base::OTAComponent *comp) { + if (state == ota_base::OTA_STARTED) { this->suspend_task_(); - } else if (state == ota::OTA_ERROR) { + } else if (state == ota_base::OTA_ERROR) { this->resume_task_(); } }); diff --git a/esphome/components/ota/ota.cpp b/esphome/components/ota/ota.cpp index a98170ab1d..921f3769b9 100644 --- a/esphome/components/ota/ota.cpp +++ b/esphome/components/ota/ota.cpp @@ -3,18 +3,8 @@ namespace esphome { namespace ota { -#ifdef USE_OTA_STATE_CALLBACK -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(); // NOLINT(cppcoreguidelines-owning-memory) - } - return global_ota_callback; -} - -void register_ota_platform(OTAComponent *ota_caller) { get_global_ota_callback()->register_ota(ota_caller); } -#endif +// All functionality has been moved to ota_base +// This file remains for backward compatibility } // namespace ota -} // namespace esphome +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h index c089cae006..654e87a173 100644 --- a/esphome/components/ota/ota.h +++ b/esphome/components/ota/ota.h @@ -1,52 +1,41 @@ #pragma once -#include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/components/ota_base/ota_backend.h" -#ifdef USE_OTA_STATE_CALLBACK -#include "esphome/core/automation.h" -#endif - namespace esphome { namespace ota { // Import types from ota_base namespace for backward compatibility using ota_base::OTABackend; +using ota_base::OTAComponent; using ota_base::OTAResponseTypes; using ota_base::OTAState; -class OTAComponent : public Component { -#ifdef USE_OTA_STATE_CALLBACK - public: - void add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); - } - - protected: - CallbackManager state_callback_{}; -#endif -}; +// Re-export specific enum values for backward compatibility +// (in case external components use ota::OTA_STARTED, etc.) +static constexpr auto OTA_COMPLETED = ota_base::OTA_COMPLETED; +static constexpr auto OTA_STARTED = ota_base::OTA_STARTED; +static constexpr auto OTA_IN_PROGRESS = ota_base::OTA_IN_PROGRESS; +static constexpr auto OTA_ABORT = ota_base::OTA_ABORT; +static constexpr auto OTA_ERROR = ota_base::OTA_ERROR; #ifdef USE_OTA_STATE_CALLBACK -class OTAGlobalCallback { - public: - void register_ota(OTAComponent *ota_caller) { - ota_caller->add_on_state_callback([this, ota_caller](ota_base::OTAState state, float progress, uint8_t error) { - this->state_callback_.call(state, progress, error, ota_caller); - }); - } - void add_on_state_callback(std::function &&callback) { - this->state_callback_.add(std::move(callback)); - } +using ota_base::OTAGlobalCallback; - protected: - CallbackManager state_callback_{}; -}; +// Deprecated: Use ota_base::get_global_ota_callback() instead +// Will be removed after 2025-12-30 (6 months from 2025-06-30) +[[deprecated("Use ota_base::get_global_ota_callback() instead")]] inline OTAGlobalCallback *get_global_ota_callback() { + return ota_base::get_global_ota_callback(); +} -OTAGlobalCallback *get_global_ota_callback(); -void register_ota_platform(OTAComponent *ota_caller); +// Deprecated: Use ota_base::register_ota_platform() instead +// Will be removed after 2025-12-30 (6 months from 2025-06-30) +[[deprecated("Use ota_base::register_ota_platform() instead")]] inline void register_ota_platform( + OTAComponent *ota_caller) { + ota_base::register_ota_platform(ota_caller); +} #endif } // namespace ota -} // namespace esphome +} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ota_base/__init__.py b/esphome/components/ota_base/__init__.py index 1a562b3d2f..7a1f233d26 100644 --- a/esphome/components/ota_base/__init__.py +++ b/esphome/components/ota_base/__init__.py @@ -9,6 +9,11 @@ ota_base_ns = cg.esphome_ns.namespace("ota_base") @coroutine_with_priority(52.0) async def to_code(config): + # Note: USE_OTA_STATE_CALLBACK is not defined here + # Components that need OTA callbacks (like esp32_ble_tracker, speaker, etc.) + # define USE_OTA_STATE_CALLBACK themselves in their own __init__.py files + # This ensures the callback functionality is only compiled when actually needed + if CORE.is_esp32 and CORE.using_arduino: cg.add_library("Update", None) diff --git a/esphome/components/ota_base/ota_backend.cpp b/esphome/components/ota_base/ota_backend.cpp index a2b2575f41..7cbc795866 100644 --- a/esphome/components/ota_base/ota_backend.cpp +++ b/esphome/components/ota_base/ota_backend.cpp @@ -5,5 +5,18 @@ namespace ota_base { // The make_ota_backend() implementation is provided by each platform-specific backend +#ifdef USE_OTA_STATE_CALLBACK +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(); // NOLINT(cppcoreguidelines-owning-memory) + } + return global_ota_callback; +} + +void register_ota_platform(OTAComponent *ota_caller) { get_global_ota_callback()->register_ota(ota_caller); } +#endif + } // namespace ota_base } // namespace esphome diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index b028f3605f..8e2831a063 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -1,8 +1,13 @@ #pragma once +#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_base { @@ -56,5 +61,37 @@ class OTABackend { std::unique_ptr make_ota_backend(); +class OTAComponent : public Component { +#ifdef USE_OTA_STATE_CALLBACK + public: + void add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); + } + + protected: + CallbackManager state_callback_{}; +#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 &&callback) { + this->state_callback_.add(std::move(callback)); + } + + protected: + CallbackManager state_callback_{}; +}; + +OTAGlobalCallback *get_global_ota_callback(); +void register_ota_platform(OTAComponent *ota_caller); +#endif + } // namespace ota_base } // namespace esphome diff --git a/esphome/components/speaker/media_player/speaker_media_player.cpp b/esphome/components/speaker/media_player/speaker_media_player.cpp index 2cebebd523..c6f6c91760 100644 --- a/esphome/components/speaker/media_player/speaker_media_player.cpp +++ b/esphome/components/speaker/media_player/speaker_media_player.cpp @@ -6,7 +6,7 @@ #include "esphome/components/audio/audio.h" #ifdef USE_OTA -#include "esphome/components/ota/ota.h" +#include "esphome/components/ota_base/ota_backend.h" #endif namespace esphome { @@ -67,16 +67,16 @@ void SpeakerMediaPlayer::setup() { } #ifdef USE_OTA - 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) { + ota_base::get_global_ota_callback()->add_on_state_callback( + [this](ota_base::OTAState state, float progress, uint8_t error, ota_base::OTAComponent *comp) { + if (state == ota_base::OTA_STARTED) { if (this->media_pipeline_ != nullptr) { this->media_pipeline_->suspend_tasks(); } if (this->announcement_pipeline_ != nullptr) { this->announcement_pipeline_->suspend_tasks(); } - } else if (state == ota::OTA_ERROR) { + } else if (state == ota_base::OTA_ERROR) { if (this->media_pipeline_ != nullptr) { this->media_pipeline_->resume_tasks(); } From 088bea9ccd9b2f8d679e626b240f5407d4840021 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 10:50:26 -0500 Subject: [PATCH 05/31] split --- esphome/components/ota/ota.cpp | 2 +- esphome/components/ota/ota.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/ota/ota.cpp b/esphome/components/ota/ota.cpp index 921f3769b9..47fda17be8 100644 --- a/esphome/components/ota/ota.cpp +++ b/esphome/components/ota/ota.cpp @@ -7,4 +7,4 @@ namespace ota { // This file remains for backward compatibility } // namespace ota -} // namespace esphome \ No newline at end of file +} // namespace esphome diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h index 654e87a173..17d2d24d00 100644 --- a/esphome/components/ota/ota.h +++ b/esphome/components/ota/ota.h @@ -38,4 +38,4 @@ using ota_base::OTAGlobalCallback; #endif } // namespace ota -} // namespace esphome \ No newline at end of file +} // namespace esphome From 981177da2355557e9981293a8e7f8f49000abcbf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 12:09:07 -0500 Subject: [PATCH 06/31] todo --- esphome/components/ota/ota.h | 30 ++++++++++++++++++- esphome/components/ota_base/ota_backend.h | 8 +++++ .../web_server_base/web_server_base.h | 2 ++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h index 17d2d24d00..42a6cdbe85 100644 --- a/esphome/components/ota/ota.h +++ b/esphome/components/ota/ota.h @@ -13,13 +13,41 @@ using ota_base::OTAResponseTypes; using ota_base::OTAState; // Re-export specific enum values for backward compatibility -// (in case external components use ota::OTA_STARTED, etc.) +// OTAState values static constexpr auto OTA_COMPLETED = ota_base::OTA_COMPLETED; static constexpr auto OTA_STARTED = ota_base::OTA_STARTED; static constexpr auto OTA_IN_PROGRESS = ota_base::OTA_IN_PROGRESS; static constexpr auto OTA_ABORT = ota_base::OTA_ABORT; static constexpr auto OTA_ERROR = ota_base::OTA_ERROR; +// OTAResponseTypes values +static constexpr auto OTA_RESPONSE_OK = ota_base::OTA_RESPONSE_OK; +static constexpr auto OTA_RESPONSE_REQUEST_AUTH = ota_base::OTA_RESPONSE_REQUEST_AUTH; +static constexpr auto OTA_RESPONSE_HEADER_OK = ota_base::OTA_RESPONSE_HEADER_OK; +static constexpr auto OTA_RESPONSE_AUTH_OK = ota_base::OTA_RESPONSE_AUTH_OK; +static constexpr auto OTA_RESPONSE_UPDATE_PREPARE_OK = ota_base::OTA_RESPONSE_UPDATE_PREPARE_OK; +static constexpr auto OTA_RESPONSE_BIN_MD5_OK = ota_base::OTA_RESPONSE_BIN_MD5_OK; +static constexpr auto OTA_RESPONSE_RECEIVE_OK = ota_base::OTA_RESPONSE_RECEIVE_OK; +static constexpr auto OTA_RESPONSE_UPDATE_END_OK = ota_base::OTA_RESPONSE_UPDATE_END_OK; +static constexpr auto OTA_RESPONSE_SUPPORTS_COMPRESSION = ota_base::OTA_RESPONSE_SUPPORTS_COMPRESSION; +static constexpr auto OTA_RESPONSE_CHUNK_OK = ota_base::OTA_RESPONSE_CHUNK_OK; +static constexpr auto OTA_RESPONSE_ERROR_MAGIC = ota_base::OTA_RESPONSE_ERROR_MAGIC; +static constexpr auto OTA_RESPONSE_ERROR_UPDATE_PREPARE = ota_base::OTA_RESPONSE_ERROR_UPDATE_PREPARE; +static constexpr auto OTA_RESPONSE_ERROR_AUTH_INVALID = ota_base::OTA_RESPONSE_ERROR_AUTH_INVALID; +static constexpr auto OTA_RESPONSE_ERROR_WRITING_FLASH = ota_base::OTA_RESPONSE_ERROR_WRITING_FLASH; +static constexpr auto OTA_RESPONSE_ERROR_UPDATE_END = ota_base::OTA_RESPONSE_ERROR_UPDATE_END; +static constexpr auto OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = ota_base::OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; +static constexpr auto OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = + ota_base::OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; +static constexpr auto OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = ota_base::OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; +static constexpr auto OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = + ota_base::OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; +static constexpr auto OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = ota_base::OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; +static constexpr auto OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = ota_base::OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; +static constexpr auto OTA_RESPONSE_ERROR_MD5_MISMATCH = ota_base::OTA_RESPONSE_ERROR_MD5_MISMATCH; +static constexpr auto OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = ota_base::OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE; +static constexpr auto OTA_RESPONSE_ERROR_UNKNOWN = ota_base::OTA_RESPONSE_ERROR_UNKNOWN; + #ifdef USE_OTA_STATE_CALLBACK using ota_base::OTAGlobalCallback; diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index 8e2831a063..7f4c89a540 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -91,6 +91,14 @@ class OTAGlobalCallback { OTAGlobalCallback *get_global_ota_callback(); void register_ota_platform(OTAComponent *ota_caller); + +// TODO: When web_server is updated to use ota_base, we need to add thread-safe +// callback execution. The web_server OTA runs in a separate task, so callbacks +// need to be deferred to the main loop task to avoid race conditions. +// This could be implemented using: +// - A queue of callback events that the main loop processes +// - Or using App.schedule() to defer callback execution to the main loop +// Example: App.schedule([=]() { state_callback_.call(state, progress, error); }); #endif } // namespace ota_base diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 641006cb99..a1e3added0 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -110,6 +110,8 @@ class WebServerBase : public Component { void add_handler(AsyncWebHandler *handler); + // TODO: In future PR, update this to use ota_base instead of duplicating OTA code + // Important: OTA callbacks must be thread-safe as web server OTA runs in a separate task void add_ota_handler(); void set_port(uint16_t port) { port_ = port; } From 4f365c1716c2d518076560917f6dcc480de1a01c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 12:11:37 -0500 Subject: [PATCH 07/31] todo --- .../components/esphome/ota/ota_esphome.cpp | 47 +++++++++---------- esphome/components/esphome/ota/ota_esphome.h | 4 +- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/esphome/components/esphome/ota/ota_esphome.cpp b/esphome/components/esphome/ota/ota_esphome.cpp index cfa8364059..5f8d1baf49 100644 --- a/esphome/components/esphome/ota/ota_esphome.cpp +++ b/esphome/components/esphome/ota/ota_esphome.cpp @@ -2,8 +2,7 @@ #ifdef USE_OTA #include "esphome/components/md5/md5.h" #include "esphome/components/network/util.h" -#include "esphome/components/ota/ota.h" // For OTAComponent and callbacks -#include "esphome/components/ota_base/ota_backend.h" // For OTABackend class +#include "esphome/components/ota_base/ota_backend.h" // For OTAComponent and callbacks #include "esphome/components/ota_base/ota_backend_arduino_esp32.h" #include "esphome/components/ota_base/ota_backend_arduino_esp8266.h" #include "esphome/components/ota_base/ota_backend_arduino_libretiny.h" @@ -95,7 +94,7 @@ void ESPHomeOTAComponent::loop() { static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01; void ESPHomeOTAComponent::handle_() { - ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN; + ota_base::OTAResponseTypes error_code = ota_base::OTA_RESPONSE_ERROR_UNKNOWN; bool update_started = false; size_t total = 0; uint32_t last_progress = 0; @@ -103,7 +102,7 @@ void ESPHomeOTAComponent::handle_() { char *sbuf = reinterpret_cast(buf); size_t ota_size; uint8_t ota_features; - std::unique_ptr backend; + std::unique_ptr backend; (void) ota_features; #if USE_OTA_VERSION == 2 size_t size_acknowledged = 0; @@ -130,7 +129,7 @@ void ESPHomeOTAComponent::handle_() { ESP_LOGD(TAG, "Starting update from %s", this->client_->getpeername().c_str()); this->status_set_warning(); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); + this->state_callback_.call(ota_base::OTA_STARTED, 0.0f, 0); #endif if (!this->readall_(buf, 5)) { @@ -141,12 +140,12 @@ void ESPHomeOTAComponent::handle_() { 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::OTA_RESPONSE_ERROR_MAGIC; + error_code = ota_base::OTA_RESPONSE_ERROR_MAGIC; goto error; // NOLINT(cppcoreguidelines-avoid-goto) } // Send OK and version - 2 bytes - buf[0] = ota::OTA_RESPONSE_OK; + buf[0] = ota_base::OTA_RESPONSE_OK; buf[1] = USE_OTA_VERSION; this->writeall_(buf, 2); @@ -161,16 +160,16 @@ void ESPHomeOTAComponent::handle_() { ESP_LOGV(TAG, "Features: 0x%02X", ota_features); // Acknowledge header - 1 byte - buf[0] = ota::OTA_RESPONSE_HEADER_OK; + buf[0] = ota_base::OTA_RESPONSE_HEADER_OK; if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) { - buf[0] = ota::OTA_RESPONSE_SUPPORTS_COMPRESSION; + buf[0] = ota_base::OTA_RESPONSE_SUPPORTS_COMPRESSION; } this->writeall_(buf, 1); #ifdef USE_OTA_PASSWORD if (!this->password_.empty()) { - buf[0] = ota::OTA_RESPONSE_REQUEST_AUTH; + buf[0] = ota_base::OTA_RESPONSE_REQUEST_AUTH; this->writeall_(buf, 1); md5::MD5Digest md5{}; md5.init(); @@ -221,14 +220,14 @@ void ESPHomeOTAComponent::handle_() { if (!matches) { ESP_LOGW(TAG, "Auth failed! Passwords do not match"); - error_code = ota::OTA_RESPONSE_ERROR_AUTH_INVALID; + error_code = ota_base::OTA_RESPONSE_ERROR_AUTH_INVALID; goto error; // NOLINT(cppcoreguidelines-avoid-goto) } } #endif // USE_OTA_PASSWORD // Acknowledge auth OK - 1 byte - buf[0] = ota::OTA_RESPONSE_AUTH_OK; + buf[0] = ota_base::OTA_RESPONSE_AUTH_OK; this->writeall_(buf, 1); // Read size, 4 bytes MSB first @@ -244,12 +243,12 @@ void ESPHomeOTAComponent::handle_() { ESP_LOGV(TAG, "Size is %u bytes", ota_size); error_code = backend->begin(ota_size); - if (error_code != ota::OTA_RESPONSE_OK) + if (error_code != ota_base::OTA_RESPONSE_OK) goto error; // NOLINT(cppcoreguidelines-avoid-goto) update_started = true; // Acknowledge prepare OK - 1 byte - buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK; + buf[0] = ota_base::OTA_RESPONSE_UPDATE_PREPARE_OK; this->writeall_(buf, 1); // Read binary MD5, 32 bytes @@ -262,7 +261,7 @@ void ESPHomeOTAComponent::handle_() { backend->set_update_md5(sbuf); // Acknowledge MD5 OK - 1 byte - buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK; + buf[0] = ota_base::OTA_RESPONSE_BIN_MD5_OK; this->writeall_(buf, 1); while (total < ota_size) { @@ -286,14 +285,14 @@ void ESPHomeOTAComponent::handle_() { } error_code = backend->write(buf, read); - if (error_code != ota::OTA_RESPONSE_OK) { + if (error_code != ota_base::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::OTA_RESPONSE_CHUNK_OK; + buf[0] = ota_base::OTA_RESPONSE_CHUNK_OK; this->writeall_(buf, 1); size_acknowledged += OTA_BLOCK_SIZE; } @@ -305,7 +304,7 @@ void ESPHomeOTAComponent::handle_() { float percentage = (total * 100.0f) / ota_size; ESP_LOGD(TAG, "Progress: %0.1f%%", percentage); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0); + this->state_callback_.call(ota_base::OTA_IN_PROGRESS, percentage, 0); #endif // feed watchdog and give other tasks a chance to run App.feed_wdt(); @@ -314,21 +313,21 @@ void ESPHomeOTAComponent::handle_() { } // Acknowledge receive OK - 1 byte - buf[0] = ota::OTA_RESPONSE_RECEIVE_OK; + buf[0] = ota_base::OTA_RESPONSE_RECEIVE_OK; this->writeall_(buf, 1); error_code = backend->end(); - if (error_code != ota::OTA_RESPONSE_OK) { + if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Error ending update! error_code: %d", error_code); goto error; // NOLINT(cppcoreguidelines-avoid-goto) } // Acknowledge Update end OK - 1 byte - buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK; + buf[0] = ota_base::OTA_RESPONSE_UPDATE_END_OK; this->writeall_(buf, 1); // Read ACK - if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) { + if (!this->readall_(buf, 1) || buf[0] != ota_base::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Reading back acknowledgement failed"); // do not go to error, this is not fatal } @@ -339,7 +338,7 @@ void ESPHomeOTAComponent::handle_() { ESP_LOGI(TAG, "Update complete"); this->status_clear_warning(); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, 0); + this->state_callback_.call(ota_base::OTA_COMPLETED, 100.0f, 0); #endif delay(100); // NOLINT App.safe_reboot(); @@ -356,7 +355,7 @@ error: this->status_momentary_error("onerror", 5000); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_ERROR, 0.0f, static_cast(error_code)); + this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, static_cast(error_code)); #endif } diff --git a/esphome/components/esphome/ota/ota_esphome.h b/esphome/components/esphome/ota/ota_esphome.h index ce5f2a59b9..08266122d6 100644 --- a/esphome/components/esphome/ota/ota_esphome.h +++ b/esphome/components/esphome/ota/ota_esphome.h @@ -4,13 +4,13 @@ #ifdef USE_OTA #include "esphome/core/helpers.h" #include "esphome/core/preferences.h" -#include "esphome/components/ota/ota.h" +#include "esphome/components/ota_base/ota_backend.h" #include "esphome/components/socket/socket.h" namespace esphome { /// ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. -class ESPHomeOTAComponent : public ota::OTAComponent { +class ESPHomeOTAComponent : public ota_base::OTAComponent { public: #ifdef USE_OTA_PASSWORD void set_auth_password(const std::string &password) { password_ = password; } From 58de53123aa9dfb571104ae9c5af6a97af2b501c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 12:41:55 -0500 Subject: [PATCH 08/31] move more --- .../components/http_request/ota/__init__.py | 3 +- .../http_request/ota/ota_http_request.cpp | 25 ++++---- .../http_request/ota/ota_http_request.h | 6 +- .../update/http_request_update.cpp | 7 ++- esphome/components/ota/__init__.py | 4 +- esphome/components/ota/automation.h | 15 +++-- esphome/components/ota/ota.h | 61 +------------------ esphome/components/ota_base/__init__.py | 2 + esphome/components/ota_base/ota_backend.h | 35 ++++++++--- .../web_server_base/web_server_base.h | 2 +- 10 files changed, 65 insertions(+), 95 deletions(-) diff --git a/esphome/components/http_request/ota/__init__.py b/esphome/components/http_request/ota/__init__.py index a3f6d5840c..a1c9dba455 100644 --- a/esphome/components/http_request/ota/__init__.py +++ b/esphome/components/http_request/ota/__init__.py @@ -1,6 +1,7 @@ from esphome import automation import esphome.codegen as cg -from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code +from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code +from esphome.components.ota_base import OTAComponent import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME from esphome.core import coroutine_with_priority diff --git a/esphome/components/http_request/ota/ota_http_request.cpp b/esphome/components/http_request/ota/ota_http_request.cpp index 57e65e6c03..23caa6fbd3 100644 --- a/esphome/components/http_request/ota/ota_http_request.cpp +++ b/esphome/components/http_request/ota/ota_http_request.cpp @@ -6,8 +6,7 @@ #include "esphome/components/md5/md5.h" #include "esphome/components/watchdog/watchdog.h" -#include "esphome/components/ota/ota.h" // For OTAComponent and callbacks -#include "esphome/components/ota_base/ota_backend.h" // For OTABackend class +#include "esphome/components/ota_base/ota_backend.h" #include "esphome/components/ota_base/ota_backend_arduino_esp32.h" #include "esphome/components/ota_base/ota_backend_arduino_esp8266.h" #include "esphome/components/ota_base/ota_backend_arduino_rp2040.h" @@ -51,15 +50,15 @@ void OtaHttpRequestComponent::flash() { ESP_LOGI(TAG, "Starting update"); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0); + this->state_callback_.call(ota_base::OTA_STARTED, 0.0f, 0); #endif auto ota_status = this->do_ota_(); switch (ota_status) { - case ota::OTA_RESPONSE_OK: + case ota_base::OTA_RESPONSE_OK: #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, ota_status); + this->state_callback_.call(ota_base::OTA_COMPLETED, 100.0f, ota_status); #endif delay(10); App.safe_reboot(); @@ -67,7 +66,7 @@ void OtaHttpRequestComponent::flash() { default: #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_ERROR, 0.0f, ota_status); + this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, ota_status); #endif this->md5_computed_.clear(); // will be reset at next attempt this->md5_expected_.clear(); // will be reset at next attempt @@ -75,7 +74,7 @@ void OtaHttpRequestComponent::flash() { } } -void OtaHttpRequestComponent::cleanup_(std::unique_ptr backend, +void OtaHttpRequestComponent::cleanup_(std::unique_ptr backend, const std::shared_ptr &container) { if (this->update_started_) { ESP_LOGV(TAG, "Aborting OTA backend"); @@ -118,7 +117,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { ESP_LOGV(TAG, "OTA backend begin"); auto backend = ota_base::make_ota_backend(); auto error_code = backend->begin(container->content_length); - if (error_code != ota::OTA_RESPONSE_OK) { + if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "backend->begin error: %d", error_code); this->cleanup_(std::move(backend), container); return error_code; @@ -145,7 +144,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { // write bytes to OTA backend this->update_started_ = true; error_code = backend->write(buf, bufsize); - if (error_code != ota::OTA_RESPONSE_OK) { + if (error_code != ota_base::OTA_RESPONSE_OK) { // error code explanation available at // https://github.com/esphome/esphome/blob/dev/esphome/components/ota/ota_backend.h ESP_LOGE(TAG, "Error code (%02X) writing binary data to flash at offset %d and size %d", error_code, @@ -161,7 +160,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { float percentage = container->get_bytes_read() * 100.0f / container->content_length; ESP_LOGD(TAG, "Progress: %0.1f%%", percentage); #ifdef USE_OTA_STATE_CALLBACK - this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0); + this->state_callback_.call(ota_base::OTA_IN_PROGRESS, percentage, 0); #endif } } // while @@ -175,7 +174,7 @@ uint8_t OtaHttpRequestComponent::do_ota_() { if (strncmp(this->md5_computed_.c_str(), this->md5_expected_.c_str(), MD5_SIZE) != 0) { ESP_LOGE(TAG, "MD5 computed: %s - Aborting due to MD5 mismatch", this->md5_computed_.c_str()); this->cleanup_(std::move(backend), container); - return ota::OTA_RESPONSE_ERROR_MD5_MISMATCH; + return ota_base::OTA_RESPONSE_ERROR_MD5_MISMATCH; } else { backend->set_update_md5(md5_receive_str.get()); } @@ -188,14 +187,14 @@ uint8_t OtaHttpRequestComponent::do_ota_() { delay(100); // NOLINT error_code = backend->end(); - if (error_code != ota::OTA_RESPONSE_OK) { + if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGW(TAG, "Error ending update! error_code: %d", error_code); this->cleanup_(std::move(backend), container); return error_code; } ESP_LOGI(TAG, "Update complete"); - return ota::OTA_RESPONSE_OK; + return ota_base::OTA_RESPONSE_OK; } std::string OtaHttpRequestComponent::get_url_with_auth_(const std::string &url) { diff --git a/esphome/components/http_request/ota/ota_http_request.h b/esphome/components/http_request/ota/ota_http_request.h index 20a7abba71..138731fc5c 100644 --- a/esphome/components/http_request/ota/ota_http_request.h +++ b/esphome/components/http_request/ota/ota_http_request.h @@ -1,6 +1,6 @@ #pragma once -#include "esphome/components/ota/ota.h" +#include "esphome/components/ota_base/ota_backend.h" #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/helpers.h" @@ -22,7 +22,7 @@ enum OtaHttpRequestError : uint8_t { OTA_CONNECTION_ERROR = 0x12, }; -class OtaHttpRequestComponent : public ota::OTAComponent, public Parented { +class OtaHttpRequestComponent : public ota_base::OTAComponent, public Parented { public: void setup() override; void dump_config() override; @@ -40,7 +40,7 @@ class OtaHttpRequestComponent : public ota::OTAComponent, public Parented backend, const std::shared_ptr &container); + void cleanup_(std::unique_ptr backend, const std::shared_ptr &container); uint8_t do_ota_(); std::string get_url_with_auth_(const std::string &url); bool http_get_md5_(); diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 828fb5bd8b..3a4c83f398 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -5,6 +5,7 @@ #include "esphome/components/json/json_util.h" #include "esphome/components/network/util.h" +#include "esphome/components/ota_base/ota_backend.h" namespace esphome { namespace http_request { @@ -21,13 +22,13 @@ static const char *const TAG = "http_request.update"; static const size_t MAX_READ_SIZE = 256; void HttpRequestUpdate::setup() { - this->ota_parent_->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t err) { - if (state == ota::OTAState::OTA_IN_PROGRESS) { + this->ota_parent_->add_on_state_callback([this](ota_base::OTAState state, float progress, uint8_t err) { + if (state == ota_base::OTAState::OTA_IN_PROGRESS) { this->state_ = update::UPDATE_STATE_INSTALLING; this->update_info_.has_progress = true; this->update_info_.progress = progress; this->publish_state(); - } else if (state == ota::OTAState::OTA_ABORT || state == ota::OTAState::OTA_ERROR) { + } else if (state == ota_base::OTAState::OTA_ABORT || state == ota_base::OTAState::OTA_ERROR) { this->state_ = update::UPDATE_STATE_AVAILABLE; this->status_set_error("Failed to install firmware"); this->publish_state(); diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index e990256969..1fa9bfa410 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,5 +1,6 @@ from esphome import automation import esphome.codegen as cg +from esphome.components.ota_base import OTAState import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, @@ -23,8 +24,7 @@ CONF_ON_STATE_CHANGE = "on_state_change" ota_ns = cg.esphome_ns.namespace("ota") -OTAComponent = ota_ns.class_("OTAComponent", cg.Component) -OTAState = ota_ns.enum("OTAState") +# OTAComponent and OTAState are imported from ota_base OTAAbortTrigger = ota_ns.class_("OTAAbortTrigger", automation.Trigger.template()) OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h index c3ff8e33d7..2dbf0c70e1 100644 --- a/esphome/components/ota/automation.h +++ b/esphome/components/ota/automation.h @@ -1,12 +1,17 @@ #pragma once #ifdef USE_OTA_STATE_CALLBACK #include "ota.h" +#include "esphome/components/ota_base/ota_backend.h" #include "esphome/core/automation.h" namespace esphome { namespace ota { +// Import types from ota_base for the automation triggers +using ota_base::OTAComponent; +using ota_base::OTAState; + class OTAStateChangeTrigger : public Trigger { public: explicit OTAStateChangeTrigger(OTAComponent *parent) { @@ -22,7 +27,7 @@ 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()) { + if (state == ota_base::OTA_STARTED && !parent->is_failed()) { trigger(); } }); @@ -33,7 +38,7 @@ class OTAProgressTrigger : public Trigger { 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()) { + if (state == ota_base::OTA_IN_PROGRESS && !parent->is_failed()) { trigger(progress); } }); @@ -44,7 +49,7 @@ 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()) { + if (state == ota_base::OTA_COMPLETED && !parent->is_failed()) { trigger(); } }); @@ -55,7 +60,7 @@ class OTAAbortTrigger : public Trigger<> { public: explicit OTAAbortTrigger(OTAComponent *parent) { parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { - if (state == OTA_ABORT && !parent->is_failed()) { + if (state == ota_base::OTA_ABORT && !parent->is_failed()) { trigger(); } }); @@ -66,7 +71,7 @@ class OTAErrorTrigger : public Trigger { 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()) { + if (state == ota_base::OTA_ERROR && !parent->is_failed()) { trigger(error); } }); diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h index 42a6cdbe85..141f99c87b 100644 --- a/esphome/components/ota/ota.h +++ b/esphome/components/ota/ota.h @@ -1,69 +1,12 @@ #pragma once #include "esphome/core/defines.h" -#include "esphome/components/ota_base/ota_backend.h" namespace esphome { namespace ota { -// Import types from ota_base namespace for backward compatibility -using ota_base::OTABackend; -using ota_base::OTAComponent; -using ota_base::OTAResponseTypes; -using ota_base::OTAState; - -// Re-export specific enum values for backward compatibility -// OTAState values -static constexpr auto OTA_COMPLETED = ota_base::OTA_COMPLETED; -static constexpr auto OTA_STARTED = ota_base::OTA_STARTED; -static constexpr auto OTA_IN_PROGRESS = ota_base::OTA_IN_PROGRESS; -static constexpr auto OTA_ABORT = ota_base::OTA_ABORT; -static constexpr auto OTA_ERROR = ota_base::OTA_ERROR; - -// OTAResponseTypes values -static constexpr auto OTA_RESPONSE_OK = ota_base::OTA_RESPONSE_OK; -static constexpr auto OTA_RESPONSE_REQUEST_AUTH = ota_base::OTA_RESPONSE_REQUEST_AUTH; -static constexpr auto OTA_RESPONSE_HEADER_OK = ota_base::OTA_RESPONSE_HEADER_OK; -static constexpr auto OTA_RESPONSE_AUTH_OK = ota_base::OTA_RESPONSE_AUTH_OK; -static constexpr auto OTA_RESPONSE_UPDATE_PREPARE_OK = ota_base::OTA_RESPONSE_UPDATE_PREPARE_OK; -static constexpr auto OTA_RESPONSE_BIN_MD5_OK = ota_base::OTA_RESPONSE_BIN_MD5_OK; -static constexpr auto OTA_RESPONSE_RECEIVE_OK = ota_base::OTA_RESPONSE_RECEIVE_OK; -static constexpr auto OTA_RESPONSE_UPDATE_END_OK = ota_base::OTA_RESPONSE_UPDATE_END_OK; -static constexpr auto OTA_RESPONSE_SUPPORTS_COMPRESSION = ota_base::OTA_RESPONSE_SUPPORTS_COMPRESSION; -static constexpr auto OTA_RESPONSE_CHUNK_OK = ota_base::OTA_RESPONSE_CHUNK_OK; -static constexpr auto OTA_RESPONSE_ERROR_MAGIC = ota_base::OTA_RESPONSE_ERROR_MAGIC; -static constexpr auto OTA_RESPONSE_ERROR_UPDATE_PREPARE = ota_base::OTA_RESPONSE_ERROR_UPDATE_PREPARE; -static constexpr auto OTA_RESPONSE_ERROR_AUTH_INVALID = ota_base::OTA_RESPONSE_ERROR_AUTH_INVALID; -static constexpr auto OTA_RESPONSE_ERROR_WRITING_FLASH = ota_base::OTA_RESPONSE_ERROR_WRITING_FLASH; -static constexpr auto OTA_RESPONSE_ERROR_UPDATE_END = ota_base::OTA_RESPONSE_ERROR_UPDATE_END; -static constexpr auto OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = ota_base::OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING; -static constexpr auto OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = - ota_base::OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG; -static constexpr auto OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = ota_base::OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG; -static constexpr auto OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = - ota_base::OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE; -static constexpr auto OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = ota_base::OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE; -static constexpr auto OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = ota_base::OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION; -static constexpr auto OTA_RESPONSE_ERROR_MD5_MISMATCH = ota_base::OTA_RESPONSE_ERROR_MD5_MISMATCH; -static constexpr auto OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = ota_base::OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE; -static constexpr auto OTA_RESPONSE_ERROR_UNKNOWN = ota_base::OTA_RESPONSE_ERROR_UNKNOWN; - -#ifdef USE_OTA_STATE_CALLBACK -using ota_base::OTAGlobalCallback; - -// Deprecated: Use ota_base::get_global_ota_callback() instead -// Will be removed after 2025-12-30 (6 months from 2025-06-30) -[[deprecated("Use ota_base::get_global_ota_callback() instead")]] inline OTAGlobalCallback *get_global_ota_callback() { - return ota_base::get_global_ota_callback(); -} - -// Deprecated: Use ota_base::register_ota_platform() instead -// Will be removed after 2025-12-30 (6 months from 2025-06-30) -[[deprecated("Use ota_base::register_ota_platform() instead")]] inline void register_ota_platform( - OTAComponent *ota_caller) { - ota_base::register_ota_platform(ota_caller); -} -#endif +// All OTA backend functionality has been moved to the ota_base component. +// This file remains for the high-level OTA automation triggers defined in automation.h } // namespace ota } // namespace esphome diff --git a/esphome/components/ota_base/__init__.py b/esphome/components/ota_base/__init__.py index 7a1f233d26..2203785953 100644 --- a/esphome/components/ota_base/__init__.py +++ b/esphome/components/ota_base/__init__.py @@ -5,6 +5,8 @@ CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["md5"] ota_base_ns = cg.esphome_ns.namespace("ota_base") +OTAComponent = ota_base_ns.class_("OTAComponent", cg.Component) +OTAState = ota_base_ns.enum("OTAState") @coroutine_with_priority(52.0) diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index 7f4c89a540..f60019ce5a 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -69,7 +69,29 @@ class OTAComponent : public Component { } protected: - CallbackManager state_callback_{}; + /** Thread-safe callback manager that automatically defers to main loop. + * + * This ensures all OTA callbacks are executed in the main loop task, + * making them safe to call from any context (including web_server's OTA task). + * Existing code doesn't need changes - callbacks are automatically deferred. + */ + class DeferredCallbackManager : public CallbackManager { + public: + DeferredCallbackManager(OTAComponent *component) : component_(component) {} + + /// Override call to automatically defer to main loop + void call(OTAState state, float progress, uint8_t error) { + // Always defer to main loop for thread safety + component_->defer([this, state, progress, error]() { + CallbackManager::call(state, progress, error); + }); + } + + private: + OTAComponent *component_; + }; + + DeferredCallbackManager state_callback_{this}; #endif }; @@ -92,13 +114,10 @@ class OTAGlobalCallback { OTAGlobalCallback *get_global_ota_callback(); void register_ota_platform(OTAComponent *ota_caller); -// TODO: When web_server is updated to use ota_base, we need to add thread-safe -// callback execution. The web_server OTA runs in a separate task, so callbacks -// need to be deferred to the main loop task to avoid race conditions. -// This could be implemented using: -// - A queue of callback events that the main loop processes -// - Or using App.schedule() to defer callback execution to the main loop -// Example: App.schedule([=]() { state_callback_.call(state, progress, error); }); +// Thread-safe callback execution is automatically provided by DeferredCallbackManager +// which overrides call() to use Component::defer(). This ensures all OTA callbacks +// run in the main loop task, making them safe to call from any context including +// web_server's separate OTA task. No code changes needed. #endif } // namespace ota_base diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index a1e3added0..7e339dadab 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -111,7 +111,7 @@ class WebServerBase : public Component { void add_handler(AsyncWebHandler *handler); // TODO: In future PR, update this to use ota_base instead of duplicating OTA code - // Important: OTA callbacks must be thread-safe as web server OTA runs in a separate task + // Note: OTA callbacks in ota_base are automatically thread-safe via defer() void add_ota_handler(); void set_port(uint16_t port) { port_ = port; } From e385f87d6cee52d2aa29774e7ed842affca4e6b9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 12:46:47 -0500 Subject: [PATCH 09/31] move more --- esphome/components/http_request/ota/__init__.py | 4 +++- esphome/components/ota/__init__.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/esphome/components/http_request/ota/__init__.py b/esphome/components/http_request/ota/__init__.py index a1c9dba455..d3a54c699b 100644 --- a/esphome/components/http_request/ota/__init__.py +++ b/esphome/components/http_request/ota/__init__.py @@ -1,7 +1,6 @@ from esphome import automation import esphome.codegen as cg from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code -from esphome.components.ota_base import OTAComponent import esphome.config_validation as cv from esphome.const import CONF_ID, CONF_PASSWORD, CONF_URL, CONF_USERNAME from esphome.core import coroutine_with_priority @@ -16,6 +15,9 @@ DEPENDENCIES = ["network", "http_request"] CONF_MD5 = "md5" CONF_MD5_URL = "md5_url" +ota_base_ns = cg.esphome_ns.namespace("ota_base") +OTAComponent = ota_base_ns.class_("OTAComponent", cg.Component) + OtaHttpRequestComponent = http_request_ns.class_( "OtaHttpRequestComponent", OTAComponent ) diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 1fa9bfa410..2ac09607be 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,6 +1,5 @@ from esphome import automation import esphome.codegen as cg -from esphome.components.ota_base import OTAState import esphome.config_validation as cv from esphome.const import ( CONF_ESPHOME, @@ -11,6 +10,8 @@ from esphome.const import ( ) from esphome.core import coroutine_with_priority +from ..ota_base import OTAState + CODEOWNERS = ["@esphome/core"] AUTO_LOAD = ["safe_mode", "ota_base"] @@ -24,7 +25,6 @@ CONF_ON_STATE_CHANGE = "on_state_change" ota_ns = cg.esphome_ns.namespace("ota") -# OTAComponent and OTAState are imported from ota_base OTAAbortTrigger = ota_ns.class_("OTAAbortTrigger", automation.Trigger.template()) OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) From 490ca8ad5a5b732e4b4cd7d28a6870ff63376196 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 12:53:41 -0500 Subject: [PATCH 10/31] relo --- esphome/components/esphome/ota/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esphome/components/esphome/ota/__init__.py b/esphome/components/esphome/ota/__init__.py index 901657ec82..bf5c438f9b 100644 --- a/esphome/components/esphome/ota/__init__.py +++ b/esphome/components/esphome/ota/__init__.py @@ -1,7 +1,8 @@ import logging import esphome.codegen as cg -from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code +from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code +from esphome.components.ota_base import OTAComponent from esphome.config_helpers import merge_config import esphome.config_validation as cv from esphome.const import ( From c96ffefa42b43cf102f9615ace915f2d0fc532ac Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 13:02:26 -0500 Subject: [PATCH 11/31] fix --- esphome/components/ota_base/ota_backend.h | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index f60019ce5a..b20155a45a 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -69,29 +69,29 @@ class OTAComponent : public Component { } protected: - /** Thread-safe callback manager that automatically defers to main loop. + /** Thread-safe callback wrapper that automatically defers to main loop. * * This ensures all OTA callbacks are executed in the main loop task, * making them safe to call from any context (including web_server's OTA task). * Existing code doesn't need changes - callbacks are automatically deferred. */ - class DeferredCallbackManager : public CallbackManager { + class StateCallbackManager { public: - DeferredCallbackManager(OTAComponent *component) : component_(component) {} + StateCallbackManager(OTAComponent *component) : component_(component) {} + + void add(std::function &&callback) { callbacks_.add(std::move(callback)); } - /// Override call to automatically defer to main loop void call(OTAState state, float progress, uint8_t error) { // Always defer to main loop for thread safety - component_->defer([this, state, progress, error]() { - CallbackManager::call(state, progress, error); - }); + component_->defer([this, state, progress, error]() { this->callbacks_.call(state, progress, error); }); } private: OTAComponent *component_; + CallbackManager callbacks_; }; - DeferredCallbackManager state_callback_{this}; + StateCallbackManager state_callback_{this}; #endif }; @@ -114,10 +114,10 @@ class OTAGlobalCallback { OTAGlobalCallback *get_global_ota_callback(); void register_ota_platform(OTAComponent *ota_caller); -// Thread-safe callback execution is automatically provided by DeferredCallbackManager -// which overrides call() to use Component::defer(). This ensures all OTA callbacks -// run in the main loop task, making them safe to call from any context including -// web_server's separate OTA task. No code changes needed. +// Thread-safe callback execution is automatically provided by StateCallbackManager +// which uses Component::defer() to ensure all OTA callbacks run in the main loop task. +// This makes OTA callbacks safe to call from any context including web_server's +// separate OTA task. No code changes needed. #endif } // namespace ota_base From 519c49f17512e1fc3af7b065fd5e9a96658ae165 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 13:11:27 -0500 Subject: [PATCH 12/31] Revert "fix" This reverts commit c96ffefa42b43cf102f9615ace915f2d0fc532ac. --- esphome/components/ota_base/ota_backend.h | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index b20155a45a..f60019ce5a 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -69,29 +69,29 @@ class OTAComponent : public Component { } protected: - /** Thread-safe callback wrapper that automatically defers to main loop. + /** Thread-safe callback manager that automatically defers to main loop. * * This ensures all OTA callbacks are executed in the main loop task, * making them safe to call from any context (including web_server's OTA task). * Existing code doesn't need changes - callbacks are automatically deferred. */ - class StateCallbackManager { + class DeferredCallbackManager : public CallbackManager { public: - StateCallbackManager(OTAComponent *component) : component_(component) {} - - void add(std::function &&callback) { callbacks_.add(std::move(callback)); } + DeferredCallbackManager(OTAComponent *component) : component_(component) {} + /// Override call to automatically defer to main loop void call(OTAState state, float progress, uint8_t error) { // Always defer to main loop for thread safety - component_->defer([this, state, progress, error]() { this->callbacks_.call(state, progress, error); }); + component_->defer([this, state, progress, error]() { + CallbackManager::call(state, progress, error); + }); } private: OTAComponent *component_; - CallbackManager callbacks_; }; - StateCallbackManager state_callback_{this}; + DeferredCallbackManager state_callback_{this}; #endif }; @@ -114,10 +114,10 @@ class OTAGlobalCallback { OTAGlobalCallback *get_global_ota_callback(); void register_ota_platform(OTAComponent *ota_caller); -// Thread-safe callback execution is automatically provided by StateCallbackManager -// which uses Component::defer() to ensure all OTA callbacks run in the main loop task. -// This makes OTA callbacks safe to call from any context including web_server's -// separate OTA task. No code changes needed. +// Thread-safe callback execution is automatically provided by DeferredCallbackManager +// which overrides call() to use Component::defer(). This ensures all OTA callbacks +// run in the main loop task, making them safe to call from any context including +// web_server's separate OTA task. No code changes needed. #endif } // namespace ota_base From 44a7c1d4a5b696c113efb32d2a829bb5175d528d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 13:14:55 -0500 Subject: [PATCH 13/31] cleanup --- esphome/components/ota_base/ota_backend.h | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/esphome/components/ota_base/ota_backend.h b/esphome/components/ota_base/ota_backend.h index f60019ce5a..27637a9af2 100644 --- a/esphome/components/ota_base/ota_backend.h +++ b/esphome/components/ota_base/ota_backend.h @@ -69,29 +69,28 @@ class OTAComponent : public Component { } protected: - /** Thread-safe callback manager that automatically defers to main loop. + /** Extended callback manager with deferred call support. * - * This ensures all OTA callbacks are executed in the main loop task, - * making them safe to call from any context (including web_server's OTA task). - * Existing code doesn't need changes - callbacks are automatically deferred. + * This adds a call_deferred() method for thread-safe execution from other tasks. */ - class DeferredCallbackManager : public CallbackManager { + class StateCallbackManager : public CallbackManager { public: - DeferredCallbackManager(OTAComponent *component) : component_(component) {} + StateCallbackManager(OTAComponent *component) : component_(component) {} - /// Override call to automatically defer to main loop - void call(OTAState state, float progress, uint8_t error) { - // Always defer to main loop for thread safety - component_->defer([this, state, progress, error]() { - CallbackManager::call(state, progress, error); - }); + /** Call callbacks with deferral to main loop (for thread safety). + * + * This should be used by OTA implementations that run in separate tasks + * (like web_server OTA) to ensure callbacks execute in the main loop. + */ + void call_deferred(OTAState state, float progress, uint8_t error) { + component_->defer([this, state, progress, error]() { this->call(state, progress, error); }); } private: OTAComponent *component_; }; - DeferredCallbackManager state_callback_{this}; + StateCallbackManager state_callback_{this}; #endif }; @@ -114,10 +113,10 @@ class OTAGlobalCallback { OTAGlobalCallback *get_global_ota_callback(); void register_ota_platform(OTAComponent *ota_caller); -// Thread-safe callback execution is automatically provided by DeferredCallbackManager -// which overrides call() to use Component::defer(). This ensures all OTA callbacks -// run in the main loop task, making them safe to call from any context including -// web_server's separate OTA task. No code changes needed. +// OTA implementations should use: +// - state_callback_.call() when already in main loop (e.g., esphome OTA) +// - state_callback_.call_deferred() when in separate task (e.g., web_server OTA) +// This ensures proper callback execution in all contexts. #endif } // namespace ota_base From 340bb5cef62e59631c4a6dd3c86b690cc8060929 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 13:31:55 -0500 Subject: [PATCH 14/31] clenaup --- esphome/components/web_server_base/web_server_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 7e339dadab..a2709c1087 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -111,7 +111,7 @@ class WebServerBase : public Component { void add_handler(AsyncWebHandler *handler); // TODO: In future PR, update this to use ota_base instead of duplicating OTA code - // Note: OTA callbacks in ota_base are automatically thread-safe via defer() + // Note: web_server OTA runs in a separate task, so use state_callback_.call_deferred() void add_ota_handler(); void set_port(uint16_t port) { port_ = port; } From 560886eb90b1d457e42fb7f9bfedc6bf2a3716a9 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 30 Jun 2025 13:32:59 -0500 Subject: [PATCH 15/31] clenaup --- esphome/components/web_server_base/web_server_base.h | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index a2709c1087..5242b2732c 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -112,6 +112,7 @@ class WebServerBase : public Component { // TODO: In future PR, update this to use ota_base instead of duplicating OTA code // Note: web_server OTA runs in a separate task, so use state_callback_.call_deferred() + // Note: web_server OTA does not support MD5, backends should only check MD5 if set void add_ota_handler(); void set_port(uint16_t port) { port_ = port; } From 649ad47e628cb8f5ee965cda8da9f23a82ab2902 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:05:23 -0500 Subject: [PATCH 16/31] web_server_ support for ota backend idf --- .../components/ota_base/ota_backend_esp_idf.cpp | 15 ++++++++++----- esphome/components/ota_base/ota_backend_esp_idf.h | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/esphome/components/ota_base/ota_backend_esp_idf.cpp b/esphome/components/ota_base/ota_backend_esp_idf.cpp index eef4cb8026..b49a690a95 100644 --- a/esphome/components/ota_base/ota_backend_esp_idf.cpp +++ b/esphome/components/ota_base/ota_backend_esp_idf.cpp @@ -67,7 +67,10 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) { return OTA_RESPONSE_OK; } -void IDFOTABackend::set_update_md5(const char *expected_md5) { memcpy(this->expected_bin_md5_, expected_md5, 32); } +void IDFOTABackend::set_update_md5(const char *expected_md5) { + memcpy(this->expected_bin_md5_, expected_md5, 32); + this->md5_set_ = true; +} OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) { esp_err_t err = esp_ota_write(this->update_handle_, data, len); @@ -84,10 +87,12 @@ OTAResponseTypes IDFOTABackend::write(uint8_t *data, size_t len) { } OTAResponseTypes IDFOTABackend::end() { - this->md5_.calculate(); - if (!this->md5_.equals_hex(this->expected_bin_md5_)) { - this->abort(); - return OTA_RESPONSE_ERROR_MD5_MISMATCH; + if (this->md5_set_) { + this->md5_.calculate(); + if (!this->md5_.equals_hex(this->expected_bin_md5_)) { + this->abort(); + return OTA_RESPONSE_ERROR_MD5_MISMATCH; + } } esp_err_t err = esp_ota_end(this->update_handle_); this->update_handle_ = 0; diff --git a/esphome/components/ota_base/ota_backend_esp_idf.h b/esphome/components/ota_base/ota_backend_esp_idf.h index a7e34cb5ae..3c760df1c8 100644 --- a/esphome/components/ota_base/ota_backend_esp_idf.h +++ b/esphome/components/ota_base/ota_backend_esp_idf.h @@ -24,6 +24,7 @@ class IDFOTABackend : public OTABackend { const esp_partition_t *partition_; md5::MD5Digest md5_{}; char expected_bin_md5_[32]; + bool md5_set_{false}; }; } // namespace ota_base From 8b195d7f6373ccfc277bb417df910cace0570ff6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:11:41 -0500 Subject: [PATCH 17/31] use ota backend --- esphome/components/web_server/__init__.py | 4 +- .../web_server_base/web_server_base.cpp | 149 ++++-------------- .../web_server_base/web_server_base.h | 9 +- 3 files changed, 34 insertions(+), 128 deletions(-) diff --git a/esphome/components/web_server/__init__.py b/esphome/components/web_server/__init__.py index ca145c732b..8c77866540 100644 --- a/esphome/components/web_server/__init__.py +++ b/esphome/components/web_server/__init__.py @@ -34,7 +34,7 @@ from esphome.const import ( from esphome.core import CORE, coroutine_with_priority import esphome.final_validate as fv -AUTO_LOAD = ["json", "web_server_base"] +AUTO_LOAD = ["json", "web_server_base", "ota_base"] CONF_SORTING_GROUP_ID = "sorting_group_id" CONF_SORTING_GROUPS = "sorting_groups" @@ -274,7 +274,7 @@ async def to_code(config): cg.add(var.set_allow_ota(config[CONF_OTA])) if config[CONF_OTA]: # Define USE_WEBSERVER_OTA based only on web_server OTA config - # This allows web server OTA to work without loading the OTA component + # Web server OTA now uses ota_base backend for consistency cg.add_define("USE_WEBSERVER_OTA") cg.add(var.set_expose_log(config[CONF_LOG])) if config[CONF_ENABLE_PRIVATE_NETWORK_ACCESS]: diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 9ad88e09f4..fbc3adbf03 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -14,9 +14,8 @@ #endif #endif -#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA) -#include -#include +#ifdef USE_WEBSERVER_OTA +#include "esphome/components/ota_base/ota_backend.h" #endif namespace esphome { @@ -24,104 +23,6 @@ namespace web_server_base { static const char *const TAG = "web_server_base"; -#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA) -// Minimal OTA backend implementation for web server -// This allows OTA updates via web server without requiring the OTA component -// TODO: In the future, this should be refactored into a common ota_base component -// that both web_server and ota components can depend on, avoiding code duplication -// while keeping the components independent. This would allow both ESP-IDF and Arduino -// implementations to share the base OTA functionality without requiring the full OTA component. -// The IDFWebServerOTABackend class is intentionally designed with the same interface -// as OTABackend to make it easy to swap to using OTABackend when the ota component -// is split into ota and ota_base in the future. -class IDFWebServerOTABackend { - public: - bool begin() { - this->partition_ = esp_ota_get_next_update_partition(nullptr); - if (this->partition_ == nullptr) { - ESP_LOGE(TAG, "No OTA partition available"); - return false; - } - -#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15 - // The following function takes longer than the default timeout of WDT due to flash erase -#if ESP_IDF_VERSION_MAJOR >= 5 - esp_task_wdt_config_t wdtc; - wdtc.idle_core_mask = 0; -#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 - wdtc.idle_core_mask |= (1 << 0); -#endif -#if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 - wdtc.idle_core_mask |= (1 << 1); -#endif - wdtc.timeout_ms = 15000; - wdtc.trigger_panic = false; - esp_task_wdt_reconfigure(&wdtc); -#else - esp_task_wdt_init(15, false); -#endif -#endif - - esp_err_t err = esp_ota_begin(this->partition_, 0, &this->update_handle_); - -#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15 - // Set the WDT back to the configured timeout -#if ESP_IDF_VERSION_MAJOR >= 5 - wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000; - esp_task_wdt_reconfigure(&wdtc); -#else - esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false); -#endif -#endif - - if (err != ESP_OK) { - esp_ota_abort(this->update_handle_); - this->update_handle_ = 0; - ESP_LOGE(TAG, "esp_ota_begin failed: %s", esp_err_to_name(err)); - return false; - } - return true; - } - - bool write(uint8_t *data, size_t len) { - esp_err_t err = esp_ota_write(this->update_handle_, data, len); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_write failed: %s", esp_err_to_name(err)); - return false; - } - return true; - } - - bool end() { - esp_err_t err = esp_ota_end(this->update_handle_); - this->update_handle_ = 0; - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_end failed: %s", esp_err_to_name(err)); - return false; - } - - err = esp_ota_set_boot_partition(this->partition_); - if (err != ESP_OK) { - ESP_LOGE(TAG, "esp_ota_set_boot_partition failed: %s", esp_err_to_name(err)); - return false; - } - - return true; - } - - void abort() { - if (this->update_handle_ != 0) { - esp_ota_abort(this->update_handle_); - this->update_handle_ = 0; - } - } - - private: - esp_ota_handle_t update_handle_{0}; - const esp_partition_t *partition_{nullptr}; -}; -#endif - void WebServerBase::add_handler(AsyncWebHandler *handler) { // remove all handlers @@ -213,33 +114,38 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin #endif // USE_ARDUINO #ifdef USE_ESP_IDF - // ESP-IDF implementation + // ESP-IDF implementation using ota_base backend + ota_base::OTAResponseTypes error_code = ota_base::OTA_RESPONSE_OK; + if (index == 0 && !this->ota_backend_) { // Initialize OTA on first call this->ota_init_(filename.c_str()); - this->ota_success_ = false; - auto *backend = new IDFWebServerOTABackend(); - if (!backend->begin()) { - ESP_LOGE(TAG, "OTA begin failed"); - delete backend; + this->ota_backend_ = ota_base::make_ota_backend(); + if (!this->ota_backend_) { + ESP_LOGE(TAG, "Failed to create OTA backend"); + return; + } + + error_code = this->ota_backend_->begin(request->contentLength()); + if (error_code != ota_base::OTA_RESPONSE_OK) { + ESP_LOGE(TAG, "OTA begin failed: %d", error_code); + this->ota_backend_.reset(); return; } - this->ota_backend_ = backend; } - auto *backend = static_cast(this->ota_backend_); - if (!backend) { + if (!this->ota_backend_) { return; } // Process data if (len > 0) { - if (!backend->write(data, len)) { - ESP_LOGE(TAG, "OTA write failed"); - backend->abort(); - delete backend; - this->ota_backend_ = nullptr; + error_code = this->ota_backend_->write(data, len); + if (error_code != ota_base::OTA_RESPONSE_OK) { + ESP_LOGE(TAG, "OTA write failed: %d", error_code); + this->ota_backend_->abort(); + this->ota_backend_.reset(); return; } this->ota_read_length_ += len; @@ -248,14 +154,13 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin // Finalize if (final) { - this->ota_success_ = backend->end(); - if (this->ota_success_) { + error_code = this->ota_backend_->end(); + if (error_code == ota_base::OTA_RESPONSE_OK) { this->schedule_ota_reboot_(); } else { - ESP_LOGE(TAG, "OTA end failed"); + ESP_LOGE(TAG, "OTA end failed: %d", error_code); } - delete backend; - this->ota_backend_ = nullptr; + this->ota_backend_.reset(); } #endif // USE_ESP_IDF } @@ -273,8 +178,8 @@ void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) { } #endif // USE_ARDUINO #ifdef USE_ESP_IDF - // Send response based on the OTA result - response = request->beginResponse(200, "text/plain", this->ota_success_ ? "Update Successful!" : "Update Failed!"); + // Send response based on whether backend still exists (error) or was reset (success) + response = request->beginResponse(200, "text/plain", !this->ota_backend_ ? "Update Successful!" : "Update Failed!"); #endif // USE_ESP_IDF response->addHeader("Connection", "close"); request->send(response); diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 09a41956c9..b221d7b28d 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -14,6 +14,10 @@ #include "esphome/components/web_server_idf/web_server_idf.h" #endif +#ifdef USE_WEBSERVER_OTA +#include "esphome/components/ota_base/ota_backend.h" +#endif + namespace esphome { namespace web_server_base { @@ -153,10 +157,7 @@ class OTARequestHandler : public AsyncWebHandler { WebServerBase *parent_; private: -#ifdef USE_ESP_IDF - void *ota_backend_{nullptr}; - bool ota_success_{false}; -#endif + std::unique_ptr ota_backend_{nullptr}; }; #endif // USE_WEBSERVER_OTA From 943d0f103d6123b16f6f59762f5ba847ee0dab2b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:17:28 -0500 Subject: [PATCH 18/31] single ota path --- .../ota_base/ota_backend_arduino_esp8266.cpp | 5 + .../web_server_base/web_server_base.cpp | 102 +++++------------- 2 files changed, 32 insertions(+), 75 deletions(-) diff --git a/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp index 38d0ad96c3..1133ce7b05 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp @@ -17,6 +17,11 @@ static const char *const TAG = "ota.arduino_esp8266"; std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { + // Handle UPDATE_SIZE_UNKNOWN (0) by calculating available space + if (image_size == 0) { + // NOLINTNEXTLINE(readability-static-accessed-through-instance) + image_size = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + } bool ret = Update.begin(image_size, U_FLASH); if (ret) { esp8266::preferences_prevent_write(true); diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index fbc3adbf03..babef752d9 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -4,20 +4,18 @@ #include "esphome/core/helpers.h" #include "esphome/core/log.h" -#ifdef USE_ARDUINO -#include -#if defined(USE_ESP32) || defined(USE_LIBRETINY) -#include -#endif -#ifdef USE_ESP8266 -#include -#endif -#endif - #ifdef USE_WEBSERVER_OTA #include "esphome/components/ota_base/ota_backend.h" #endif +#ifdef USE_ARDUINO +#ifdef USE_ESP8266 +#include +#elif defined(USE_ESP32) || defined(USE_LIBRETINY) +#include +#endif +#endif + namespace esphome { namespace web_server_base { @@ -62,72 +60,39 @@ void OTARequestHandler::ota_init_(const char *filename) { this->ota_read_length_ = 0; } -void report_ota_error() { -#ifdef USE_ARDUINO - StreamString ss; - Update.printError(ss); - ESP_LOGW(TAG, "OTA Update failed! Error: %s", ss.c_str()); -#endif -} - void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final) { -#ifdef USE_ARDUINO - bool success; - if (index == 0) { - this->ota_init_(filename.c_str()); -#ifdef USE_ESP8266 - Update.runAsync(true); - // NOLINTNEXTLINE(readability-static-accessed-through-instance) - success = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000); -#endif -#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_LIBRETINY) - if (Update.isRunning()) { - Update.abort(); - } - success = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH); -#endif - if (!success) { - report_ota_error(); - return; - } - } else if (Update.hasError()) { - // don't spam logs with errors if something failed at start - return; - } - - success = Update.write(data, len) == len; - if (!success) { - report_ota_error(); - return; - } - this->ota_read_length_ += len; - this->report_ota_progress_(request); - - if (final) { - if (Update.end(true)) { - this->schedule_ota_reboot_(); - } else { - report_ota_error(); - } - } -#endif // USE_ARDUINO - -#ifdef USE_ESP_IDF - // ESP-IDF implementation using ota_base backend ota_base::OTAResponseTypes error_code = ota_base::OTA_RESPONSE_OK; if (index == 0 && !this->ota_backend_) { // Initialize OTA on first call this->ota_init_(filename.c_str()); + // Platform-specific pre-initialization +#ifdef USE_ARDUINO +#ifdef USE_ESP8266 + Update.runAsync(true); +#endif +#if defined(USE_ESP32_FRAMEWORK_ARDUINO) || defined(USE_LIBRETINY) + if (Update.isRunning()) { + Update.abort(); + } +#endif +#endif // USE_ARDUINO + this->ota_backend_ = ota_base::make_ota_backend(); if (!this->ota_backend_) { ESP_LOGE(TAG, "Failed to create OTA backend"); return; } - error_code = this->ota_backend_->begin(request->contentLength()); + size_t ota_size = request->contentLength(); + if (ota_size == 0) { + // For chunked encoding, we don't know the size + ota_size = UPDATE_SIZE_UNKNOWN; + } + + error_code = this->ota_backend_->begin(ota_size); if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGE(TAG, "OTA begin failed: %d", error_code); this->ota_backend_.reset(); @@ -162,25 +127,12 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin } this->ota_backend_.reset(); } -#endif // USE_ESP_IDF } void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) { AsyncWebServerResponse *response; -#ifdef USE_ARDUINO - if (!Update.hasError()) { - response = request->beginResponse(200, "text/plain", "Update Successful!"); - } else { - StreamString ss; - ss.print("Update Failed: "); - Update.printError(ss); - response = request->beginResponse(200, "text/plain", ss); - } -#endif // USE_ARDUINO -#ifdef USE_ESP_IDF // Send response based on whether backend still exists (error) or was reset (success) response = request->beginResponse(200, "text/plain", !this->ota_backend_ ? "Update Successful!" : "Update Failed!"); -#endif // USE_ESP_IDF response->addHeader("Connection", "close"); request->send(response); } From 8aa8af735d0d9401bc577f1ef36dfcb7ebdfa354 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:25:48 -0500 Subject: [PATCH 19/31] single ota path --- .../web_server_base/web_server_base.cpp | 33 ++++++++++++++++++- .../web_server_base/web_server_base.h | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index babef752d9..a11ce11e03 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -37,12 +37,17 @@ void WebServerBase::add_handler(AsyncWebHandler *handler) { void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) { const uint32_t now = millis(); if (now - this->last_ota_progress_ > 1000) { + float percentage = 0.0f; if (request->contentLength() != 0) { - float percentage = (this->ota_read_length_ * 100.0f) / request->contentLength(); + percentage = (this->ota_read_length_ * 100.0f) / request->contentLength(); ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage); } else { ESP_LOGD(TAG, "OTA in progress: %u bytes read", this->ota_read_length_); } +#ifdef USE_OTA_STATE_CALLBACK + // Report progress - use call_deferred since we're in web server task + this->parent_->state_callback_.call_deferred(ota_base::OTA_IN_PROGRESS, percentage, 0); +#endif this->last_ota_progress_ = now; } } @@ -68,6 +73,11 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin // Initialize OTA on first call this->ota_init_(filename.c_str()); +#ifdef USE_OTA_STATE_CALLBACK + // Notify OTA started - use call_deferred since we're in web server task + this->parent_->state_callback_.call_deferred(ota_base::OTA_STARTED, 0.0f, 0); +#endif + // Platform-specific pre-initialization #ifdef USE_ARDUINO #ifdef USE_ESP8266 @@ -83,6 +93,10 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin this->ota_backend_ = ota_base::make_ota_backend(); if (!this->ota_backend_) { ESP_LOGE(TAG, "Failed to create OTA backend"); +#ifdef USE_OTA_STATE_CALLBACK + this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, + static_cast(ota_base::OTA_RESPONSE_ERROR_UNKNOWN)); +#endif return; } @@ -96,6 +110,9 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGE(TAG, "OTA begin failed: %d", error_code); this->ota_backend_.reset(); +#ifdef USE_OTA_STATE_CALLBACK + this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, static_cast(error_code)); +#endif return; } } @@ -111,6 +128,9 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin ESP_LOGE(TAG, "OTA write failed: %d", error_code); this->ota_backend_->abort(); this->ota_backend_.reset(); +#ifdef USE_OTA_STATE_CALLBACK + this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, static_cast(error_code)); +#endif return; } this->ota_read_length_ += len; @@ -121,9 +141,16 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin if (final) { error_code = this->ota_backend_->end(); if (error_code == ota_base::OTA_RESPONSE_OK) { +#ifdef USE_OTA_STATE_CALLBACK + // Report completion before reboot - use call_deferred since we're in web server task + this->parent_->state_callback_.call_deferred(ota_base::OTA_COMPLETED, 100.0f, 0); +#endif this->schedule_ota_reboot_(); } else { ESP_LOGE(TAG, "OTA end failed: %d", error_code); +#ifdef USE_OTA_STATE_CALLBACK + this->parent_->state_callback_.call_deferred(ota_base::OTA_ERROR, 0.0f, static_cast(error_code)); +#endif } this->ota_backend_.reset(); } @@ -139,6 +166,10 @@ void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) { void WebServerBase::add_ota_handler() { this->add_handler(new OTARequestHandler(this)); // NOLINT +#ifdef USE_OTA_STATE_CALLBACK + // Register with global OTA callback system + ota_base::register_ota_platform(this); +#endif } #endif diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index b221d7b28d..ec6978181b 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -83,7 +83,7 @@ class AuthMiddlewareHandler : public MiddlewareHandler { } // namespace internal -class WebServerBase : public Component { +class WebServerBase : public Component, public ota_base::OTAComponent { public: void init() { if (this->initialized_) { From 681d9236f9972d071e5c19b599c2a32259bd9280 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:26:55 -0500 Subject: [PATCH 20/31] single ota path --- esphome/components/web_server_base/web_server_base.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index ec6978181b..3bf39d8eb7 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -83,7 +83,7 @@ class AuthMiddlewareHandler : public MiddlewareHandler { } // namespace internal -class WebServerBase : public Component, public ota_base::OTAComponent { +class WebServerBase : public ota_base::OTAComponent { public: void init() { if (this->initialized_) { From 31db6e51eb1e9bda4d3f66a80052e19fcc020ee6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:27:46 -0500 Subject: [PATCH 21/31] single ota path --- esphome/components/web_server_base/web_server_base.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index a11ce11e03..4bce5d6679 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -100,12 +100,8 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin return; } + // 0 means unknown size for ota_base backends (chunked encoding) size_t ota_size = request->contentLength(); - if (ota_size == 0) { - // For chunked encoding, we don't know the size - ota_size = UPDATE_SIZE_UNKNOWN; - } - error_code = this->ota_backend_->begin(ota_size); if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGE(TAG, "OTA begin failed: %d", error_code); From 1ff7cf11253b4d737dbdcb9d904e8db4d969a819 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:28:48 -0500 Subject: [PATCH 22/31] single ota path --- esphome/components/web_server_base/web_server_base.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 4bce5d6679..194b72d5fc 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -100,9 +100,10 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin return; } - // 0 means unknown size for ota_base backends (chunked encoding) - size_t ota_size = request->contentLength(); - error_code = this->ota_backend_->begin(ota_size); + // Web server OTA uses multipart uploads where the actual firmware size + // is unknown (contentLength includes multipart overhead) + // Pass 0 to indicate unknown size + error_code = this->ota_backend_->begin(0); if (error_code != ota_base::OTA_RESPONSE_OK) { ESP_LOGE(TAG, "OTA begin failed: %d", error_code); this->ota_backend_.reset(); From b88f87799e667016d8f6c2f123b160e62cc9d429 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:30:52 -0500 Subject: [PATCH 23/31] single ota path --- esphome/components/web_server_base/web_server_base.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 194b72d5fc..1503ee3bdd 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -39,6 +39,10 @@ void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) { if (now - this->last_ota_progress_ > 1000) { float percentage = 0.0f; if (request->contentLength() != 0) { + // Note: Using contentLength() for progress calculation is technically wrong as it includes + // multipart headers/boundaries, but it's only off by a small amount and we don't have + // access to the actual firmware size until the upload is complete. This is intentional + // as it still gives the user a reasonable progress indication. percentage = (this->ota_read_length_ * 100.0f) / request->contentLength(); ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage); } else { From ad628c9cbab5582348bbf37b24b773a5f4b0818d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:36:36 -0500 Subject: [PATCH 24/31] single ota path --- esphome/components/web_server_base/web_server_base.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 3bf39d8eb7..6f3a770e42 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -83,7 +83,11 @@ class AuthMiddlewareHandler : public MiddlewareHandler { } // namespace internal +#ifdef USE_WEBSERVER_OTA class WebServerBase : public ota_base::OTAComponent { +#else +class WebServerBase : public Component { +#endif public: void init() { if (this->initialized_) { From 149bdaf14680458b9a8d31dc062ea7c5c85ca988 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:50:17 -0500 Subject: [PATCH 25/31] fixes --- esphome/components/ota_base/ota_backend_arduino_esp32.cpp | 5 +++++ .../components/ota_base/ota_backend_arduino_libretiny.cpp | 5 +++++ esphome/components/ota_base/ota_backend_arduino_rp2040.cpp | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/esphome/components/ota_base/ota_backend_arduino_esp32.cpp b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp index 34ba3ae6ff..b89c61472a 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp @@ -15,6 +15,11 @@ static const char *const TAG = "ota.arduino_esp32"; std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { + // Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA + // where the exact firmware size is unknown due to multipart encoding + if (image_size == 0) { + image_size = UPDATE_SIZE_UNKNOWN; + } bool ret = Update.begin(image_size, U_FLASH); if (ret) { return OTA_RESPONSE_OK; diff --git a/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp index 12d4b677a3..052a9faed9 100644 --- a/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp @@ -15,6 +15,11 @@ static const char *const TAG = "ota.arduino_libretiny"; std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { + // Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA + // where the exact firmware size is unknown due to multipart encoding + if (image_size == 0) { + image_size = UPDATE_SIZE_UNKNOWN; + } bool ret = Update.begin(image_size, U_FLASH); if (ret) { return OTA_RESPONSE_OK; diff --git a/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp index 7276381919..bcb87f3547 100644 --- a/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp @@ -17,6 +17,11 @@ static const char *const TAG = "ota.arduino_rp2040"; std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { + // Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA + // where the exact firmware size is unknown due to multipart encoding + if (image_size == 0) { + image_size = UPDATE_SIZE_UNKNOWN; + } bool ret = Update.begin(image_size, U_FLASH); if (ret) { rp2040::preferences_prevent_write(true); From cd1390916c97909af5a246894f08221d0262f102 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 11:09:08 -0500 Subject: [PATCH 26/31] md5 fixes --- .../ota_base/ota_backend_arduino_esp32.cpp | 9 +++++++-- .../components/ota_base/ota_backend_arduino_esp32.h | 3 +++ .../ota_base/ota_backend_arduino_esp8266.cpp | 9 +++++++-- .../ota_base/ota_backend_arduino_esp8266.h | 3 +++ .../ota_base/ota_backend_arduino_libretiny.cpp | 9 +++++++-- .../ota_base/ota_backend_arduino_libretiny.h | 3 +++ .../ota_base/ota_backend_arduino_rp2040.cpp | 9 +++++++-- .../ota_base/ota_backend_arduino_rp2040.h | 3 +++ .../components/web_server_base/web_server_base.cpp | 13 +++++++++++-- .../components/web_server_base/web_server_base.h | 1 + 10 files changed, 52 insertions(+), 10 deletions(-) diff --git a/esphome/components/ota_base/ota_backend_arduino_esp32.cpp b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp index b89c61472a..f239544cfe 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp32.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.cpp @@ -34,7 +34,10 @@ OTAResponseTypes ArduinoESP32OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_UNKNOWN; } -void ArduinoESP32OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); } +void ArduinoESP32OTABackend::set_update_md5(const char *md5) { + Update.setMD5(md5); + this->md5_set_ = true; +} OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); @@ -49,7 +52,9 @@ OTAResponseTypes ArduinoESP32OTABackend::write(uint8_t *data, size_t len) { } OTAResponseTypes ArduinoESP32OTABackend::end() { - if (Update.end()) { + // Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5 + // This matches the behavior of the old web_server OTA implementation + if (Update.end(!this->md5_set_)) { return OTA_RESPONSE_OK; } diff --git a/esphome/components/ota_base/ota_backend_arduino_esp32.h b/esphome/components/ota_base/ota_backend_arduino_esp32.h index 6fb9454c64..e3966eb2f6 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp32.h +++ b/esphome/components/ota_base/ota_backend_arduino_esp32.h @@ -16,6 +16,9 @@ class ArduinoESP32OTABackend : public OTABackend { OTAResponseTypes end() override; void abort() override; bool supports_compression() override { return false; } + + private: + bool md5_set_{false}; }; } // namespace ota_base diff --git a/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp index 1133ce7b05..5df2ed0a58 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp @@ -43,7 +43,10 @@ OTAResponseTypes ArduinoESP8266OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_UNKNOWN; } -void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); } +void ArduinoESP8266OTABackend::set_update_md5(const char *md5) { + Update.setMD5(md5); + this->md5_set_ = true; +} OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); @@ -58,7 +61,9 @@ OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) { } OTAResponseTypes ArduinoESP8266OTABackend::end() { - if (Update.end()) { + // Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5 + // This matches the behavior of the old web_server OTA implementation + if (Update.end(!this->md5_set_)) { return OTA_RESPONSE_OK; } diff --git a/esphome/components/ota_base/ota_backend_arduino_esp8266.h b/esphome/components/ota_base/ota_backend_arduino_esp8266.h index 3f9982a514..f399013121 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp8266.h +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.h @@ -21,6 +21,9 @@ class ArduinoESP8266OTABackend : public OTABackend { #else bool supports_compression() override { return false; } #endif + + private: + bool md5_set_{false}; }; } // namespace ota_base diff --git a/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp index 052a9faed9..2596e3c2a3 100644 --- a/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.cpp @@ -34,7 +34,10 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_UNKNOWN; } -void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); } +void ArduinoLibreTinyOTABackend::set_update_md5(const char *md5) { + Update.setMD5(md5); + this->md5_set_ = true; +} OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); @@ -49,7 +52,9 @@ OTAResponseTypes ArduinoLibreTinyOTABackend::write(uint8_t *data, size_t len) { } OTAResponseTypes ArduinoLibreTinyOTABackend::end() { - if (Update.end()) { + // Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5 + // This matches the behavior of the old web_server OTA implementation + if (Update.end(!this->md5_set_)) { return OTA_RESPONSE_OK; } diff --git a/esphome/components/ota_base/ota_backend_arduino_libretiny.h b/esphome/components/ota_base/ota_backend_arduino_libretiny.h index b1cf1df738..33eebeb95a 100644 --- a/esphome/components/ota_base/ota_backend_arduino_libretiny.h +++ b/esphome/components/ota_base/ota_backend_arduino_libretiny.h @@ -15,6 +15,9 @@ class ArduinoLibreTinyOTABackend : public OTABackend { OTAResponseTypes end() override; void abort() override; bool supports_compression() override { return false; } + + private: + bool md5_set_{false}; }; } // namespace ota_base diff --git a/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp index bcb87f3547..589187f615 100644 --- a/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp @@ -43,7 +43,10 @@ OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_UNKNOWN; } -void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { Update.setMD5(md5); } +void ArduinoRP2040OTABackend::set_update_md5(const char *md5) { + Update.setMD5(md5); + this->md5_set_ = true; +} OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) { size_t written = Update.write(data, len); @@ -58,7 +61,9 @@ OTAResponseTypes ArduinoRP2040OTABackend::write(uint8_t *data, size_t len) { } OTAResponseTypes ArduinoRP2040OTABackend::end() { - if (Update.end()) { + // Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5 + // This matches the behavior of the old web_server OTA implementation + if (Update.end(!this->md5_set_)) { return OTA_RESPONSE_OK; } diff --git a/esphome/components/ota_base/ota_backend_arduino_rp2040.h b/esphome/components/ota_base/ota_backend_arduino_rp2040.h index fb6e90bb53..6d622d4a5a 100644 --- a/esphome/components/ota_base/ota_backend_arduino_rp2040.h +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.h @@ -17,6 +17,9 @@ class ArduinoRP2040OTABackend : public OTABackend { OTAResponseTypes end() override; void abort() override; bool supports_compression() override { return false; } + + private: + bool md5_set_{false}; }; } // namespace ota_base diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index 1503ee3bdd..a683ee85eb 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -67,6 +67,7 @@ void OTARequestHandler::schedule_ota_reboot_() { void OTARequestHandler::ota_init_(const char *filename) { ESP_LOGI(TAG, "OTA Update Start: %s", filename); this->ota_read_length_ = 0; + this->ota_success_ = false; } void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const String &filename, size_t index, @@ -140,8 +141,15 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin // Finalize if (final) { + ESP_LOGD(TAG, "OTA final chunk: index=%u, len=%u, total_read=%u, contentLength=%u", index, len, + this->ota_read_length_, request->contentLength()); + + // For Arduino framework, the Update library tracks expected size from firmware header + // If we haven't received enough data, calling end() will fail + // This can happen if the upload is interrupted or the client disconnects error_code = this->ota_backend_->end(); if (error_code == ota_base::OTA_RESPONSE_OK) { + this->ota_success_ = true; #ifdef USE_OTA_STATE_CALLBACK // Report completion before reboot - use call_deferred since we're in web server task this->parent_->state_callback_.call_deferred(ota_base::OTA_COMPLETED, 100.0f, 0); @@ -159,8 +167,9 @@ void OTARequestHandler::handleUpload(AsyncWebServerRequest *request, const Strin void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) { AsyncWebServerResponse *response; - // Send response based on whether backend still exists (error) or was reset (success) - response = request->beginResponse(200, "text/plain", !this->ota_backend_ ? "Update Successful!" : "Update Failed!"); + // Use the ota_success_ flag to determine the actual result + const char *msg = this->ota_success_ ? "Update Successful!" : "Update Failed!"; + response = request->beginResponse(200, "text/plain", msg); response->addHeader("Connection", "close"); request->send(response); } diff --git a/esphome/components/web_server_base/web_server_base.h b/esphome/components/web_server_base/web_server_base.h index 6f3a770e42..99087ddfa8 100644 --- a/esphome/components/web_server_base/web_server_base.h +++ b/esphome/components/web_server_base/web_server_base.h @@ -159,6 +159,7 @@ class OTARequestHandler : public AsyncWebHandler { uint32_t last_ota_progress_{0}; uint32_t ota_read_length_{0}; WebServerBase *parent_; + bool ota_success_{false}; private: std::unique_ptr ota_backend_{nullptr}; From 3470305d9d41837a8412eee0b4f6fdf247e6beed Mon Sep 17 00:00:00 2001 From: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> Date: Tue, 1 Jul 2025 12:22:41 -0400 Subject: [PATCH 27/31] [esp32] Remove IDF 4 support and clean up code (#9145) --- esphome/components/adc/adc_sensor.h | 7 +-- esphome/components/esp32/__init__.py | 28 ++++-------- esphome/components/esp32/core.cpp | 4 -- .../components/esp32_ble_tracker/__init__.py | 10 +---- .../components/ethernet/esp_eth_phy_jl1101.c | 16 ------- .../ethernet/ethernet_component.cpp | 17 ------- esphome/components/i2s_audio/__init__.py | 12 +---- esphome/components/i2s_audio/i2s_audio.cpp | 6 +-- .../improv_serial/improv_serial_component.cpp | 4 -- .../internal_temperature.cpp | 35 ++++----------- .../components/internal_temperature/sensor.py | 26 ----------- esphome/components/logger/logger_esp32.cpp | 2 - esphome/components/mdns/__init__.py | 6 +-- .../components/mqtt/mqtt_backend_esp32.cpp | 45 +------------------ esphome/components/opentherm/opentherm.cpp | 19 +++----- .../components/ota/ota_backend_esp_idf.cpp | 11 ----- esphome/components/psram/psram.cpp | 20 --------- .../display/pvvx_display.cpp | 4 -- .../components/rpi_dpi_rgb/rpi_dpi_rgb.cpp | 4 -- esphome/components/st7701s/st7701s.cpp | 4 -- .../uart/uart_component_esp_idf.cpp | 4 -- esphome/components/watchdog/watchdog.cpp | 4 -- esphome/components/wireguard/__init__.py | 13 +----- 23 files changed, 34 insertions(+), 267 deletions(-) diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 9ffb6cf856..28dfd2262c 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -15,8 +15,7 @@ namespace adc { #ifdef USE_ESP32 // clang-format off -#if (ESP_IDF_VERSION_MAJOR == 4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 7)) || \ - (ESP_IDF_VERSION_MAJOR == 5 && \ +#if (ESP_IDF_VERSION_MAJOR == 5 && \ ((ESP_IDF_VERSION_MINOR == 0 && ESP_IDF_VERSION_PATCH >= 5) || \ (ESP_IDF_VERSION_MINOR == 1 && ESP_IDF_VERSION_PATCH >= 3) || \ (ESP_IDF_VERSION_MINOR >= 2)) \ @@ -100,11 +99,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage adc1_channel_t channel1_{ADC1_CHANNEL_MAX}; adc2_channel_t channel2_{ADC2_CHANNEL_MAX}; bool autorange_{false}; -#if ESP_IDF_VERSION_MAJOR >= 5 esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {}; -#else - esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {}; -#endif // ESP_IDF_VERSION_MAJOR #endif // USE_ESP32 }; diff --git a/esphome/components/esp32/__init__.py b/esphome/components/esp32/__init__.py index 32323b7504..b4c7a4e05b 100644 --- a/esphome/components/esp32/__init__.py +++ b/esphome/components/esp32/__init__.py @@ -411,8 +411,8 @@ def _esp_idf_check_versions(value): version = cv.Version.parse(cv.version_number(value[CONF_VERSION])) source = value.get(CONF_SOURCE, None) - if version < cv.Version(4, 0, 0): - raise cv.Invalid("Only ESP-IDF 4.0+ is supported.") + if version < cv.Version(5, 0, 0): + raise cv.Invalid("Only ESP-IDF 5.0+ is supported.") # flag this for later *before* we set value[CONF_PLATFORM_VERSION] below has_platform_ver = CONF_PLATFORM_VERSION in value @@ -422,20 +422,15 @@ def _esp_idf_check_versions(value): ) if ( - (is_platformio := _platform_is_platformio(value[CONF_PLATFORM_VERSION])) - and version.major >= 5 - and version not in SUPPORTED_PLATFORMIO_ESP_IDF_5X - ): + is_platformio := _platform_is_platformio(value[CONF_PLATFORM_VERSION]) + ) and version not in SUPPORTED_PLATFORMIO_ESP_IDF_5X: raise cv.Invalid( f"ESP-IDF {str(version)} not supported by platformio/espressif32" ) if ( - version.major < 5 - or ( - version in SUPPORTED_PLATFORMIO_ESP_IDF_5X - and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X - ) + version in SUPPORTED_PLATFORMIO_ESP_IDF_5X + and version not in SUPPORTED_PIOARDUINO_ESP_IDF_5X ) and not has_platform_ver: raise cv.Invalid( f"ESP-IDF {value[CONF_VERSION]} may be supported by platformio/espressif32; please specify '{CONF_PLATFORM_VERSION}'" @@ -801,14 +796,9 @@ async def to_code(config): if advanced.get(CONF_IGNORE_EFUSE_MAC_CRC): add_idf_sdkconfig_option("CONFIG_ESP_MAC_IGNORE_MAC_CRC_ERROR", True) - if (framework_ver.major, framework_ver.minor) >= (4, 4): - add_idf_sdkconfig_option( - "CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False - ) - else: - add_idf_sdkconfig_option( - "CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE", False - ) + add_idf_sdkconfig_option( + "CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE", False + ) if advanced.get(CONF_ENABLE_IDF_EXPERIMENTAL_FEATURES): _LOGGER.warning( "Using experimental features in ESP-IDF may result in unexpected failures." diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index 562bcba3c2..f3bdfea2a0 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -56,11 +56,7 @@ void arch_init() { void IRAM_ATTR HOT arch_feed_wdt() { esp_task_wdt_reset(); } uint8_t progmem_read_byte(const uint8_t *addr) { return *addr; } -#if ESP_IDF_VERSION_MAJOR >= 5 uint32_t arch_get_cpu_cycle_count() { return esp_cpu_get_cycle_count(); } -#else -uint32_t arch_get_cpu_cycle_count() { return cpu_hal_get_cycle_count(); } -#endif uint32_t arch_get_cpu_freq_hz() { uint32_t freq = 0; #ifdef USE_ESP_IDF diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 2242d709a4..547cf84ed1 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -29,8 +29,6 @@ from esphome.const import ( CONF_ON_BLE_SERVICE_DATA_ADVERTISE, CONF_SERVICE_UUID, CONF_TRIGGER_ID, - KEY_CORE, - KEY_FRAMEWORK_VERSION, ) from esphome.core import CORE @@ -323,10 +321,7 @@ async def to_code(config): # https://github.com/espressif/esp-idf/issues/2503 # Match arduino CONFIG_BTU_TASK_STACK_SIZE # https://github.com/espressif/arduino-esp32/blob/fd72cf46ad6fc1a6de99c1d83ba8eba17d80a4ee/tools/sdk/esp32/sdkconfig#L1866 - if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(4, 4, 6): - add_idf_sdkconfig_option("CONFIG_BT_BTU_TASK_STACK_SIZE", 8192) - else: - add_idf_sdkconfig_option("CONFIG_BTU_TASK_STACK_SIZE", 8192) + add_idf_sdkconfig_option("CONFIG_BT_BTU_TASK_STACK_SIZE", 8192) add_idf_sdkconfig_option("CONFIG_BT_ACL_CONNECTIONS", 9) add_idf_sdkconfig_option( "CONFIG_BTDM_CTRL_BLE_MAX_CONN", config[CONF_MAX_CONNECTIONS] @@ -335,8 +330,7 @@ async def to_code(config): # max notifications in 5.x, setting CONFIG_BT_ACL_CONNECTIONS # is enough in 4.x # https://github.com/esphome/issues/issues/6808 - if CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version(5, 0, 0): - add_idf_sdkconfig_option("CONFIG_BT_GATTC_NOTIF_REG_MAX", 9) + add_idf_sdkconfig_option("CONFIG_BT_GATTC_NOTIF_REG_MAX", 9) 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/ethernet/esp_eth_phy_jl1101.c b/esphome/components/ethernet/esp_eth_phy_jl1101.c index de2a6f4f35..4f31e0a9fd 100644 --- a/esphome/components/ethernet/esp_eth_phy_jl1101.c +++ b/esphome/components/ethernet/esp_eth_phy_jl1101.c @@ -19,11 +19,7 @@ #include #include "esp_log.h" #include "esp_eth.h" -#if ESP_IDF_VERSION_MAJOR >= 5 #include "esp_eth_phy_802_3.h" -#else -#include "eth_phy_regs_struct.h" -#endif #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/gpio.h" @@ -174,11 +170,7 @@ static esp_err_t jl1101_reset_hw(esp_eth_phy_t *phy) { return ESP_OK; } -#if ESP_IDF_VERSION_MAJOR >= 5 static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy, eth_phy_autoneg_cmd_t cmd, bool *nego_state) { -#else -static esp_err_t jl1101_negotiate(esp_eth_phy_t *phy) { -#endif phy_jl1101_t *jl1101 = __containerof(phy, phy_jl1101_t, parent); esp_eth_mediator_t *eth = jl1101->eth; /* in case any link status has changed, let's assume we're in link down status */ @@ -293,11 +285,7 @@ static esp_err_t jl1101_init(esp_eth_phy_t *phy) { esp_eth_mediator_t *eth = jl1101->eth; // Detect PHY address if (jl1101->addr == ESP_ETH_PHY_ADDR_AUTO) { -#if ESP_IDF_VERSION_MAJOR >= 5 PHY_CHECK(esp_eth_phy_802_3_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err); -#else - PHY_CHECK(esp_eth_detect_phy_addr(eth, &jl1101->addr) == ESP_OK, "Detect PHY address failed", err); -#endif } /* Power on Ethernet PHY */ PHY_CHECK(jl1101_pwrctl(phy, true) == ESP_OK, "power control failed", err); @@ -336,11 +324,7 @@ esp_eth_phy_t *esp_eth_phy_new_jl1101(const eth_phy_config_t *config) { jl1101->parent.init = jl1101_init; jl1101->parent.deinit = jl1101_deinit; jl1101->parent.set_mediator = jl1101_set_mediator; -#if ESP_IDF_VERSION_MAJOR >= 5 jl1101->parent.autonego_ctrl = jl1101_negotiate; -#else - jl1101->parent.negotiate = jl1101_negotiate; -#endif jl1101->parent.get_link = jl1101_get_link; jl1101->parent.pwrctl = jl1101_pwrctl; jl1101->parent.get_addr = jl1101_get_addr; diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 19a11c6945..f8c2f3a72e 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -122,25 +122,12 @@ void EthernetComponent::setup() { .post_cb = nullptr, }; -#if ESP_IDF_VERSION_MAJOR >= 5 #if CONFIG_ETH_SPI_ETHERNET_W5500 eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg); #endif #if CONFIG_ETH_SPI_ETHERNET_DM9051 eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(host, &devcfg); #endif -#else - spi_device_handle_t spi_handle = nullptr; - err = spi_bus_add_device(host, &devcfg, &spi_handle); - ESPHL_ERROR_CHECK(err, "SPI bus add device error"); - -#if CONFIG_ETH_SPI_ETHERNET_W5500 - eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); -#endif -#if CONFIG_ETH_SPI_ETHERNET_DM9051 - eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle); -#endif -#endif // ESP_IDF_VERSION_MAJOR >= 5 #if CONFIG_ETH_SPI_ETHERNET_W5500 w5500_config.int_gpio_num = this->interrupt_pin_; @@ -211,11 +198,7 @@ void EthernetComponent::setup() { } case ETHERNET_TYPE_KSZ8081: case ETHERNET_TYPE_KSZ8081RNA: { -#if ESP_IDF_VERSION_MAJOR >= 5 this->phy_ = esp_eth_phy_new_ksz80xx(&phy_config); -#else - this->phy_ = esp_eth_phy_new_ksz8081(&phy_config); -#endif break; } #endif diff --git a/esphome/components/i2s_audio/__init__.py b/esphome/components/i2s_audio/__init__.py index ef95fd0b41..9a2aa0362f 100644 --- a/esphome/components/i2s_audio/__init__.py +++ b/esphome/components/i2s_audio/__init__.py @@ -9,14 +9,7 @@ from esphome.components.esp32.const import ( VARIANT_ESP32S3, ) import esphome.config_validation as cv -from esphome.const import ( - CONF_BITS_PER_SAMPLE, - CONF_CHANNEL, - CONF_ID, - CONF_SAMPLE_RATE, - KEY_CORE, - KEY_FRAMEWORK_VERSION, -) +from esphome.const import CONF_BITS_PER_SAMPLE, CONF_CHANNEL, CONF_ID, CONF_SAMPLE_RATE from esphome.core import CORE from esphome.cpp_generator import MockObjClass import esphome.final_validate as fv @@ -250,8 +243,7 @@ def _final_validate(_): def use_legacy(): - framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] - if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0): + if CORE.using_esp_idf: if not _use_legacy_driver: return False return True diff --git a/esphome/components/i2s_audio/i2s_audio.cpp b/esphome/components/i2s_audio/i2s_audio.cpp index 7ff21bba57..7f233516e6 100644 --- a/esphome/components/i2s_audio/i2s_audio.cpp +++ b/esphome/components/i2s_audio/i2s_audio.cpp @@ -9,15 +9,11 @@ namespace i2s_audio { static const char *const TAG = "i2s_audio"; -#if ESP_IDF_VERSION_MAJOR >= 5 -static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :( -#endif - void I2SAudioComponent::setup() { ESP_LOGCONFIG(TAG, "Running setup"); static i2s_port_t next_port_num = I2S_NUM_0; - if (next_port_num >= I2S_NUM_MAX) { + if (next_port_num >= SOC_I2S_NUM) { ESP_LOGE(TAG, "Too many components"); this->mark_failed(); return; diff --git a/esphome/components/improv_serial/improv_serial_component.cpp b/esphome/components/improv_serial/improv_serial_component.cpp index c3a0f2eacc..ae4927828b 100644 --- a/esphome/components/improv_serial/improv_serial_component.cpp +++ b/esphome/components/improv_serial/improv_serial_component.cpp @@ -59,11 +59,7 @@ optional ImprovSerialComponent::read_byte_() { break; #if defined(USE_LOGGER_USB_CDC) && defined(CONFIG_ESP_CONSOLE_USB_CDC) case logger::UART_SELECTION_USB_CDC: -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) if (esp_usb_console_available_for_read()) { -#else - if (esp_usb_console_read_available()) { -#endif esp_usb_console_read_buf((char *) &data, 1); byte = data; } diff --git a/esphome/components/internal_temperature/internal_temperature.cpp b/esphome/components/internal_temperature/internal_temperature.cpp index f503927d8e..85844647f2 100644 --- a/esphome/components/internal_temperature/internal_temperature.cpp +++ b/esphome/components/internal_temperature/internal_temperature.cpp @@ -10,11 +10,7 @@ uint8_t temprature_sens_read(); #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \ defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32P4) -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) -#include "driver/temp_sensor.h" -#else #include "driver/temperature_sensor.h" -#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #endif // USE_ESP32_VARIANT #endif // USE_ESP32 #ifdef USE_RP2040 @@ -31,12 +27,11 @@ namespace internal_temperature { static const char *const TAG = "internal_temperature"; #ifdef USE_ESP32 -#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \ - (defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \ - defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \ - defined(USE_ESP32_VARIANT_ESP32P4)) +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \ + defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \ + defined(USE_ESP32_VARIANT_ESP32P4) static temperature_sensor_handle_t tsensNew = NULL; -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT +#endif // USE_ESP32_VARIANT #endif // USE_ESP32 void InternalTemperatureSensor::update() { @@ -51,24 +46,11 @@ void InternalTemperatureSensor::update() { #elif defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || \ defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || \ defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32P4) -#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) - temp_sensor_config_t tsens = TSENS_CONFIG_DEFAULT(); - temp_sensor_set_config(tsens); - temp_sensor_start(); -#if defined(USE_ESP32_VARIANT_ESP32S3) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 3)) -#error \ - "ESP32-S3 internal temperature sensor requires ESP IDF V4.4.3 or higher. See https://github.com/esphome/issues/issues/4271" -#endif - esp_err_t result = temp_sensor_read_celsius(&temperature); - temp_sensor_stop(); - success = (result == ESP_OK); -#else esp_err_t result = temperature_sensor_get_celsius(tsensNew, &temperature); success = (result == ESP_OK); if (!success) { ESP_LOGE(TAG, "Reading failed (%d)", result); } -#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) #endif // USE_ESP32_VARIANT #endif // USE_ESP32 #ifdef USE_RP2040 @@ -99,10 +81,9 @@ void InternalTemperatureSensor::update() { void InternalTemperatureSensor::setup() { #ifdef USE_ESP32 -#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) && \ - (defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \ - defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \ - defined(USE_ESP32_VARIANT_ESP32P4)) +#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32S2) || \ + defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32H2) || defined(USE_ESP32_VARIANT_ESP32C2) || \ + defined(USE_ESP32_VARIANT_ESP32P4) ESP_LOGCONFIG(TAG, "Running setup"); temperature_sensor_config_t tsens_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80); @@ -120,7 +101,7 @@ void InternalTemperatureSensor::setup() { this->mark_failed(); return; } -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && USE_ESP32_VARIANT +#endif // USE_ESP32_VARIANT #endif // USE_ESP32 } diff --git a/esphome/components/internal_temperature/sensor.py b/esphome/components/internal_temperature/sensor.py index 9bfa3739c8..93b98a30f4 100644 --- a/esphome/components/internal_temperature/sensor.py +++ b/esphome/components/internal_temperature/sensor.py @@ -1,46 +1,21 @@ import esphome.codegen as cg from esphome.components import sensor -from esphome.components.esp32 import get_esp32_variant -from esphome.components.esp32.const import VARIANT_ESP32S3 import esphome.config_validation as cv from esphome.const import ( DEVICE_CLASS_TEMPERATURE, ENTITY_CATEGORY_DIAGNOSTIC, - KEY_CORE, - KEY_FRAMEWORK_VERSION, PLATFORM_BK72XX, PLATFORM_ESP32, PLATFORM_RP2040, STATE_CLASS_MEASUREMENT, UNIT_CELSIUS, ) -from esphome.core import CORE internal_temperature_ns = cg.esphome_ns.namespace("internal_temperature") InternalTemperatureSensor = internal_temperature_ns.class_( "InternalTemperatureSensor", sensor.Sensor, cg.PollingComponent ) - -def validate_config(config): - if CORE.is_esp32: - variant = get_esp32_variant() - if variant == VARIANT_ESP32S3: - if CORE.using_arduino and CORE.data[KEY_CORE][ - KEY_FRAMEWORK_VERSION - ] < cv.Version(2, 0, 6): - raise cv.Invalid( - "ESP32-S3 Internal Temperature Sensor requires framework version 2.0.6 or higher. See ." - ) - if CORE.using_esp_idf and CORE.data[KEY_CORE][ - KEY_FRAMEWORK_VERSION - ] < cv.Version(4, 4, 3): - raise cv.Invalid( - "ESP32-S3 Internal Temperature Sensor requires framework version 4.4.3 or higher. See ." - ) - return config - - CONFIG_SCHEMA = cv.All( sensor.sensor_schema( InternalTemperatureSensor, @@ -51,7 +26,6 @@ CONFIG_SCHEMA = cv.All( entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ).extend(cv.polling_component_schema("60s")), cv.only_on([PLATFORM_ESP32, PLATFORM_RP2040, PLATFORM_BK72XX]), - validate_config, ) diff --git a/esphome/components/logger/logger_esp32.cpp b/esphome/components/logger/logger_esp32.cpp index b5ac84a665..41445fa3b4 100644 --- a/esphome/components/logger/logger_esp32.cpp +++ b/esphome/components/logger/logger_esp32.cpp @@ -83,9 +83,7 @@ void init_uart(uart_port_t uart_num, uint32_t baud_rate, int tx_buffer_size) { uart_config.parity = UART_PARITY_DISABLE; uart_config.stop_bits = UART_STOP_BITS_1; uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) uart_config.source_clk = UART_SCLK_DEFAULT; -#endif uart_param_config(uart_num, &uart_config); const int uart_buffer_size = tx_buffer_size; // Install UART driver using an event queue here diff --git a/esphome/components/mdns/__init__.py b/esphome/components/mdns/__init__.py index 2f81068e8a..ed230d43aa 100644 --- a/esphome/components/mdns/__init__.py +++ b/esphome/components/mdns/__init__.py @@ -8,8 +8,6 @@ from esphome.const import ( CONF_PROTOCOL, CONF_SERVICE, CONF_SERVICES, - KEY_CORE, - KEY_FRAMEWORK_VERSION, ) from esphome.core import CORE, coroutine_with_priority @@ -85,9 +83,7 @@ async def to_code(config): elif CORE.is_rp2040: cg.add_library("LEAmDNS", None) - if CORE.using_esp_idf and CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version( - 5, 0, 0 - ): + if CORE.using_esp_idf: add_idf_component(name="espressif/mdns", ref="1.8.2") cg.add_define("USE_MDNS") diff --git a/esphome/components/mqtt/mqtt_backend_esp32.cpp b/esphome/components/mqtt/mqtt_backend_esp32.cpp index 4648e66e1d..a096408aa5 100644 --- a/esphome/components/mqtt/mqtt_backend_esp32.cpp +++ b/esphome/components/mqtt/mqtt_backend_esp32.cpp @@ -14,49 +14,6 @@ namespace mqtt { static const char *const TAG = "mqtt.idf"; bool MQTTBackendESP32::initialize_() { -#if ESP_IDF_VERSION_MAJOR < 5 - mqtt_cfg_.user_context = (void *) this; - mqtt_cfg_.buffer_size = MQTT_BUFFER_SIZE; - - mqtt_cfg_.host = this->host_.c_str(); - mqtt_cfg_.port = this->port_; - mqtt_cfg_.keepalive = this->keep_alive_; - mqtt_cfg_.disable_clean_session = !this->clean_session_; - - if (!this->username_.empty()) { - mqtt_cfg_.username = this->username_.c_str(); - if (!this->password_.empty()) { - mqtt_cfg_.password = this->password_.c_str(); - } - } - - if (!this->lwt_topic_.empty()) { - mqtt_cfg_.lwt_topic = this->lwt_topic_.c_str(); - this->mqtt_cfg_.lwt_qos = this->lwt_qos_; - this->mqtt_cfg_.lwt_retain = this->lwt_retain_; - - if (!this->lwt_message_.empty()) { - mqtt_cfg_.lwt_msg = this->lwt_message_.c_str(); - mqtt_cfg_.lwt_msg_len = this->lwt_message_.size(); - } - } - - if (!this->client_id_.empty()) { - mqtt_cfg_.client_id = this->client_id_.c_str(); - } - if (ca_certificate_.has_value()) { - mqtt_cfg_.cert_pem = ca_certificate_.value().c_str(); - mqtt_cfg_.skip_cert_common_name_check = skip_cert_cn_check_; - mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_SSL; - - if (this->cl_certificate_.has_value() && this->cl_key_.has_value()) { - mqtt_cfg_.client_cert_pem = this->cl_certificate_.value().c_str(); - mqtt_cfg_.client_key_pem = this->cl_key_.value().c_str(); - } - } else { - mqtt_cfg_.transport = MQTT_TRANSPORT_OVER_TCP; - } -#else mqtt_cfg_.broker.address.hostname = this->host_.c_str(); mqtt_cfg_.broker.address.port = this->port_; mqtt_cfg_.session.keepalive = this->keep_alive_; @@ -95,7 +52,7 @@ bool MQTTBackendESP32::initialize_() { } else { mqtt_cfg_.broker.address.transport = MQTT_TRANSPORT_OVER_TCP; } -#endif + auto *mqtt_client = esp_mqtt_client_init(&mqtt_cfg_); if (mqtt_client) { handler_.reset(mqtt_client); diff --git a/esphome/components/opentherm/opentherm.cpp b/esphome/components/opentherm/opentherm.cpp index 49482316ee..b2751470b2 100644 --- a/esphome/components/opentherm/opentherm.cpp +++ b/esphome/components/opentherm/opentherm.cpp @@ -272,18 +272,13 @@ bool OpenTherm::init_esp32_timer_() { this->timer_idx_ = timer_idx; timer_config_t const config = { - .alarm_en = TIMER_ALARM_EN, - .counter_en = TIMER_PAUSE, - .intr_type = TIMER_INTR_LEVEL, - .counter_dir = TIMER_COUNT_UP, - .auto_reload = TIMER_AUTORELOAD_EN, -#if ESP_IDF_VERSION_MAJOR >= 5 - .clk_src = TIMER_SRC_CLK_DEFAULT, -#endif - .divider = 80, -#if defined(SOC_TIMER_GROUP_SUPPORT_XTAL) && ESP_IDF_VERSION_MAJOR < 5 - .clk_src = TIMER_SRC_CLK_APB -#endif + .alarm_en = TIMER_ALARM_EN, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL, + .counter_dir = TIMER_COUNT_UP, + .auto_reload = TIMER_AUTORELOAD_EN, + .clk_src = TIMER_SRC_CLK_DEFAULT, + .divider = 80, }; esp_err_t result; diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index 6f45fb75e4..cad44a5795 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -6,10 +6,7 @@ #include #include - -#if ESP_IDF_VERSION_MAJOR >= 5 #include -#endif namespace esphome { namespace ota { @@ -24,7 +21,6 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) { #if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15 // The following function takes longer than the 5 seconds timeout of WDT -#if ESP_IDF_VERSION_MAJOR >= 5 esp_task_wdt_config_t wdtc; wdtc.idle_core_mask = 0; #if CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 @@ -36,21 +32,14 @@ OTAResponseTypes IDFOTABackend::begin(size_t image_size) { wdtc.timeout_ms = 15000; wdtc.trigger_panic = false; esp_task_wdt_reconfigure(&wdtc); -#else - esp_task_wdt_init(15, false); -#endif #endif esp_err_t err = esp_ota_begin(this->partition_, image_size, &this->update_handle_); #if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15 // Set the WDT back to the configured timeout -#if ESP_IDF_VERSION_MAJOR >= 5 wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000; esp_task_wdt_reconfigure(&wdtc); -#else - esp_task_wdt_init(CONFIG_ESP_TASK_WDT_TIMEOUT_S, false); -#endif #endif if (err != ESP_OK) { diff --git a/esphome/components/psram/psram.cpp b/esphome/components/psram/psram.cpp index 162543545e..6c110a577d 100644 --- a/esphome/components/psram/psram.cpp +++ b/esphome/components/psram/psram.cpp @@ -2,9 +2,7 @@ #ifdef USE_ESP32 #include "psram.h" #include -#if defined(USE_ESP_IDF) && ESP_IDF_VERSION_MAJOR >= 5 #include -#endif // USE_ESP_IDF #include "esphome/core/log.h" @@ -16,7 +14,6 @@ static const char *const TAG = "psram"; void PsramComponent::dump_config() { ESP_LOGCONFIG(TAG, "PSRAM:"); -#if defined(USE_ESP_IDF) && ESP_IDF_VERSION_MAJOR >= 5 bool available = esp_psram_is_initialized(); ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available)); @@ -26,23 +23,6 @@ void PsramComponent::dump_config() { ESP_LOGCONFIG(TAG, " ECC enabled: YES"); #endif } -#else - // Technically this can be false if the PSRAM is full, but heap_caps_get_total_size() isn't always available, and it's - // very unlikely for the PSRAM to be full. - bool available = heap_caps_get_free_size(MALLOC_CAP_SPIRAM) > 0; - ESP_LOGCONFIG(TAG, " Available: %s", YESNO(available)); - - if (available) { - const size_t psram_total_size_bytes = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); - const float psram_total_size_kb = psram_total_size_bytes / 1024.0f; - - if (abs(std::round(psram_total_size_kb) - psram_total_size_kb) < 0.05f) { - ESP_LOGCONFIG(TAG, " Size: %.0f KB", psram_total_size_kb); - } else { - ESP_LOGCONFIG(TAG, " Size: %zu bytes", psram_total_size_bytes); - } - } -#endif // USE_ESP_IDF } } // namespace psram diff --git a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp index 74f63a9640..4b6c11b332 100644 --- a/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp +++ b/esphome/components/pvvx_mithermometer/display/pvvx_display.cpp @@ -146,11 +146,7 @@ void PVVXDisplay::sync_time_() { } time.recalc_timestamp_utc(true); // calculate timestamp of local time uint8_t blk[5] = {}; -#if ESP_IDF_VERSION_MAJOR >= 5 ESP_LOGD(TAG, "[%s] Sync time with timestamp %" PRIu64 ".", this->parent_->address_str().c_str(), time.timestamp); -#else - ESP_LOGD(TAG, "[%s] Sync time with timestamp %lu.", this->parent_->address_str().c_str(), time.timestamp); -#endif blk[0] = 0x23; blk[1] = time.timestamp & 0xff; blk[2] = (time.timestamp >> 8) & 0xff; diff --git a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp index 666bac354d..91eb947a3e 100644 --- a/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp +++ b/esphome/components/rpi_dpi_rgb/rpi_dpi_rgb.cpp @@ -10,10 +10,8 @@ void RpiDpiRgb::setup() { this->reset_display_(); esp_lcd_rgb_panel_config_t config{}; config.flags.fb_in_psram = 1; -#if ESP_IDF_VERSION_MAJOR >= 5 config.bounce_buffer_size_px = this->width_ * 10; config.num_fbs = 1; -#endif // ESP_IDF_VERSION_MAJOR config.timings.h_res = this->width_; config.timings.v_res = this->height_; config.timings.hsync_pulse_width = this->hsync_pulse_width_; @@ -47,10 +45,8 @@ void RpiDpiRgb::setup() { ESP_LOGCONFIG(TAG, "RPI_DPI_RGB setup complete"); } void RpiDpiRgb::loop() { -#if ESP_IDF_VERSION_MAJOR >= 5 if (this->handle_ != nullptr) esp_lcd_rgb_panel_restart(this->handle_); -#endif // ESP_IDF_VERSION_MAJOR } void RpiDpiRgb::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, diff --git a/esphome/components/st7701s/st7701s.cpp b/esphome/components/st7701s/st7701s.cpp index 6261f33b77..46509a7f9f 100644 --- a/esphome/components/st7701s/st7701s.cpp +++ b/esphome/components/st7701s/st7701s.cpp @@ -12,10 +12,8 @@ void ST7701S::setup() { esp_lcd_rgb_panel_config_t config{}; config.flags.fb_in_psram = 1; -#if ESP_IDF_VERSION_MAJOR >= 5 config.bounce_buffer_size_px = this->width_ * 10; config.num_fbs = 1; -#endif // ESP_IDF_VERSION_MAJOR config.timings.h_res = this->width_; config.timings.v_res = this->height_; config.timings.hsync_pulse_width = this->hsync_pulse_width_; @@ -48,10 +46,8 @@ void ST7701S::setup() { } void ST7701S::loop() { -#if ESP_IDF_VERSION_MAJOR >= 5 if (this->handle_ != nullptr) esp_lcd_rgb_panel_restart(this->handle_); -#endif // ESP_IDF_VERSION_MAJOR } void ST7701S::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order, diff --git a/esphome/components/uart/uart_component_esp_idf.cpp b/esphome/components/uart/uart_component_esp_idf.cpp index 8fae63a603..63b2579c3f 100644 --- a/esphome/components/uart/uart_component_esp_idf.cpp +++ b/esphome/components/uart/uart_component_esp_idf.cpp @@ -48,11 +48,7 @@ uart_config_t IDFUARTComponent::get_config_() { uart_config.parity = parity; uart_config.stop_bits = this->stop_bits_ == 1 ? UART_STOP_BITS_1 : UART_STOP_BITS_2; uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) uart_config.source_clk = UART_SCLK_DEFAULT; -#else - uart_config.source_clk = UART_SCLK_APB; -#endif uart_config.rx_flow_ctrl_thresh = 122; return uart_config; diff --git a/esphome/components/watchdog/watchdog.cpp b/esphome/components/watchdog/watchdog.cpp index f6f2992a11..2ce46756e4 100644 --- a/esphome/components/watchdog/watchdog.cpp +++ b/esphome/components/watchdog/watchdog.cpp @@ -38,16 +38,12 @@ WatchdogManager::~WatchdogManager() { void WatchdogManager::set_timeout_(uint32_t timeout_ms) { ESP_LOGV(TAG, "Adjusting WDT to %" PRIu32 "ms", timeout_ms); #ifdef USE_ESP32 -#if ESP_IDF_VERSION_MAJOR >= 5 esp_task_wdt_config_t wdt_config = { .timeout_ms = timeout_ms, .idle_core_mask = (1 << SOC_CPU_CORES_NUM) - 1, .trigger_panic = true, }; esp_task_wdt_reconfigure(&wdt_config); -#else - esp_task_wdt_init(timeout_ms / 1000, true); -#endif // ESP_IDF_VERSION_MAJOR #endif // USE_ESP32 #ifdef USE_RP2040 diff --git a/esphome/components/wireguard/__init__.py b/esphome/components/wireguard/__init__.py index fc0e4e0538..8eff8e7b2a 100644 --- a/esphome/components/wireguard/__init__.py +++ b/esphome/components/wireguard/__init__.py @@ -6,14 +6,7 @@ import esphome.codegen as cg from esphome.components import time from esphome.components.esp32 import CORE, add_idf_sdkconfig_option import esphome.config_validation as cv -from esphome.const import ( - CONF_ADDRESS, - CONF_ID, - CONF_REBOOT_TIMEOUT, - CONF_TIME_ID, - KEY_CORE, - KEY_FRAMEWORK_VERSION, -) +from esphome.const import CONF_ADDRESS, CONF_ID, CONF_REBOOT_TIMEOUT, CONF_TIME_ID from esphome.core import TimePeriod CONF_NETMASK = "netmask" @@ -125,9 +118,7 @@ async def to_code(config): # Workaround for crash on IDF 5+ # See https://github.com/trombik/esp_wireguard/issues/33#issuecomment-1568503651 - if CORE.using_esp_idf and CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] >= cv.Version( - 5, 0, 0 - ): + if CORE.using_esp_idf: add_idf_sdkconfig_option("CONFIG_LWIP_PPP_SUPPORT", True) # This flag is added here because the esp_wireguard library statically From 825d0bed88fd7ce3aa6bef4ef0fd369d1220ba66 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 11:29:38 -0500 Subject: [PATCH 28/31] fix esp8266 error handling --- .../ota_base/ota_backend_arduino_esp8266.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp index 5df2ed0a58..a9d48b59df 100644 --- a/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_esp8266.cpp @@ -63,13 +63,17 @@ OTAResponseTypes ArduinoESP8266OTABackend::write(uint8_t *data, size_t len) { OTAResponseTypes ArduinoESP8266OTABackend::end() { // Use strict validation (false) when MD5 is set, lenient validation (true) when no MD5 // This matches the behavior of the old web_server OTA implementation - if (Update.end(!this->md5_set_)) { + bool success = Update.end(!this->md5_set_); + + // On ESP8266, Update.end() might return false even with error code 0 + // Check the actual error code to determine success + uint8_t error = Update.getError(); + + if (success || error == UPDATE_ERROR_OK) { return OTA_RESPONSE_OK; } - uint8_t error = Update.getError(); ESP_LOGE(TAG, "End error: %d", error); - return OTA_RESPONSE_ERROR_UPDATE_END; } From efafabed97983aa5c7ee556e7fb600ffd54e057e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 13:23:24 -0500 Subject: [PATCH 29/31] fix rp2040 --- .../ota_base/ota_backend_arduino_rp2040.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp index 589187f615..160c529231 100644 --- a/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp +++ b/esphome/components/ota_base/ota_backend_arduino_rp2040.cpp @@ -17,10 +17,16 @@ static const char *const TAG = "ota.arduino_rp2040"; std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) { - // Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA - // where the exact firmware size is unknown due to multipart encoding + // Handle UPDATE_SIZE_UNKNOWN (0) by calculating available space if (image_size == 0) { - image_size = UPDATE_SIZE_UNKNOWN; + // Similar to ESP8266, calculate available space from flash layout + extern uint8_t _FS_start; + extern uint8_t _FS_end; + // Calculate the size of the filesystem area which will be used for OTA + size_t fs_size = &_FS_end - &_FS_start; + // Reserve some space for filesystem overhead + image_size = (fs_size - 0x1000) & 0xFFFFF000; + ESP_LOGD(TAG, "OTA size unknown, using filesystem size: %u bytes", image_size); } bool ret = Update.begin(image_size, U_FLASH); if (ret) { From 099474053ebca7ad13a5c583480bbee96fb6e7ff Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 13:38:47 -0500 Subject: [PATCH 30/31] cleanuip --- esphome/components/ota/automation.h | 1 - esphome/components/ota/ota.cpp | 10 ---------- esphome/components/ota/ota.h | 12 ------------ 3 files changed, 23 deletions(-) delete mode 100644 esphome/components/ota/ota.cpp delete mode 100644 esphome/components/ota/ota.h diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h index 2dbf0c70e1..5c71859d43 100644 --- a/esphome/components/ota/automation.h +++ b/esphome/components/ota/automation.h @@ -1,6 +1,5 @@ #pragma once #ifdef USE_OTA_STATE_CALLBACK -#include "ota.h" #include "esphome/components/ota_base/ota_backend.h" #include "esphome/core/automation.h" diff --git a/esphome/components/ota/ota.cpp b/esphome/components/ota/ota.cpp deleted file mode 100644 index 47fda17be8..0000000000 --- a/esphome/components/ota/ota.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "ota.h" - -namespace esphome { -namespace ota { - -// All functionality has been moved to ota_base -// This file remains for backward compatibility - -} // namespace ota -} // namespace esphome diff --git a/esphome/components/ota/ota.h b/esphome/components/ota/ota.h deleted file mode 100644 index 141f99c87b..0000000000 --- a/esphome/components/ota/ota.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "esphome/core/defines.h" - -namespace esphome { -namespace ota { - -// All OTA backend functionality has been moved to the ota_base component. -// This file remains for the high-level OTA automation triggers defined in automation.h - -} // namespace ota -} // namespace esphome From 9799a2b63622e135c01b7992f303128230a268d5 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 13:47:59 -0500 Subject: [PATCH 31/31] test --- tests/components/ota_base/common.yaml | 10 ++++++++++ tests/components/ota_base/test.esp32-idf.yaml | 1 + 2 files changed, 11 insertions(+) create mode 100644 tests/components/ota_base/common.yaml create mode 100644 tests/components/ota_base/test.esp32-idf.yaml diff --git a/tests/components/ota_base/common.yaml b/tests/components/ota_base/common.yaml new file mode 100644 index 0000000000..9b680b7c18 --- /dev/null +++ b/tests/components/ota_base/common.yaml @@ -0,0 +1,10 @@ +# Test that ota_base compiles correctly as a dependency +# This component is typically auto-loaded by other components + +wifi: + ssid: MySSID + password: password1 + +ota: + - platform: esphome + password: "test1234" diff --git a/tests/components/ota_base/test.esp32-idf.yaml b/tests/components/ota_base/test.esp32-idf.yaml new file mode 100644 index 0000000000..dade44d145 --- /dev/null +++ b/tests/components/ota_base/test.esp32-idf.yaml @@ -0,0 +1 @@ +<<: !include common.yaml