This commit is contained in:
J. Nick Koston 2025-07-02 14:06:32 -05:00
parent cdeef700c2
commit e2d6363c68
No known key found for this signature in database
24 changed files with 802 additions and 91 deletions

View File

@ -18,7 +18,7 @@
#include <cinttypes>
#ifdef USE_OTA
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/ota/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_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) {
ota::get_global_ota_callback()->add_on_state_callback(
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) {
this->stop_scan();
for (auto *client : this->clients_) {
client->disconnect();

View File

@ -1,8 +1,7 @@
import logging
import esphome.codegen as cg
from esphome.components.ota import BASE_OTA_SCHEMA, ota_to_code
from esphome.components.ota_base import OTAComponent
from esphome.components.ota import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
from esphome.config_helpers import merge_config
import esphome.config_validation as cv
from esphome.const import (

View File

@ -2,12 +2,12 @@
#ifdef USE_OTA
#include "esphome/components/md5/md5.h"
#include "esphome/components/network/util.h"
#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"
#include "esphome/components/ota_base/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota_base/ota_backend_esp_idf.h"
#include "esphome/components/ota/ota_backend.h"
#include "esphome/components/ota/ota_backend_arduino_esp32.h"
#include "esphome/components/ota/ota_backend_arduino_esp8266.h"
#include "esphome/components/ota/ota_backend_arduino_libretiny.h"
#include "esphome/components/ota/ota_backend_arduino_rp2040.h"
#include "esphome/components/ota/ota_backend_esp_idf.h"
#include "esphome/core/application.h"
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
@ -23,7 +23,7 @@ static constexpr u_int16_t OTA_BLOCK_SIZE = 8192;
void ESPHomeOTAComponent::setup() {
#ifdef USE_OTA_STATE_CALLBACK
ota_base::register_ota_platform(this);
ota::register_ota_platform(this);
#endif
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
@ -94,7 +94,7 @@ void ESPHomeOTAComponent::loop() {
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
void ESPHomeOTAComponent::handle_() {
ota_base::OTAResponseTypes error_code = ota_base::OTA_RESPONSE_ERROR_UNKNOWN;
ota::OTAResponseTypes error_code = ota::OTA_RESPONSE_ERROR_UNKNOWN;
bool update_started = false;
size_t total = 0;
uint32_t last_progress = 0;
@ -102,7 +102,7 @@ void ESPHomeOTAComponent::handle_() {
char *sbuf = reinterpret_cast<char *>(buf);
size_t ota_size;
uint8_t ota_features;
std::unique_ptr<ota_base::OTABackend> backend;
std::unique_ptr<ota::OTABackend> backend;
(void) ota_features;
#if USE_OTA_VERSION == 2
size_t size_acknowledged = 0;
@ -129,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_base::OTA_STARTED, 0.0f, 0);
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
#endif
if (!this->readall_(buf, 5)) {
@ -140,16 +140,16 @@ 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_base::OTA_RESPONSE_ERROR_MAGIC;
error_code = ota::OTA_RESPONSE_ERROR_MAGIC;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
// Send OK and version - 2 bytes
buf[0] = ota_base::OTA_RESPONSE_OK;
buf[0] = ota::OTA_RESPONSE_OK;
buf[1] = USE_OTA_VERSION;
this->writeall_(buf, 2);
backend = ota_base::make_ota_backend();
backend = ota::make_ota_backend();
// Read features - 1 byte
if (!this->readall_(buf, 1)) {
@ -160,16 +160,16 @@ void ESPHomeOTAComponent::handle_() {
ESP_LOGV(TAG, "Features: 0x%02X", ota_features);
// Acknowledge header - 1 byte
buf[0] = ota_base::OTA_RESPONSE_HEADER_OK;
buf[0] = ota::OTA_RESPONSE_HEADER_OK;
if ((ota_features & FEATURE_SUPPORTS_COMPRESSION) != 0 && backend->supports_compression()) {
buf[0] = ota_base::OTA_RESPONSE_SUPPORTS_COMPRESSION;
buf[0] = ota::OTA_RESPONSE_SUPPORTS_COMPRESSION;
}
this->writeall_(buf, 1);
#ifdef USE_OTA_PASSWORD
if (!this->password_.empty()) {
buf[0] = ota_base::OTA_RESPONSE_REQUEST_AUTH;
buf[0] = ota::OTA_RESPONSE_REQUEST_AUTH;
this->writeall_(buf, 1);
md5::MD5Digest md5{};
md5.init();
@ -220,14 +220,14 @@ void ESPHomeOTAComponent::handle_() {
if (!matches) {
ESP_LOGW(TAG, "Auth failed! Passwords do not match");
error_code = ota_base::OTA_RESPONSE_ERROR_AUTH_INVALID;
error_code = ota::OTA_RESPONSE_ERROR_AUTH_INVALID;
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
}
#endif // USE_OTA_PASSWORD
// Acknowledge auth OK - 1 byte
buf[0] = ota_base::OTA_RESPONSE_AUTH_OK;
buf[0] = ota::OTA_RESPONSE_AUTH_OK;
this->writeall_(buf, 1);
// Read size, 4 bytes MSB first
@ -243,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_base::OTA_RESPONSE_OK)
if (error_code != ota::OTA_RESPONSE_OK)
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
update_started = true;
// Acknowledge prepare OK - 1 byte
buf[0] = ota_base::OTA_RESPONSE_UPDATE_PREPARE_OK;
buf[0] = ota::OTA_RESPONSE_UPDATE_PREPARE_OK;
this->writeall_(buf, 1);
// Read binary MD5, 32 bytes
@ -261,7 +261,7 @@ void ESPHomeOTAComponent::handle_() {
backend->set_update_md5(sbuf);
// Acknowledge MD5 OK - 1 byte
buf[0] = ota_base::OTA_RESPONSE_BIN_MD5_OK;
buf[0] = ota::OTA_RESPONSE_BIN_MD5_OK;
this->writeall_(buf, 1);
while (total < ota_size) {
@ -285,14 +285,14 @@ void ESPHomeOTAComponent::handle_() {
}
error_code = backend->write(buf, read);
if (error_code != ota_base::OTA_RESPONSE_OK) {
if (error_code != ota::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Error writing binary data to flash!, error_code: %d", error_code);
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
}
total += read;
#if USE_OTA_VERSION == 2
while (size_acknowledged + OTA_BLOCK_SIZE <= total || (total == ota_size && size_acknowledged < ota_size)) {
buf[0] = ota_base::OTA_RESPONSE_CHUNK_OK;
buf[0] = ota::OTA_RESPONSE_CHUNK_OK;
this->writeall_(buf, 1);
size_acknowledged += OTA_BLOCK_SIZE;
}
@ -304,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_base::OTA_IN_PROGRESS, percentage, 0);
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
#endif
// feed watchdog and give other tasks a chance to run
App.feed_wdt();
@ -313,21 +313,21 @@ void ESPHomeOTAComponent::handle_() {
}
// Acknowledge receive OK - 1 byte
buf[0] = ota_base::OTA_RESPONSE_RECEIVE_OK;
buf[0] = ota::OTA_RESPONSE_RECEIVE_OK;
this->writeall_(buf, 1);
error_code = backend->end();
if (error_code != ota_base::OTA_RESPONSE_OK) {
if (error_code != ota::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_base::OTA_RESPONSE_UPDATE_END_OK;
buf[0] = ota::OTA_RESPONSE_UPDATE_END_OK;
this->writeall_(buf, 1);
// Read ACK
if (!this->readall_(buf, 1) || buf[0] != ota_base::OTA_RESPONSE_OK) {
if (!this->readall_(buf, 1) || buf[0] != ota::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "Reading back acknowledgement failed");
// do not go to error, this is not fatal
}
@ -338,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_base::OTA_COMPLETED, 100.0f, 0);
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, 0);
#endif
delay(100); // NOLINT
App.safe_reboot();
@ -355,7 +355,7 @@ error:
this->status_momentary_error("onerror", 5000);
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
this->state_callback_.call(ota::OTA_ERROR, 0.0f, static_cast<uint8_t>(error_code));
#endif
}

View File

@ -4,13 +4,13 @@
#ifdef USE_OTA
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/ota/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_base::OTAComponent {
class ESPHomeOTAComponent : public ota::OTAComponent {
public:
#ifdef USE_OTA_PASSWORD
void set_auth_password(const std::string &password) { password_ = password; }

View File

@ -1,6 +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 import BASE_OTA_SCHEMA, OTAComponent, ota_to_code
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
@ -15,9 +15,6 @@ 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
)

View File

@ -6,11 +6,11 @@
#include "esphome/components/md5/md5.h"
#include "esphome/components/watchdog/watchdog.h"
#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"
#include "esphome/components/ota_base/ota_backend_esp_idf.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"
namespace esphome {
namespace http_request {
@ -19,7 +19,7 @@ static const char *const TAG = "http_request.ota";
void OtaHttpRequestComponent::setup() {
#ifdef USE_OTA_STATE_CALLBACK
ota_base::register_ota_platform(this);
ota::register_ota_platform(this);
#endif
}
@ -50,15 +50,15 @@ void OtaHttpRequestComponent::flash() {
ESP_LOGI(TAG, "Starting update");
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota_base::OTA_STARTED, 0.0f, 0);
this->state_callback_.call(ota::OTA_STARTED, 0.0f, 0);
#endif
auto ota_status = this->do_ota_();
switch (ota_status) {
case ota_base::OTA_RESPONSE_OK:
case ota::OTA_RESPONSE_OK:
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota_base::OTA_COMPLETED, 100.0f, ota_status);
this->state_callback_.call(ota::OTA_COMPLETED, 100.0f, ota_status);
#endif
delay(10);
App.safe_reboot();
@ -66,7 +66,7 @@ void OtaHttpRequestComponent::flash() {
default:
#ifdef USE_OTA_STATE_CALLBACK
this->state_callback_.call(ota_base::OTA_ERROR, 0.0f, ota_status);
this->state_callback_.call(ota::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
@ -74,7 +74,7 @@ void OtaHttpRequestComponent::flash() {
}
}
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota_base::OTABackend> backend,
void OtaHttpRequestComponent::cleanup_(std::unique_ptr<ota::OTABackend> backend,
const std::shared_ptr<HttpContainer> &container) {
if (this->update_started_) {
ESP_LOGV(TAG, "Aborting OTA backend");
@ -115,9 +115,9 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
ESP_LOGV(TAG, "MD5Digest initialized");
ESP_LOGV(TAG, "OTA backend begin");
auto backend = ota_base::make_ota_backend();
auto backend = ota::make_ota_backend();
auto error_code = backend->begin(container->content_length);
if (error_code != ota_base::OTA_RESPONSE_OK) {
if (error_code != ota::OTA_RESPONSE_OK) {
ESP_LOGW(TAG, "backend->begin error: %d", error_code);
this->cleanup_(std::move(backend), container);
return error_code;
@ -144,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_base::OTA_RESPONSE_OK) {
if (error_code != ota::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,
@ -160,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_base::OTA_IN_PROGRESS, percentage, 0);
this->state_callback_.call(ota::OTA_IN_PROGRESS, percentage, 0);
#endif
}
} // while
@ -174,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_base::OTA_RESPONSE_ERROR_MD5_MISMATCH;
return ota::OTA_RESPONSE_ERROR_MD5_MISMATCH;
} else {
backend->set_update_md5(md5_receive_str.get());
}
@ -187,14 +187,14 @@ uint8_t OtaHttpRequestComponent::do_ota_() {
delay(100); // NOLINT
error_code = backend->end();
if (error_code != ota_base::OTA_RESPONSE_OK) {
if (error_code != ota::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_base::OTA_RESPONSE_OK;
return ota::OTA_RESPONSE_OK;
}
std::string OtaHttpRequestComponent::get_url_with_auth_(const std::string &url) {

View File

@ -1,6 +1,6 @@
#pragma once
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/ota/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_base::OTAComponent, public Parented<HttpRequestComponent> {
class OtaHttpRequestComponent : public ota::OTAComponent, public Parented<HttpRequestComponent> {
public:
void setup() override;
void dump_config() override;
@ -40,7 +40,7 @@ class OtaHttpRequestComponent : public ota_base::OTAComponent, public Parented<H
void flash();
protected:
void cleanup_(std::unique_ptr<ota_base::OTABackend> backend, const std::shared_ptr<HttpContainer> &container);
void cleanup_(std::unique_ptr<ota::OTABackend> 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_();

View File

@ -5,7 +5,6 @@
#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 {
@ -22,13 +21,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_base::OTAState state, float progress, uint8_t err) {
if (state == ota_base::OTAState::OTA_IN_PROGRESS) {
this->ota_parent_->add_on_state_callback([this](ota::OTAState state, float progress, uint8_t err) {
if (state == ota::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_base::OTAState::OTA_ABORT || state == ota_base::OTAState::OTA_ERROR) {
} else if (state == ota::OTAState::OTA_ABORT || state == ota::OTAState::OTA_ERROR) {
this->state_ = update::UPDATE_STATE_AVAILABLE;
this->status_set_error("Failed to install firmware");
this->publish_state();

View File

@ -9,7 +9,7 @@
#include "esphome/components/audio/audio_transfer_buffer.h"
#ifdef USE_OTA
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/ota/ota_backend.h"
#endif
namespace esphome {
@ -121,11 +121,11 @@ void MicroWakeWord::setup() {
});
#ifdef USE_OTA
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) {
ota::get_global_ota_callback()->add_on_state_callback(
[this](ota::OTAState state, float progress, uint8_t error, ota::OTAComponent *comp) {
if (state == ota::OTA_STARTED) {
this->suspend_task_();
} else if (state == ota_base::OTA_ERROR) {
} else if (state == ota::OTA_ERROR) {
this->resume_task_();
}
});

View File

@ -8,12 +8,10 @@ from esphome.const import (
CONF_PLATFORM,
CONF_TRIGGER_ID,
)
from esphome.core import coroutine_with_priority
from ..ota_base import OTAState
from esphome.core import CORE, coroutine_with_priority
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["safe_mode", "ota_base"]
AUTO_LOAD = ["md5", "safe_mode"]
IS_PLATFORM_COMPONENT = True
@ -25,6 +23,8 @@ 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")
OTAAbortTrigger = ota_ns.class_("OTAAbortTrigger", automation.Trigger.template())
OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template())
OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template())
@ -84,6 +84,12 @@ 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()

View File

@ -1,16 +1,12 @@
#pragma once
#ifdef USE_OTA_STATE_CALLBACK
#include "esphome/components/ota_base/ota_backend.h"
#include "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<OTAState> {
public:
explicit OTAStateChangeTrigger(OTAComponent *parent) {
@ -26,7 +22,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_base::OTA_STARTED && !parent->is_failed()) {
if (state == OTA_STARTED && !parent->is_failed()) {
trigger();
}
});
@ -37,7 +33,7 @@ class OTAProgressTrigger : public Trigger<float> {
public:
explicit OTAProgressTrigger(OTAComponent *parent) {
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
if (state == ota_base::OTA_IN_PROGRESS && !parent->is_failed()) {
if (state == OTA_IN_PROGRESS && !parent->is_failed()) {
trigger(progress);
}
});
@ -48,7 +44,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_base::OTA_COMPLETED && !parent->is_failed()) {
if (state == OTA_COMPLETED && !parent->is_failed()) {
trigger();
}
});
@ -59,7 +55,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_base::OTA_ABORT && !parent->is_failed()) {
if (state == OTA_ABORT && !parent->is_failed()) {
trigger();
}
});
@ -70,7 +66,7 @@ class OTAErrorTrigger : public Trigger<uint8_t> {
public:
explicit OTAErrorTrigger(OTAComponent *parent) {
parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) {
if (state == ota_base::OTA_ERROR && !parent->is_failed()) {
if (state == OTA_ERROR && !parent->is_failed()) {
trigger(error);
}
});

View File

@ -0,0 +1,20 @@
#include "ota_backend.h"
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
} // namespace ota
} // namespace esphome

View File

@ -0,0 +1,122 @@
#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 {
enum OTAResponseTypes {
OTA_RESPONSE_OK = 0x00,
OTA_RESPONSE_REQUEST_AUTH = 0x01,
OTA_RESPONSE_HEADER_OK = 0x40,
OTA_RESPONSE_AUTH_OK = 0x41,
OTA_RESPONSE_UPDATE_PREPARE_OK = 0x42,
OTA_RESPONSE_BIN_MD5_OK = 0x43,
OTA_RESPONSE_RECEIVE_OK = 0x44,
OTA_RESPONSE_UPDATE_END_OK = 0x45,
OTA_RESPONSE_SUPPORTS_COMPRESSION = 0x46,
OTA_RESPONSE_CHUNK_OK = 0x47,
OTA_RESPONSE_ERROR_MAGIC = 0x80,
OTA_RESPONSE_ERROR_UPDATE_PREPARE = 0x81,
OTA_RESPONSE_ERROR_AUTH_INVALID = 0x82,
OTA_RESPONSE_ERROR_WRITING_FLASH = 0x83,
OTA_RESPONSE_ERROR_UPDATE_END = 0x84,
OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING = 0x85,
OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG = 0x86,
OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG = 0x87,
OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE = 0x88,
OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE = 0x89,
OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION = 0x8A,
OTA_RESPONSE_ERROR_MD5_MISMATCH = 0x8B,
OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE = 0x8C,
OTA_RESPONSE_ERROR_UNKNOWN = 0xFF,
};
enum OTAState {
OTA_COMPLETED = 0,
OTA_STARTED,
OTA_IN_PROGRESS,
OTA_ABORT,
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;
};
class OTAComponent : public Component {
#ifdef USE_OTA_STATE_CALLBACK
public:
void add_on_state_callback(std::function<void(ota::OTAState, float, uint8_t)> &&callback) {
this->state_callback_.add(std::move(callback));
}
protected:
/** Extended callback manager with deferred call support.
*
* This adds a call_deferred() method for thread-safe execution from other tasks.
*/
class StateCallbackManager : public CallbackManager<void(OTAState, float, uint8_t)> {
public:
StateCallbackManager(OTAComponent *component) : component_(component) {}
/** 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(ota::OTAState state, float progress, uint8_t error) {
component_->defer([this, state, progress, error]() { this->call(state, progress, error); });
}
private:
OTAComponent *component_;
};
StateCallbackManager state_callback_{this};
#endif
};
#ifdef USE_OTA_STATE_CALLBACK
class OTAGlobalCallback {
public:
void register_ota(OTAComponent *ota_caller) {
ota_caller->add_on_state_callback([this, ota_caller](OTAState state, float progress, uint8_t error) {
this->state_callback_.call(state, progress, error, ota_caller);
});
}
void add_on_state_callback(std::function<void(OTAState, float, uint8_t, OTAComponent *)> &&callback) {
this->state_callback_.add(std::move(callback));
}
protected:
CallbackManager<void(OTAState, float, uint8_t, OTAComponent *)> state_callback_{};
};
OTAGlobalCallback *get_global_ota_callback();
void register_ota_platform(OTAComponent *ota_caller);
// 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
std::unique_ptr<ota::OTABackend> make_ota_backend();
} // namespace ota
} // namespace esphome

View File

@ -0,0 +1,72 @@
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include "ota_backend.h"
#include "ota_backend_arduino_esp32.h"
#include <Update.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_esp32";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP32OTABackend>(); }
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;
}
uint8_t error = Update.getError();
if (error == UPDATE_ERROR_SIZE)
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
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);
if (written == len) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoESP32OTABackend::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;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoESP32OTABackend::abort() { Update.abort(); }
} // namespace ota
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@ -0,0 +1,27 @@
#pragma once
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/helpers.h"
namespace esphome {
namespace ota {
class ArduinoESP32OTABackend : public OTABackend {
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; }
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace esphome
#endif // USE_ESP32_FRAMEWORK_ARDUINO

View File

@ -0,0 +1,89 @@
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
#include "ota_backend_arduino_esp8266.h"
#include "ota_backend.h"
#include "esphome/components/esp8266/preferences.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include <Updater.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_esp8266";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoESP8266OTABackend>(); }
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);
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
if (error == UPDATE_ERROR_BOOTSTRAP)
return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING;
if (error == UPDATE_ERROR_NEW_FLASH_CONFIG)
return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG;
if (error == UPDATE_ERROR_FLASH_CONFIG)
return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG;
if (error == UPDATE_ERROR_SPACE)
return OTA_RESPONSE_ERROR_ESP8266_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
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);
if (written == len) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
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
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;
}
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoESP8266OTABackend::abort() {
Update.end();
esp8266::preferences_prevent_write(false);
}
} // namespace ota
} // namespace esphome
#endif
#endif

View File

@ -0,0 +1,33 @@
#pragma once
#ifdef USE_ARDUINO
#ifdef USE_ESP8266
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
namespace esphome {
namespace ota {
class ArduinoESP8266OTABackend : public OTABackend {
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;
#if USE_ARDUINO_VERSION_CODE >= VERSION_CODE(2, 7, 0)
bool supports_compression() override { return true; }
#else
bool supports_compression() override { return false; }
#endif
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace esphome
#endif
#endif

View File

@ -0,0 +1,72 @@
#ifdef USE_LIBRETINY
#include "ota_backend_arduino_libretiny.h"
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include <Update.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_libretiny";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoLibreTinyOTABackend>(); }
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;
}
uint8_t error = Update.getError();
if (error == UPDATE_ERROR_SIZE)
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
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);
if (written == len) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoLibreTinyOTABackend::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;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoLibreTinyOTABackend::abort() { Update.abort(); }
} // namespace ota
} // namespace esphome
#endif // USE_LIBRETINY

View File

@ -0,0 +1,26 @@
#pragma once
#ifdef USE_LIBRETINY
#include "ota_backend.h"
#include "esphome/core/defines.h"
namespace esphome {
namespace ota {
class ArduinoLibreTinyOTABackend : public OTABackend {
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; }
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace esphome
#endif // USE_LIBRETINY

View File

@ -0,0 +1,82 @@
#ifdef USE_ARDUINO
#ifdef USE_RP2040
#include "ota_backend_arduino_rp2040.h"
#include "ota_backend.h"
#include "esphome/components/rp2040/preferences.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include <Updater.h>
namespace esphome {
namespace ota {
static const char *const TAG = "ota.arduino_rp2040";
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::ArduinoRP2040OTABackend>(); }
OTAResponseTypes ArduinoRP2040OTABackend::begin(size_t image_size) {
// OTA size of 0 is not currently handled, but
// web_server is not supported for RP2040, so this is not an issue.
bool ret = Update.begin(image_size, U_FLASH);
if (ret) {
rp2040::preferences_prevent_write(true);
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
if (error == UPDATE_ERROR_BOOTSTRAP)
return OTA_RESPONSE_ERROR_INVALID_BOOTSTRAPPING;
if (error == UPDATE_ERROR_NEW_FLASH_CONFIG)
return OTA_RESPONSE_ERROR_WRONG_NEW_FLASH_CONFIG;
if (error == UPDATE_ERROR_FLASH_CONFIG)
return OTA_RESPONSE_ERROR_WRONG_CURRENT_FLASH_CONFIG;
if (error == UPDATE_ERROR_SPACE)
return OTA_RESPONSE_ERROR_RP2040_NOT_ENOUGH_SPACE;
ESP_LOGE(TAG, "Begin error: %d", error);
return OTA_RESPONSE_ERROR_UNKNOWN;
}
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);
if (written == len) {
return OTA_RESPONSE_OK;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "Write error: %d", error);
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
OTAResponseTypes ArduinoRP2040OTABackend::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;
}
uint8_t error = Update.getError();
ESP_LOGE(TAG, "End error: %d", error);
return OTA_RESPONSE_ERROR_UPDATE_END;
}
void ArduinoRP2040OTABackend::abort() {
Update.end();
rp2040::preferences_prevent_write(false);
}
} // namespace ota
} // namespace esphome
#endif // USE_RP2040
#endif // USE_ARDUINO

View File

@ -0,0 +1,29 @@
#pragma once
#ifdef USE_ARDUINO
#ifdef USE_RP2040
#include "ota_backend.h"
#include "esphome/core/defines.h"
#include "esphome/core/macros.h"
namespace esphome {
namespace ota {
class ArduinoRP2040OTABackend : public OTABackend {
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; }
private:
bool md5_set_{false};
};
} // namespace ota
} // namespace esphome
#endif // USE_RP2040
#endif // USE_ARDUINO

View File

@ -0,0 +1,110 @@
#ifdef USE_ESP_IDF
#include "ota_backend_esp_idf.h"
#include "esphome/components/md5/md5.h"
#include "esphome/core/defines.h"
#include <esp_ota_ops.h>
#include <esp_task_wdt.h>
#include <spi_flash_mmap.h>
namespace esphome {
namespace ota {
std::unique_ptr<ota::OTABackend> make_ota_backend() { return make_unique<ota::IDFOTABackend>(); }
OTAResponseTypes IDFOTABackend::begin(size_t image_size) {
this->partition_ = esp_ota_get_next_update_partition(nullptr);
if (this->partition_ == nullptr) {
return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION;
}
#if CONFIG_ESP_TASK_WDT_TIMEOUT_S < 15
// The following function takes longer than the 5 seconds timeout of WDT
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);
#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
wdtc.timeout_ms = CONFIG_ESP_TASK_WDT_TIMEOUT_S * 1000;
esp_task_wdt_reconfigure(&wdtc);
#endif
if (err != ESP_OK) {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
if (err == ESP_ERR_INVALID_SIZE) {
return OTA_RESPONSE_ERROR_ESP32_NOT_ENOUGH_SPACE;
} else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
return OTA_RESPONSE_ERROR_UNKNOWN;
}
this->md5_.init();
return OTA_RESPONSE_OK;
}
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);
this->md5_.add(data, len);
if (err != ESP_OK) {
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
return OTA_RESPONSE_ERROR_MAGIC;
} else if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
return OTA_RESPONSE_ERROR_UNKNOWN;
}
return OTA_RESPONSE_OK;
}
OTAResponseTypes IDFOTABackend::end() {
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;
if (err == ESP_OK) {
err = esp_ota_set_boot_partition(this->partition_);
if (err == ESP_OK) {
return OTA_RESPONSE_OK;
}
}
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
return OTA_RESPONSE_ERROR_UPDATE_END;
}
if (err == ESP_ERR_FLASH_OP_TIMEOUT || err == ESP_ERR_FLASH_OP_FAIL) {
return OTA_RESPONSE_ERROR_WRITING_FLASH;
}
return OTA_RESPONSE_ERROR_UNKNOWN;
}
void IDFOTABackend::abort() {
esp_ota_abort(this->update_handle_);
this->update_handle_ = 0;
}
} // namespace ota
} // namespace esphome
#endif

View File

@ -0,0 +1,32 @@
#pragma once
#ifdef USE_ESP_IDF
#include "ota_backend.h"
#include "esphome/components/md5/md5.h"
#include "esphome/core/defines.h"
#include <esp_ota_ops.h>
namespace esphome {
namespace ota {
class IDFOTABackend : public OTABackend {
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; }
private:
esp_ota_handle_t update_handle_{0};
const esp_partition_t *partition_;
md5::MD5Digest md5_{};
char expected_bin_md5_[32];
bool md5_set_{false};
};
} // namespace ota
} // namespace esphome
#endif

View File

@ -6,7 +6,7 @@
#include "esphome/components/audio/audio.h"
#ifdef USE_OTA
#include "esphome/components/ota_base/ota_backend.h"
#include "esphome/components/ota/ota_backend.h"
#endif
namespace esphome {
@ -67,16 +67,16 @@ void SpeakerMediaPlayer::setup() {
}
#ifdef USE_OTA
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) {
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) {
if (this->media_pipeline_ != nullptr) {
this->media_pipeline_->suspend_tasks();
}
if (this->announcement_pipeline_ != nullptr) {
this->announcement_pipeline_->suspend_tasks();
}
} else if (state == ota_base::OTA_ERROR) {
} else if (state == ota::OTA_ERROR) {
if (this->media_pipeline_ != nullptr) {
this->media_pipeline_->resume_tasks();
}