170 lines
5.7 KiB
C++
170 lines
5.7 KiB
C++
#ifndef UNIT_TEST
|
|
#include <fetchOTA.h>
|
|
|
|
|
|
#include "Arduino.h"
|
|
#include <ArduinoJson.h>
|
|
#include <HTTPClient.h>
|
|
#include <vector>
|
|
#include <Elog.h>
|
|
#include <HTTPUpdate.h>
|
|
|
|
|
|
OTA::OTA(String server_url, Version currentVersion, String currentDeviceConfiguration) {
|
|
_serverUrl = server_url;
|
|
_currentVersion = currentVersion;
|
|
_current_device_configuration = currentDeviceConfiguration;
|
|
_current_device_configuration.toLowerCase();
|
|
}
|
|
|
|
|
|
Firmware OTA::getLatestVersionOnServer() {
|
|
HTTPClient http;
|
|
http.begin(_serverUrl);
|
|
int httpCode = http.GET();
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "HTTP Code: %d", httpCode);
|
|
|
|
if (httpCode != 200) {
|
|
return createErrorResponse("HTTP GET request failed with code " + String(httpCode));
|
|
}
|
|
|
|
String payload = http.getString();
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Payload: %s", payload.c_str());
|
|
DynamicJsonDocument doc(4096);
|
|
DeserializationError error = deserializeJson(doc, payload);
|
|
|
|
if (error) {
|
|
return createErrorResponse("Failed to deserialize JSON");
|
|
}
|
|
|
|
if (!doc.containsKey("Configurations")) {
|
|
return createErrorResponse("JSON response does not contain a 'Configurations' key");
|
|
}
|
|
|
|
std::vector<Configuration> configs;
|
|
for (JsonObject config : doc["Configurations"].as<JsonArray>()) {
|
|
if (config.containsKey("Version") && config.containsKey("URL") && config.containsKey("Config")) {
|
|
String deviceConfig = config["Config"].as<String>();
|
|
deviceConfig.toLowerCase();
|
|
if (deviceConfig == _current_device_configuration) {
|
|
configs.push_back(Configuration{
|
|
parseVersion(config["Version"]),
|
|
config["URL"],
|
|
config["Board"],
|
|
deviceConfig
|
|
});
|
|
} else {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Configuration %s does not match current device configuration %s", deviceConfig.c_str(), _current_device_configuration.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (configs.empty()) {
|
|
return createErrorResponse("No valid configuration found in the JSON response");
|
|
}
|
|
|
|
|
|
Configuration latest = getLatestConfiguration(configs.data(), configs.size());
|
|
|
|
if (!isVersionNewer(_currentVersion, latest.version)) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "No newer version found. Server version: %d.%d.%d", latest.version.major, latest.version.minor, latest.version.patch);
|
|
}
|
|
|
|
return Firmware{
|
|
latest.version,
|
|
latest.url,
|
|
true,
|
|
""
|
|
};
|
|
}
|
|
|
|
Firmware OTA::createErrorResponse(const String& errorMsg) {
|
|
return Firmware{
|
|
Version{0, 0, 0},
|
|
"",
|
|
false,
|
|
errorMsg
|
|
};
|
|
}
|
|
|
|
void run_ota_update(String url, std::function<void()> callback_started, std::function<void()> callback_finished, std::function<void(int, int)> callback_progress, std::function<void(int)> callback_error) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Starting OTA upgrade");
|
|
HTTPUpdate httpUpdate;
|
|
httpUpdate.onStart(callback_started);
|
|
httpUpdate.onEnd(callback_finished);
|
|
httpUpdate.onProgress(callback_progress);
|
|
httpUpdate.onError(callback_error);
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Defined callbacks, Starting update now");
|
|
|
|
t_httpUpdate_return ret;
|
|
|
|
if (url.startsWith("https")) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "HTTPS URL");
|
|
WiFiClient client;
|
|
// client.setInsecure();
|
|
ret = httpUpdate.update(client, url);
|
|
} else if (url.startsWith("http")) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "HTTP URL");
|
|
WiFiClient client;
|
|
ret = httpUpdate.update(client, url);
|
|
} else {
|
|
Logger.log(0, ELOG_LEVEL_ERROR, "URL is not valid: \n%s", url.c_str());
|
|
}
|
|
|
|
switch (ret) {
|
|
case HTTP_UPDATE_FAILED:
|
|
Logger.log(0, ELOG_LEVEL_ERROR, "HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
|
break;
|
|
|
|
case HTTP_UPDATE_NO_UPDATES:
|
|
Logger.log(0, ELOG_LEVEL_ERROR, "HTTP_UPDATE_NO_UPDATES");
|
|
break;
|
|
|
|
case HTTP_UPDATE_OK:
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Update done");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void run_ota_spiffs_update(String url, std::function<void()> callback_started, std::function<void()> callback_finished, std::function<void(int, int)> callback_progress, std::function<void(int)> callback_error) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Starting OTA SPIFFS upgrade");
|
|
HTTPUpdate httpUpdate;
|
|
httpUpdate.onStart(callback_started);
|
|
httpUpdate.onEnd(callback_finished);
|
|
httpUpdate.onProgress(callback_progress);
|
|
httpUpdate.onError(callback_error);
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "Defined callbacks, Starting update now");
|
|
|
|
t_httpUpdate_return ret;
|
|
|
|
if (url.startsWith("https")) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "HTTPS URL");
|
|
WiFiClient client;
|
|
// client.setInsecure();
|
|
ret = httpUpdate.updateSpiffs(client, url);
|
|
} else if (url.startsWith("http")) {
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "HTTP URL");
|
|
WiFiClient client;
|
|
ret = httpUpdate.updateSpiffs(client, url);
|
|
} else {
|
|
Logger.log(0, ELOG_LEVEL_ERROR, "URL is not valid: \n%s", url.c_str());
|
|
}
|
|
|
|
switch (ret) {
|
|
case HTTP_UPDATE_FAILED:
|
|
Logger.log(0, ELOG_LEVEL_ERROR, "HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
|
break;
|
|
|
|
case HTTP_UPDATE_NO_UPDATES:
|
|
Logger.log(0, ELOG_LEVEL_ERROR, "HTTP_UPDATE_NO_UPDATES");
|
|
break;
|
|
|
|
case HTTP_UPDATE_OK:
|
|
Logger.log(0, ELOG_LEVEL_DEBUG, "SPIFFS Update done");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#endif |