2#ifdef USE_WEBSERVER_OTA
8#ifdef USE_CAPTIVE_PORTAL
22class OTARequestHandler :
public AsyncWebHandler {
25 void handleRequest(AsyncWebServerRequest *request)
override;
26 void handleUpload(AsyncWebServerRequest *request,
const PlatformString &filename,
size_t index, uint8_t *data,
27 size_t len,
bool final)
override;
28 bool canHandle(AsyncWebServerRequest *request)
const override {
29 if (request->method() != HTTP_POST)
33 char url_buf[AsyncWebServerRequest::URL_BUF_SIZE];
34 bool is_ota_request = request->url_to(url_buf) ==
"/update";
36 bool is_ota_request = request->url() == ESPHOME_F(
"/update");
39#if defined(USE_WEBSERVER_OTA_DISABLED) && defined(USE_CAPTIVE_PORTAL)
45#elif defined(USE_WEBSERVER_OTA_DISABLED)
50 return is_ota_request;
55 bool isRequestHandlerTrivial()
const override {
return false; }
58 void report_ota_progress_(AsyncWebServerRequest *request);
59 void schedule_ota_reboot_();
60 void ota_init_(
const char *filename);
62 void abort_session_();
64 void refuse_(AsyncWebServerRequest *request);
68 WebServerOTAComponent *parent_;
69 bool ota_success_{
false};
74 const AsyncWebServerRequest *ota_request_{
nullptr};
75 const AsyncWebServerRequest *refused_request_{
nullptr};
78void OTARequestHandler::report_ota_progress_(AsyncWebServerRequest *request) {
80 if (now - this->last_ota_progress_ > 1000) {
81 float percentage = 0.0f;
82 if (request->contentLength() != 0) {
87 percentage = (this->ota_read_length_ * 100.0f) / request->contentLength();
88 ESP_LOGD(TAG,
"OTA in progress: %0.1f%%", percentage);
90 ESP_LOGD(TAG,
"OTA in progress: %" PRIu32
" bytes read", this->ota_read_length_);
92#ifdef USE_OTA_STATE_LISTENER
96 this->last_ota_progress_ = now;
100void OTARequestHandler::schedule_ota_reboot_() {
101 ESP_LOGI(TAG,
"OTA update successful!");
103 ESP_LOGI(TAG,
"Performing OTA reboot now");
108void OTARequestHandler::ota_init_(
const char *filename) {
109 ESP_LOGI(TAG,
"OTA Update Start: %s", filename);
110 this->ota_read_length_ = 0;
111 this->ota_success_ =
false;
114void OTARequestHandler::handleUpload(AsyncWebServerRequest *request,
const PlatformString &filename,
size_t index,
115 uint8_t *data,
size_t len,
bool final) {
121 if (index == 0 &&
len > 0) {
123 if (this->ota_backend_) {
124 ESP_LOGW(TAG,
"OTA upload refused: another upload is in progress");
125 this->refuse_(request);
130 if (!this->ota_backend_) {
131 ESP_LOGE(TAG,
"Failed to create OTA backend");
132#ifdef USE_OTA_STATE_LISTENER
142 error_code = this->ota_backend_->begin(0);
145 ESP_LOGW(TAG,
"OTA upload refused: another update is in progress");
146 this->ota_backend_.reset();
147 this->refuse_(request);
152 this->ota_init_(filename.c_str());
154#ifdef USE_OTA_STATE_LISTENER
160 ESP_LOGE(TAG,
"OTA begin failed: %d", error_code);
161 this->ota_backend_.reset();
162#ifdef USE_OTA_STATE_LISTENER
169 this->ota_request_ = request;
170 request->onDisconnect([
this, request]() {
171 if (this->ota_request_ == request && this->ota_backend_) {
172 ESP_LOGW(TAG,
"OTA upload interrupted; aborting");
173 this->abort_session_();
178 if (!this->ota_backend_ || request != this->ota_request_) {
184 error_code = this->ota_backend_->write(data,
len);
186 ESP_LOGE(TAG,
"OTA write failed: %d", error_code);
187 this->ota_backend_->abort();
188 this->ota_backend_.reset();
189 this->ota_request_ =
nullptr;
190#ifdef USE_OTA_STATE_LISTENER
195 this->ota_read_length_ +=
len;
196 this->report_ota_progress_(request);
201 ESP_LOGD(TAG,
"OTA final chunk: index=%zu, len=%zu, total_read=%" PRIu32
", contentLength=%zu", index,
len,
202 this->ota_read_length_, request->contentLength());
207 error_code = this->ota_backend_->end();
209 this->ota_success_ =
true;
210#ifdef USE_OTA_STATE_LISTENER
214 this->schedule_ota_reboot_();
216 ESP_LOGE(TAG,
"OTA end failed: %d", error_code);
218 this->ota_backend_->abort();
219#ifdef USE_OTA_STATE_LISTENER
223 this->ota_backend_.reset();
224 this->ota_request_ =
nullptr;
228void OTARequestHandler::abort_session_() {
229 this->ota_backend_->abort();
230#ifdef USE_OTA_STATE_LISTENER
233 this->ota_backend_.reset();
234 this->ota_request_ =
nullptr;
237void OTARequestHandler::refuse_(AsyncWebServerRequest *request) {
238 this->refused_request_ = request;
239 request->onDisconnect([
this, request]() {
240 if (this->refused_request_ == request)
241 this->refused_request_ =
nullptr;
245void OTARequestHandler::handleRequest(AsyncWebServerRequest *request) {
246 AsyncWebServerResponse *response;
248 const bool refused = request == this->refused_request_;
250 this->refused_request_ =
nullptr;
252 static const char UPDATE_SUCCESS[]
PROGMEM =
"Update Successful!";
253 static const char UPDATE_FAILED[]
PROGMEM =
"Update Failed!";
254 static const char UPDATE_BUSY[]
PROGMEM =
"Update Failed! Another update is in progress, retry later.";
255 static const char TEXT_PLAIN[]
PROGMEM =
"text/plain";
256 static const char CONNECTION_STR[]
PROGMEM =
"Connection";
257 static const char CLOSE_STR[]
PROGMEM =
"close";
258 const char *msg = refused ? UPDATE_BUSY : (this->ota_success_ ? UPDATE_SUCCESS : UPDATE_FAILED);
259 response = request->beginResponse_P(200, TEXT_PLAIN, msg);
260 response->addHeader(CONNECTION_STR, CLOSE_STR);
262 const char *msg = refused ?
"Update Failed! Another update is in progress, retry later."
263 : this->ota_success_ ?
"Update Successful!"
265 response = request->beginResponse(200,
"text/plain", msg);
266 response->addHeader(
"Connection",
"close");
268 request->send(response);
274 if (base ==
nullptr) {
275 ESP_LOGE(TAG,
"WebServerBase not found");
void mark_failed()
Mark this component as failed.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* 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_UPDATE_BUSY
@ OTA_RESPONSE_ERROR_UNKNOWN
std::unique_ptr< ArduinoLibreTinyOTABackend > make_ota_backend()
WebServerBase * global_web_server_base
ESPHOME_LOG_TAG(TAG, "web_server.ota")
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