#include class UpgradeHandler : public RequestHandler { public: enum class UpgradeType { FIRMWARE = 0, FILESYSTEM = 1 }; enum class UpgradeStatus { NONE, NO_FILE, SUCCESS, PROHIBITED, ABORTED, ERROR_ON_START, ERROR_ON_WRITE, ERROR_ON_FINISH }; typedef struct { UpgradeType type; UpgradeStatus status; String error; } UpgradeResult; typedef std::function CanHandleCallback; typedef std::function CanUploadCallback; typedef std::function BeforeUpgradeCallback; typedef std::function AfterUpgradeCallback; UpgradeHandler(const char* uri) { this->uri = uri; } UpgradeHandler* setCanHandleCallback(CanHandleCallback callback = nullptr) { this->canHandleCallback = callback; return this; } UpgradeHandler* setCanUploadCallback(CanUploadCallback callback = nullptr) { this->canUploadCallback = callback; return this; } UpgradeHandler* setBeforeUpgradeCallback(BeforeUpgradeCallback callback = nullptr) { this->beforeUpgradeCallback = callback; return this; } UpgradeHandler* setAfterUpgradeCallback(AfterUpgradeCallback callback = nullptr) { this->afterUpgradeCallback = callback; return this; } #if defined(ARDUINO_ARCH_ESP32) bool canHandle(HTTPMethod method, const String uri) override { #else bool canHandle(HTTPMethod method, const String& uri) override { #endif return method == HTTP_POST && uri.equals(this->uri) && (!this->canHandleCallback || this->canHandleCallback(method, uri)); } #if defined(ARDUINO_ARCH_ESP32) bool canUpload(const String uri) override { #else bool canUpload(const String& uri) override { #endif return uri.equals(this->uri) && (!this->canUploadCallback || this->canUploadCallback(uri)); } #if defined(ARDUINO_ARCH_ESP32) bool handle(WebServer& server, HTTPMethod method, const String uri) override { #else bool handle(WebServer& server, HTTPMethod method, const String& uri) override { #endif if (this->afterUpgradeCallback) { this->afterUpgradeCallback(this->firmwareResult, this->filesystemResult); } this->firmwareResult.status = UpgradeStatus::NONE; this->firmwareResult.error.clear(); this->filesystemResult.status = UpgradeStatus::NONE; this->filesystemResult.error.clear(); return true; } #if defined(ARDUINO_ARCH_ESP32) void upload(WebServer& server, const String uri, HTTPUpload& upload) override { #else void upload(WebServer& server, const String& uri, HTTPUpload& upload) override { #endif UpgradeResult* result; if (upload.name.equals("firmware")) { result = &this->firmwareResult; } else if (upload.name.equals("filesystem")) { result = &this->filesystemResult; } else { return; } if (result->status != UpgradeStatus::NONE) { return; } if (this->beforeUpgradeCallback && !this->beforeUpgradeCallback(result->type)) { result->status = UpgradeStatus::PROHIBITED; return; } if (!upload.filename.length()) { result->status = UpgradeStatus::NO_FILE; return; } if (upload.status == UPLOAD_FILE_START) { // reset if (Update.isRunning()) { Update.end(false); Update.clearError(); } bool begin = false; #ifdef ARDUINO_ARCH_ESP8266 Update.runAsync(true); if (result->type == UpgradeType::FIRMWARE) { begin = Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000, U_FLASH); } else if (result->type == UpgradeType::FILESYSTEM) { close_all_fs(); begin = Update.begin((size_t)FS_end - (size_t)FS_start, U_FS); } #elif defined(ARDUINO_ARCH_ESP32) if (result->type == UpgradeType::FIRMWARE) { begin = Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH); } else if (result->type == UpgradeType::FILESYSTEM) { begin = Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS); } #endif if (!begin || Update.hasError()) { result->status = UpgradeStatus::ERROR_ON_START; #ifdef ARDUINO_ARCH_ESP8266 result->error = Update.getErrorString(); #else result->error = Update.errorString(); #endif Log.serrorln(FPSTR(L_PORTAL_OTA), F("File '%s', on start: %s"), upload.filename.c_str(), result->error.c_str()); return; } Log.sinfoln(FPSTR(L_PORTAL_OTA), F("File '%s', started"), upload.filename.c_str()); } else if (upload.status == UPLOAD_FILE_WRITE) { if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { Update.end(false); result->status = UpgradeStatus::ERROR_ON_WRITE; #ifdef ARDUINO_ARCH_ESP8266 result->error = Update.getErrorString(); #else result->error = Update.errorString(); #endif Log.serrorln( FPSTR(L_PORTAL_OTA), F("File '%s', on writing %d bytes: %s"), upload.filename.c_str(), upload.totalSize, result->error.c_str() ); } else { Log.sinfoln(FPSTR(L_PORTAL_OTA), F("File '%s', writed %d bytes"), upload.filename.c_str(), upload.totalSize); } } else if (upload.status == UPLOAD_FILE_END) { if (Update.end(true)) { result->status = UpgradeStatus::SUCCESS; Log.sinfoln(FPSTR(L_PORTAL_OTA), F("File '%s': finish"), upload.filename.c_str()); } else { result->status = UpgradeStatus::ERROR_ON_FINISH; #ifdef ARDUINO_ARCH_ESP8266 result->error = Update.getErrorString(); #else result->error = Update.errorString(); #endif Log.serrorln(FPSTR(L_PORTAL_OTA), F("File '%s', on finish: %s"), upload.filename.c_str(), result->error); } } else if (upload.status == UPLOAD_FILE_ABORTED) { Update.end(false); result->status = UpgradeStatus::ABORTED; Log.serrorln(FPSTR(L_PORTAL_OTA), F("File '%s': aborted"), upload.filename.c_str()); } } protected: CanHandleCallback canHandleCallback; CanUploadCallback canUploadCallback; BeforeUpgradeCallback beforeUpgradeCallback; AfterUpgradeCallback afterUpgradeCallback; const char* uri = nullptr; UpgradeResult firmwareResult{UpgradeType::FIRMWARE, UpgradeStatus::NONE}; UpgradeResult filesystemResult{UpgradeType::FILESYSTEM, UpgradeStatus::NONE}; };