[ota] Devirtualize OTA backend calls

Each platform defines exactly one OTA backend subclass (marked final),
so using concrete types in unique_ptr eliminates virtual dispatch overhead.

- Move make_ota_backend() declaration from base header to each concrete
  backend header with concrete return type
- Add ota_backend_factory.h convenience header for consumers
- Use concrete unique_ptr types in ota_esphome, http_request, and
  web_server consumers
This commit is contained in:
J. Nick Koston
2026-03-04 09:49:31 -10:00
parent 4928e678d1
commit 9cb5d287a4
16 changed files with 47 additions and 18 deletions
+12 -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,17 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
socket::ListenSocket *server_{nullptr};
std::unique_ptr<socket::Socket> client_;
std::unique_ptr<ota::OTABackend> backend_;
#ifdef USE_ESP8266
std::unique_ptr<ota::ESP8266OTABackend> backend_;
#elif defined(USE_ESP32)
std::unique_ptr<ota::IDFOTABackend> backend_;
#elif defined(USE_RP2040)
std::unique_ptr<ota::ArduinoRP2040OTABackend> backend_;
#elif defined(USE_LIBRETINY)
std::unique_ptr<ota::ArduinoLibreTinyOTABackend> backend_;
#elif defined(USE_HOST)
std::unique_ptr<ota::HostOTABackend> backend_;
#endif
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,7 +65,7 @@ void OtaHttpRequestComponent::flash() {
}
}
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota::OTABackend> backend,
void OtaHttpRequestComponent::cleanup_(decltype(ota::make_ota_backend()) backend,
const std::shared_ptr<HttpContainer> &container) {
if (this->update_started_) {
ESP_LOGV(TAG, "Aborting OTA backend");
@@ -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_(decltype(ota::make_ota_backend()) 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_();
-2
View File
@@ -130,7 +130,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
@@ -20,6 +20,8 @@ class ArduinoLibreTinyOTABackend final : public OTABackend {
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
@@ -22,6 +22,8 @@ class ArduinoRP2040OTABackend final : public OTABackend {
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
@@ -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
@@ -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,15 @@
#pragma once
#include "ota_backend.h"
#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"
#endif
+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; }
@@ -17,5 +17,7 @@ class HostOTABackend final : public OTABackend {
bool supports_compression() override { 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};
decltype(ota::make_ota_backend()) ota_backend_{nullptr};
};
void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {