diff --git a/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp b/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp index fcec1a5f20..9f8ae3277e 100644 --- a/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp +++ b/esphome/components/esp32_hosted/update/esp32_hosted_update.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef USE_ESP32_HOSTED_HTTP_UPDATE #include "esphome/components/json/json_util.h" @@ -442,6 +443,12 @@ void Esp32HostedUpdate::perform(bool force) { this->status_clear_error(); this->publish_state(); +#ifdef USE_OTA_ROLLBACK + // Mark the host partition as valid before rebooting, in case the safe mode + // timer hasn't expired yet. + esp_ota_mark_app_valid_cancel_rollback(); +#endif + // Schedule a restart to ensure everything is in sync ESP_LOGI(TAG, "Restarting in 1 second"); this->set_timeout(1000, []() { App.safe_reboot(); }); diff --git a/esphome/components/ota/ota_backend_esp_idf.cpp b/esphome/components/ota/ota_backend_esp_idf.cpp index f278c3741f..93c65a9624 100644 --- a/esphome/components/ota/ota_backend_esp_idf.cpp +++ b/esphome/components/ota/ota_backend_esp_idf.cpp @@ -14,6 +14,13 @@ namespace ota { std::unique_ptr make_ota_backend() { return make_unique(); } OTAResponseTypes IDFOTABackend::begin(size_t image_size) { +#ifdef USE_OTA_ROLLBACK + // If we're starting an OTA, the current boot is good enough - mark it valid + // to prevent rollback and allow the OTA to proceed even if the safe mode + // timer hasn't expired yet. + esp_ota_mark_app_valid_cancel_rollback(); +#endif + this->partition_ = esp_ota_get_next_update_partition(nullptr); if (this->partition_ == nullptr) { return OTA_RESPONSE_ERROR_NO_UPDATE_PARTITION;