[ota] Devirtualize OTA backend calls (#14473)

This commit is contained in:
J. Nick Koston
2026-03-05 19:15:02 +00:00
committed by GitHub
parent cce7a09fa9
commit e210e414bd
16 changed files with 84 additions and 65 deletions
+2 -2
View File
@@ -2,7 +2,7 @@
#include "esphome/core/defines.h"
#ifdef USE_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_factory.h"
#include "esphome/components/socket/socket.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
@@ -86,7 +86,7 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
socket::ListenSocket *server_{nullptr};
std::unique_ptr<socket::Socket> client_;
std::unique_ptr<ota::OTABackend> backend_;
ota::OTABackendPtr backend_;
uint32_t client_connect_time_{0};
uint16_t port_;
@@ -8,10 +8,6 @@
#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_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota/ota_backend_esp_idf.h"
namespace esphome {
namespace http_request {
@@ -69,8 +65,7 @@ void OtaHttpRequestComponent::flash() {
}
}
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota::OTABackend> backend,
const std::shared_ptr<HttpContainer> &container) {
void OtaHttpRequestComponent::cleanup_(ota::OTABackendPtr backend, const std::shared_ptr<HttpContainer> &container) {
if (this->update_started_) {
ESP_LOGV(TAG, "Aborting OTA backend");
backend->abort();
@@ -1,6 +1,6 @@
#pragma once
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_factory.h"
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
@@ -39,7 +39,7 @@ class OtaHttpRequestComponent final : public ota::OTAComponent, public Parented<
void flash();
protected:
void cleanup_(std::unique_ptr<ota::OTABackend> backend, const std::shared_ptr<HttpContainer> &container);
void cleanup_(ota::OTABackendPtr backend, const std::shared_ptr<HttpContainer> &container);
uint8_t do_ota_();
std::string get_url_with_auth_(const std::string &url);
bool http_get_md5_();
-13
View File
@@ -49,17 +49,6 @@ enum OTAState {
OTA_ERROR,
};
class OTABackend {
public:
virtual ~OTABackend() = default;
virtual OTAResponseTypes begin(size_t image_size) = 0;
virtual void set_update_md5(const char *md5) = 0;
virtual OTAResponseTypes write(uint8_t *data, size_t len) = 0;
virtual OTAResponseTypes end() = 0;
virtual void abort() = 0;
virtual bool supports_compression() = 0;
};
/** Listener interface for OTA state changes.
*
* Components can implement this interface to receive OTA state updates
@@ -130,7 +119,5 @@ OTAGlobalCallback *get_global_ota_callback();
// - notify_state_deferred_() when in separate task (e.g., web_server OTA)
// This ensures proper listener execution in all contexts.
#endif
std::unique_ptr<ota::OTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
@@ -12,7 +12,7 @@ namespace ota {
static const char *const TAG = "ota.arduino_libretiny";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoLibreTinyOTABackend>(); }
std::unique_ptr<ArduinoLibreTinyOTABackend> make_ota_backend() { return make_unique<ArduinoLibreTinyOTABackend>(); }
OTAResponseTypes ArduinoLibreTinyOTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) which is used by web server OTA
@@ -7,19 +7,21 @@
namespace esphome {
namespace ota {
class ArduinoLibreTinyOTABackend final : public OTABackend {
class ArduinoLibreTinyOTABackend final {
public:
OTAResponseTypes begin(size_t image_size) override;
void set_update_md5(const char *md5) override;
OTAResponseTypes write(uint8_t *data, size_t len) override;
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
OTAResponseTypes begin(size_t image_size);
void set_update_md5(const char *md5);
OTAResponseTypes write(uint8_t *data, size_t len);
OTAResponseTypes end();
void abort();
bool supports_compression() { return false; }
private:
bool md5_set_{false};
};
std::unique_ptr<ArduinoLibreTinyOTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
@@ -14,7 +14,7 @@ namespace ota {
static const char *const TAG = "ota.arduino_rp2040";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoRP2040OTABackend>(); }
std::unique_ptr<ArduinoRP2040OTABackend> make_ota_backend() { return make_unique<ArduinoRP2040OTABackend>(); }
OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
// OTA size of 0 is not currently handled, but
@@ -9,19 +9,21 @@
namespace esphome {
namespace ota {
class ArduinoRP2040OTABackend final : public OTABackend {
class ArduinoRP2040OTABackend final {
public:
OTAResponseTypes begin(size_t image_size) override;
void set_update_md5(const char *md5) override;
OTAResponseTypes write(uint8_t *data, size_t len) override;
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
OTAResponseTypes begin(size_t image_size);
void set_update_md5(const char *md5);
OTAResponseTypes write(uint8_t *data, size_t len);
OTAResponseTypes end();
void abort();
bool supports_compression() { return false; }
private:
bool md5_set_{false};
};
std::unique_ptr<ArduinoRP2040OTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
@@ -48,7 +48,7 @@ namespace esphome::ota {
static const char *const TAG = "ota.esp8266";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ESP8266OTABackend>(); }
std::unique_ptr<ESP8266OTABackend> make_ota_backend() { return make_unique<ESP8266OTABackend>(); }
OTAResponseTypes ESP8266OTABackend::begin(size_t image_size) {
// Handle UPDATE_SIZE_UNKNOWN (0) by calculating available space
+9 -7
View File
@@ -12,15 +12,15 @@ namespace esphome::ota {
/// OTA backend for ESP8266 using native SDK functions.
/// This implementation bypasses the Arduino Updater library to save ~228 bytes of RAM
/// by not having a global Update object in .bss.
class ESP8266OTABackend final : public OTABackend {
class ESP8266OTABackend final {
public:
OTAResponseTypes begin(size_t image_size) override;
void set_update_md5(const char *md5) override;
OTAResponseTypes write(uint8_t *data, size_t len) override;
OTAResponseTypes end() override;
void abort() override;
OTAResponseTypes begin(size_t image_size);
void set_update_md5(const char *md5);
OTAResponseTypes write(uint8_t *data, size_t len);
OTAResponseTypes end();
void abort();
// Compression supported in all ESP8266 Arduino versions ESPHome supports (>= 2.7.0)
bool supports_compression() override { return true; }
bool supports_compression() { return true; }
protected:
/// Erase flash sector if current address is at sector boundary
@@ -54,5 +54,7 @@ class ESP8266OTABackend final : public OTABackend {
bool md5_set_{false};
};
std::unique_ptr<ESP8266OTABackend> make_ota_backend();
} // namespace esphome::ota
#endif // USE_ESP8266
@@ -11,7 +11,7 @@
namespace esphome {
namespace ota {
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::IDFOTABackend>(); }
std::unique_ptr<IDFOTABackend> make_ota_backend() { return make_unique<IDFOTABackend>(); }
OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
#ifdef USE_OTA_ROLLBACK
+9 -7
View File
@@ -10,14 +10,14 @@
namespace esphome {
namespace ota {
class IDFOTABackend final : public OTABackend {
class IDFOTABackend final {
public:
OTAResponseTypes begin(size_t image_size) override;
void set_update_md5(const char *md5) override;
OTAResponseTypes write(uint8_t *data, size_t len) override;
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
OTAResponseTypes begin(size_t image_size);
void set_update_md5(const char *md5);
OTAResponseTypes write(uint8_t *data, size_t len);
OTAResponseTypes end();
void abort();
bool supports_compression() { return false; }
private:
esp_ota_handle_t update_handle_{0};
@@ -27,6 +27,8 @@ class IDFOTABackend final : public OTABackend {
bool md5_set_{false};
};
std::unique_ptr<IDFOTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome
#endif // USE_ESP32
@@ -0,0 +1,27 @@
#pragma once
#include "ota_backend.h"
#include <memory>
#ifdef USE_ESP8266
#include "ota_backend_esp8266.h"
#elif defined(USE_ESP32)
#include "ota_backend_esp_idf.h"
#elif defined(USE_RP2040)
#include "ota_backend_arduino_rp2040.h"
#elif defined(USE_LIBRETINY)
#include "ota_backend_arduino_libretiny.h"
#elif defined(USE_HOST)
#include "ota_backend_host.h"
#else
// Stub for static analysis when no platform is defined
namespace esphome::ota {
struct StubOTABackend {};
std::unique_ptr<StubOTABackend> make_ota_backend();
} // namespace esphome::ota
#endif
namespace esphome::ota {
using OTABackendPtr = decltype(make_ota_backend());
} // namespace esphome::ota
+1 -1
View File
@@ -8,7 +8,7 @@ namespace esphome::ota {
// Stub implementation - OTA is not supported on host platform.
// All methods return error codes to allow compilation of configs with OTA triggers.
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::HostOTABackend>(); }
std::unique_ptr<HostOTABackend> make_ota_backend() { return make_unique<HostOTABackend>(); }
OTAResponseTypes HostOTABackend::begin(size_t image_size) { return OTA_RESPONSE_ERROR_UPDATE_PREPARE; }
+9 -7
View File
@@ -7,15 +7,17 @@ namespace esphome::ota {
/// Stub OTA backend for host platform - allows compilation but does not implement OTA.
/// All operations return error codes immediately. This enables configurations with
/// OTA triggers to compile for host platform during development.
class HostOTABackend final : public OTABackend {
class HostOTABackend final {
public:
OTAResponseTypes begin(size_t image_size) override;
void set_update_md5(const char *md5) override;
OTAResponseTypes write(uint8_t *data, size_t len) override;
OTAResponseTypes end() override;
void abort() override;
bool supports_compression() override { return false; }
OTAResponseTypes begin(size_t image_size);
void set_update_md5(const char *md5);
OTAResponseTypes write(uint8_t *data, size_t len);
OTAResponseTypes end();
void abort();
bool supports_compression() { return false; }
};
std::unique_ptr<HostOTABackend> make_ota_backend();
} // namespace esphome::ota
#endif
@@ -1,7 +1,7 @@
#include "ota_web_server.h"
#ifdef USE_WEBSERVER_OTA
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_factory.h"
#include "esphome/core/application.h"
#include "esphome/core/log.h"
@@ -71,7 +71,7 @@ class OTARequestHandler : public AsyncWebHandler {
bool ota_success_{false};
private:
std::unique_ptr<ota::OTABackend> ota_backend_{nullptr};
ota::OTABackendPtr ota_backend_{nullptr};
};
void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {