From 8b195d7f6373ccfc277bb417df910cace0570ff6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 1 Jul 2025 10:11:41 -0500 Subject: [PATCH] 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