2#ifdef USE_WEBSERVER_OTA
8#ifdef USE_CAPTIVE_PORTAL
13#if defined(USE_LIBRETINY)
26static const char *
const TAG =
"web_server.ota";
28class OTARequestHandler :
public AsyncWebHandler {
30 OTARequestHandler(WebServerOTAComponent *parent) : parent_(parent) {}
31 void handleRequest(AsyncWebServerRequest *request)
override;
32 void handleUpload(AsyncWebServerRequest *request,
const PlatformString &filename,
size_t index, uint8_t *data,
33 size_t len,
bool final)
override;
34 bool canHandle(AsyncWebServerRequest *request)
const override {
35 if (request->method() != HTTP_POST)
39 char url_buf[AsyncWebServerRequest::URL_BUF_SIZE];
40 bool is_ota_request = request->url_to(url_buf) ==
"/update";
42 bool is_ota_request = request->url() == ESPHOME_F(
"/update");
45#if defined(USE_WEBSERVER_OTA_DISABLED) && defined(USE_CAPTIVE_PORTAL)
51#elif defined(USE_WEBSERVER_OTA_DISABLED)
56 return is_ota_request;
61 bool isRequestHandlerTrivial()
const override {
return false; }
64 void report_ota_progress_(AsyncWebServerRequest *request);
65 void schedule_ota_reboot_();
66 void ota_init_(
const char *filename);
70 WebServerOTAComponent *parent_;
71 bool ota_success_{
false};
77void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {
79 if (now - this->last_ota_progress_ > 1000) {
80 float percentage = 0.0f;
81 if (request->contentLength() != 0) {
86 percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
87 ESP_LOGD(TAG,
"OTA in progress: %0.1f%%", percentage);
89 ESP_LOGD(TAG,
"OTA in progress: %" PRIu32
" bytes read", this->ota_read_length_);
91#ifdef USE_OTA_STATE_LISTENER
95 this->last_ota_progress_ = now;
99void OTARequestHandler::schedule_ota_reboot_() {
100 ESP_LOGI(TAG,
"OTA update successful!");
102 ESP_LOGI(TAG,
"Performing OTA reboot now");
107void OTARequestHandler::ota_init_(
const char *filename) {
108 ESP_LOGI(TAG,
"OTA Update Start: %s", filename);
109 this->ota_read_length_ = 0;
110 this->ota_success_ =
false;
113void OTARequestHandler::handleUpload(AsyncWebServerRequest *request,
const PlatformString &filename,
size_t index,
114 uint8_t *data,
size_t len,
bool final) {
120 if (index == 0 &&
len > 0) {
126 if (this->ota_backend_) {
127 ESP_LOGW(TAG,
"New OTA upload received while previous session was still open; aborting previous session");
128 this->ota_backend_->abort();
129#ifdef USE_OTA_STATE_LISTENER
133 this->ota_backend_.reset();
137 this->ota_init_(filename.c_str());
139#ifdef USE_OTA_STATE_LISTENER
146#if defined(USE_LIBRETINY)
147 if (Update.isRunning()) {
154 if (!this->ota_backend_) {
155 ESP_LOGE(TAG,
"Failed to create OTA backend");
156#ifdef USE_OTA_STATE_LISTENER
166 error_code = this->ota_backend_->begin(0);
168 ESP_LOGE(TAG,
"OTA begin failed: %d", error_code);
169 this->ota_backend_.reset();
170#ifdef USE_OTA_STATE_LISTENER
177 if (!this->ota_backend_) {
183 error_code = this->ota_backend_->write(data,
len);
185 ESP_LOGE(TAG,
"OTA write failed: %d", error_code);
186 this->ota_backend_->abort();
187 this->ota_backend_.reset();
188#ifdef USE_OTA_STATE_LISTENER
193 this->ota_read_length_ +=
len;
194 this->report_ota_progress_(request);
199 ESP_LOGD(TAG,
"OTA final chunk: index=%zu, len=%zu, total_read=%" PRIu32
", contentLength=%zu", index,
len,
200 this->ota_read_length_, request->contentLength());
205 error_code = this->ota_backend_->end();
207 this->ota_success_ =
true;
208#ifdef USE_OTA_STATE_LISTENER
212 this->schedule_ota_reboot_();
214 ESP_LOGE(TAG,
"OTA end failed: %d", error_code);
215#ifdef USE_OTA_STATE_LISTENER
219 this->ota_backend_.reset();
223void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) {
224 AsyncWebServerResponse *response;
227 static const char UPDATE_SUCCESS[]
PROGMEM =
"Update Successful!";
228 static const char UPDATE_FAILED[]
PROGMEM =
"Update Failed!";
229 static const char TEXT_PLAIN[]
PROGMEM =
"text/plain";
230 static const char CONNECTION_STR[]
PROGMEM =
"Connection";
231 static const char CLOSE_STR[]
PROGMEM =
"close";
232 const char *msg = this->ota_success_ ? UPDATE_SUCCESS : UPDATE_FAILED;
233 response = request->beginResponse_P(200, TEXT_PLAIN, msg);
234 response->addHeader(CONNECTION_STR, CLOSE_STR);
236 const char *msg = this->ota_success_ ?
"Update Successful!" :
"Update Failed!";
237 response = request->beginResponse(200,
"text/plain", msg);
238 response->addHeader(
"Connection",
"close");
240 request->send(response);
246 if (base ==
nullptr) {
247 ESP_LOGE(TAG,
"WebServerBase not found");
void mark_failed()
Mark this component as failed.
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
void notify_state_deferred_(OTAState state, float progress, uint8_t error)
Notify state with deferral to main loop (for thread safety).
void dump_config() override
friend class OTARequestHandler
void add_handler(AsyncWebHandler *handler)
CaptivePortal * global_captive_portal
decltype(make_ota_backend()) OTABackendPtr
@ OTA_RESPONSE_ERROR_UNKNOWN
std::unique_ptr< ArduinoLibreTinyOTABackend > make_ota_backend()
WebServerBase * global_web_server_base
constexpr uint8_t INDEX_GZ[] PROGMEM
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
std::string PlatformString