Files
2024-07-09 20:31:31 +02:00

136 lines
5.0 KiB
C++

/**
* ESP32 SvelteKit
*
* A simple, secure and extensible framework for IoT projects for ESP32 platforms
* with responsive Sveltekit front-end built with TailwindCSS and DaisyUI.
* https://github.com/theelims/ESP32-sveltekit
*
* Copyright (C) 2023 theelims
*
* All Rights Reserved. This software may be modified and distributed under
* the terms of the LGPL v3 license. See the LICENSE file for details.
**/
#include <DownloadFirmwareService.h>
extern const uint8_t rootca_crt_bundle_start[] asm("_binary_src_certs_x509_crt_bundle_bin_start");
static EventSocket *_socket = nullptr;
static int previousProgress = 0;
JsonDocument doc;
void update_started() {
String output;
doc["status"] = "preparing";
serializeJson(doc, output);
_socket->emit(EVENT_DOWNLOAD_OTA, output.c_str());
}
void update_progress(int currentBytes, int totalBytes) {
String output;
doc["status"] = "progress";
int progress = ((currentBytes * 100) / totalBytes);
if (progress > previousProgress) {
doc["progress"] = progress;
_socket->emit(EVENT_DOWNLOAD_OTA, output.c_str());
ESP_LOGV("Download OTA", "HTTP update process at %d of %d bytes... (%d %%)", currentBytes, totalBytes,
progress);
}
previousProgress = progress;
}
void update_finished() {
String output;
doc["status"] = "finished";
serializeJson(doc, output);
_socket->emit(EVENT_DOWNLOAD_OTA, output.c_str());
// delay to allow the event to be sent out
vTaskDelay(100 / portTICK_PERIOD_MS);
}
void updateTask(void *param) {
WiFiClientSecure client;
client.setCACertBundle(rootca_crt_bundle_start);
client.setTimeout(10);
httpUpdate.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
httpUpdate.rebootOnUpdate(true);
String url = *((String *)param);
String output;
// httpUpdate.onStart(update_started);
// httpUpdate.onProgress(update_progress);
// httpUpdate.onEnd(update_finished);
t_httpUpdate_return ret = httpUpdate.update(client, url.c_str());
switch (ret) {
case HTTP_UPDATE_FAILED:
doc["status"] = "error";
doc["error"] = httpUpdate.getLastErrorString().c_str();
serializeJson(doc, output);
_socket->emit(EVENT_DOWNLOAD_OTA, output.c_str());
ESP_LOGE("Download OTA", "HTTP Update failed with error (%d): %s", httpUpdate.getLastError(),
httpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
doc["status"] = "error";
doc["error"] = "Update failed, has same firmware version";
serializeJson(doc, output);
_socket->emit(EVENT_DOWNLOAD_OTA, output.c_str());
ESP_LOGE("Download OTA", "HTTP Update failed, has same firmware version");
break;
case HTTP_UPDATE_OK: ESP_LOGI("Download OTA", "HTTP Update successful - Restarting"); break;
}
vTaskDelete(NULL);
}
DownloadFirmwareService::DownloadFirmwareService(PsychicHttpServer *server, SecurityManager *securityManager,
EventSocket *socket, TaskManager *taskManager)
: _server(server), _securityManager(securityManager), _socket(socket), _taskManager(taskManager) {}
void DownloadFirmwareService::begin() {
_server->on(GITHUB_FIRMWARE_PATH, HTTP_POST,
_securityManager->wrapCallback(std::bind(&DownloadFirmwareService::downloadUpdate, this,
std::placeholders::_1, std::placeholders::_2),
AuthenticationPredicates::IS_ADMIN));
ESP_LOGV("DownloadFirmwareService", "Registered POST endpoint: %s", GITHUB_FIRMWARE_PATH);
}
esp_err_t DownloadFirmwareService::downloadUpdate(PsychicRequest *request, JsonVariant &json) {
if (!json.is<JsonObject>()) {
return request->reply(400);
}
String downloadURL = json["download_url"];
ESP_LOGI("Download OTA", "Starting OTA from: %s", downloadURL.c_str());
doc["status"] = "preparing";
doc["progress"] = 0;
doc["error"] = "";
String output;
serializeJson(doc, output);
_socket->emit(EVENT_DOWNLOAD_OTA, output.c_str());
if (_taskManager->createTask(&updateTask, // Function that should be called
"Firmware download", // Name of the task (for debugging)
OTA_TASK_STACK_SIZE, // Stack size (bytes)
&downloadURL, // Pass reference to this class instance
(configMAX_PRIORITIES - 1), // Pretty high task priority
NULL, // Task handle
1 // Have it on application core
) != pdPASS) {
ESP_LOGE("Download OTA", "Couldn't create download OTA task");
return request->reply(500);
}
return request->reply(200);
}