mirror of
https://github.com/btclock/btclock_v3.git
synced 2024-11-19 02:30:01 +01:00
Add single-click auto-update functionality
This commit is contained in:
parent
d00c216126
commit
023ff29131
2
data
2
data
@ -1 +1 @@
|
|||||||
Subproject commit 1c2d8dcdd0efd846f39b0f24977c982a96f16392
|
Subproject commit 6c40b54273b7f7c7d6c2624d3c2a066435f27756
|
@ -4,6 +4,8 @@ TaskHandle_t taskOtaHandle = NULL;
|
|||||||
bool isOtaUpdating = false;
|
bool isOtaUpdating = false;
|
||||||
QueueHandle_t otaQueue;
|
QueueHandle_t otaQueue;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void setupOTA()
|
void setupOTA()
|
||||||
{
|
{
|
||||||
if (preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED))
|
if (preferences.getBool("otaEnabled", DEFAULT_OTA_ENABLED))
|
||||||
@ -65,7 +67,7 @@ void onOTAStart()
|
|||||||
vTaskSuspend(workerTaskHandle);
|
vTaskSuspend(workerTaskHandle);
|
||||||
vTaskSuspend(taskScreenRotateTaskHandle);
|
vTaskSuspend(taskScreenRotateTaskHandle);
|
||||||
|
|
||||||
vTaskSuspend(ledTaskHandle);
|
// vTaskSuspend(ledTaskHandle);
|
||||||
vTaskSuspend(buttonTaskHandle);
|
vTaskSuspend(buttonTaskHandle);
|
||||||
|
|
||||||
// stopWebServer();
|
// stopWebServer();
|
||||||
@ -81,7 +83,18 @@ void handleOTATask(void *parameter)
|
|||||||
{
|
{
|
||||||
if (xQueueReceive(otaQueue, &msg, 0) == pdTRUE)
|
if (xQueueReceive(otaQueue, &msg, 0) == pdTRUE)
|
||||||
{
|
{
|
||||||
int result = downloadUpdateHandler(msg.updateType);
|
if (msg.updateType == UPDATE_ALL) {
|
||||||
|
int resultWebUi = downloadUpdateHandler(UPDATE_WEBUI);
|
||||||
|
int resultFw = downloadUpdateHandler(UPDATE_FIRMWARE);
|
||||||
|
|
||||||
|
if (resultWebUi == 0 && resultFw == 0) {
|
||||||
|
ESP.restart();
|
||||||
|
} else {
|
||||||
|
queueLedEffect(LED_FLASH_ERROR);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ArduinoOTA.handle(); // Allow OTA updates to occur
|
ArduinoOTA.handle(); // Allow OTA updates to occur
|
||||||
@ -89,7 +102,7 @@ void handleOTATask(void *parameter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String getLatestRelease(const String &fileToDownload)
|
ReleaseInfo getLatestRelease(const String &fileToDownload)
|
||||||
{
|
{
|
||||||
String releaseUrl = "https://api.github.com/repos/btclock/btclock_v3/releases/latest";
|
String releaseUrl = "https://api.github.com/repos/btclock/btclock_v3/releases/latest";
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
@ -100,7 +113,7 @@ String getLatestRelease(const String &fileToDownload)
|
|||||||
|
|
||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
|
|
||||||
String downloadUrl = "";
|
ReleaseInfo info = {"", ""};
|
||||||
|
|
||||||
if (httpCode > 0)
|
if (httpCode > 0)
|
||||||
{
|
{
|
||||||
@ -113,15 +126,26 @@ String getLatestRelease(const String &fileToDownload)
|
|||||||
|
|
||||||
for (JsonObject asset : assets)
|
for (JsonObject asset : assets)
|
||||||
{
|
{
|
||||||
if (asset["name"] == fileToDownload)
|
String assetName = asset["name"].as<String>();
|
||||||
|
if (assetName == fileToDownload)
|
||||||
|
{
|
||||||
|
info.fileUrl = asset["browser_download_url"].as<String>();
|
||||||
|
}
|
||||||
|
else if (assetName == fileToDownload + ".sha256")
|
||||||
|
{
|
||||||
|
info.checksumUrl = asset["browser_download_url"].as<String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.fileUrl.isEmpty() && !info.checksumUrl.isEmpty())
|
||||||
{
|
{
|
||||||
downloadUrl = asset["browser_download_url"].as<String>();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Serial.printf("Latest release URL: %s\r\n", downloadUrl.c_str());
|
Serial.printf("Latest release URL: %s\r\n", info.fileUrl.c_str());
|
||||||
|
Serial.printf("Checksum URL: %s\r\n", info.checksumUrl.c_str());
|
||||||
}
|
}
|
||||||
return downloadUrl;
|
http.end();
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
int downloadUpdateHandler(char updateType)
|
int downloadUpdateHandler(char updateType)
|
||||||
@ -131,7 +155,7 @@ int downloadUpdateHandler(char updateType)
|
|||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||||
|
|
||||||
String latestRelease = "";
|
ReleaseInfo latestRelease;
|
||||||
|
|
||||||
switch (updateType)
|
switch (updateType)
|
||||||
{
|
{
|
||||||
@ -143,25 +167,22 @@ int downloadUpdateHandler(char updateType)
|
|||||||
case UPDATE_WEBUI:
|
case UPDATE_WEBUI:
|
||||||
{
|
{
|
||||||
latestRelease = getLatestRelease("littlefs.bin");
|
latestRelease = getLatestRelease("littlefs.bin");
|
||||||
updateWebUi(latestRelease, U_SPIFFS);
|
// updateWebUi(latestRelease.fileUrl, U_SPIFFS);
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (latestRelease.isEmpty())
|
|
||||||
{
|
|
||||||
return 503;
|
|
||||||
}
|
|
||||||
// First, download the expected SHA256
|
// First, download the expected SHA256
|
||||||
String expectedSHA256 = downloadSHA256(getFirmwareFilename());
|
String expectedSHA256 = downloadSHA256(latestRelease.checksumUrl);
|
||||||
if (expectedSHA256.isEmpty())
|
if (expectedSHA256.isEmpty())
|
||||||
{
|
{
|
||||||
Serial.println("Failed to get SHA256 checksum. Aborting update.");
|
Serial.println("Failed to get SHA256 checksum. Aborting update.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
http.begin(client, latestRelease);
|
http.begin(client, latestRelease.fileUrl);
|
||||||
http.setUserAgent(USER_AGENT);
|
http.setUserAgent(USER_AGENT);
|
||||||
|
|
||||||
int httpCode = http.GET();
|
int httpCode = http.GET();
|
||||||
@ -215,19 +236,21 @@ int downloadUpdateHandler(char updateType)
|
|||||||
|
|
||||||
Update.onProgress(onOTAProgress);
|
Update.onProgress(onOTAProgress);
|
||||||
|
|
||||||
int updateType = (updateType == UPDATE_WEBUI) ? U_SPIFFS : U_FLASH;
|
|
||||||
|
|
||||||
if (Update.begin(contentLength, updateType))
|
if (Update.begin(contentLength, updateType))
|
||||||
{
|
{
|
||||||
size_t written = Update.writeStream(*stream);
|
onOTAStart();
|
||||||
|
size_t written = Update.write(firmware, contentLength);
|
||||||
|
|
||||||
if (written == contentLength)
|
if (written == contentLength)
|
||||||
{
|
{
|
||||||
Serial.println("Written : " + String(written) + " successfully");
|
Serial.println("Written : " + String(written) + " successfully");
|
||||||
|
free(firmware);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?");
|
Serial.println("Written only : " + String(written) + "/" + String(contentLength) + ". Retry?");
|
||||||
|
free(firmware);
|
||||||
|
return 503;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Update.end())
|
if (Update.end())
|
||||||
@ -236,26 +259,33 @@ int downloadUpdateHandler(char updateType)
|
|||||||
if (Update.isFinished())
|
if (Update.isFinished())
|
||||||
{
|
{
|
||||||
Serial.println("Update successfully completed. Rebooting.");
|
Serial.println("Update successfully completed. Rebooting.");
|
||||||
ESP.restart();
|
// ESP.restart();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Update not finished? Something went wrong!");
|
Serial.println("Update not finished? Something went wrong!");
|
||||||
|
free(firmware);
|
||||||
|
return 503;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
|
Serial.println("Error Occurred. Error #: " + String(Update.getError()));
|
||||||
|
free(firmware);
|
||||||
|
return 503;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Not enough space to begin OTA");
|
Serial.println("Not enough space to begin OTA");
|
||||||
|
free(firmware);
|
||||||
|
return 503;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Serial.println("Invalid content length");
|
Serial.println("Invalid content length");
|
||||||
|
return 503;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -265,7 +295,7 @@ int downloadUpdateHandler(char updateType)
|
|||||||
}
|
}
|
||||||
http.end();
|
http.end();
|
||||||
|
|
||||||
return 200;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateWebUi(String latestRelease, int command)
|
void updateWebUi(String latestRelease, int command)
|
||||||
@ -380,9 +410,8 @@ bool getIsOTAUpdating()
|
|||||||
return isOtaUpdating;
|
return isOtaUpdating;
|
||||||
}
|
}
|
||||||
|
|
||||||
String downloadSHA256(const String &filename)
|
String downloadSHA256(const String &sha256Url)
|
||||||
{
|
{
|
||||||
String sha256Url = getLatestRelease(filename + ".sha256");
|
|
||||||
if (sha256Url.isEmpty())
|
if (sha256Url.isEmpty())
|
||||||
{
|
{
|
||||||
Serial.println("Failed to get SHA256 file URL");
|
Serial.println("Failed to get SHA256 file URL");
|
||||||
|
@ -15,6 +15,11 @@ typedef struct {
|
|||||||
|
|
||||||
extern QueueHandle_t otaQueue;
|
extern QueueHandle_t otaQueue;
|
||||||
|
|
||||||
|
struct ReleaseInfo {
|
||||||
|
String fileUrl;
|
||||||
|
String checksumUrl;
|
||||||
|
};
|
||||||
|
|
||||||
void setupOTA();
|
void setupOTA();
|
||||||
void onOTAStart();
|
void onOTAStart();
|
||||||
void handleOTATask(void *parameter);
|
void handleOTATask(void *parameter);
|
||||||
@ -23,9 +28,10 @@ void onOTAProgress(unsigned int progress, unsigned int total);
|
|||||||
void onOTAError(ota_error_t error);
|
void onOTAError(ota_error_t error);
|
||||||
void onOTAComplete();
|
void onOTAComplete();
|
||||||
int downloadUpdateHandler(char updateType);
|
int downloadUpdateHandler(char updateType);
|
||||||
String getLatestRelease(const String& fileToDownload);
|
ReleaseInfo getLatestRelease(const String& fileToDownload);
|
||||||
|
|
||||||
bool getIsOTAUpdating();
|
bool getIsOTAUpdating();
|
||||||
|
|
||||||
void updateWebUi(String latestRelease, int command);
|
void updateWebUi(String latestRelease, int command);
|
||||||
String downloadSHA256(const String& filename);
|
String downloadSHA256(const String& filename);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <GxEPD2.h>
|
#include <GxEPD2.h>
|
||||||
#include <GxEPD2_BW.h>
|
#include <GxEPD2_BW.h>
|
||||||
#include <mbedtls/md.h>
|
#include <mbedtls/md.h>
|
||||||
|
#include <Update.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
@ -69,9 +70,9 @@ const int usPerMinute = 60 * usPerSecond;
|
|||||||
|
|
||||||
extern const char *github_root_ca;
|
extern const char *github_root_ca;
|
||||||
|
|
||||||
const PROGMEM char UPDATE_FIRMWARE = 0;
|
const PROGMEM char UPDATE_FIRMWARE = U_FLASH;
|
||||||
const PROGMEM char UPDATE_WEBUI = 1;
|
const PROGMEM char UPDATE_WEBUI = U_SPIFFS;
|
||||||
|
const PROGMEM char UPDATE_ALL = 99;
|
||||||
|
|
||||||
struct ScreenMapping {
|
struct ScreenMapping {
|
||||||
int value;
|
int value;
|
||||||
|
@ -86,8 +86,7 @@ void setupWebserver()
|
|||||||
{
|
{
|
||||||
server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler);
|
server.on("/upload/firmware", HTTP_POST, onFirmwareUpdate, asyncFirmwareUpdateHandler);
|
||||||
server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler);
|
server.on("/upload/webui", HTTP_POST, onFirmwareUpdate, asyncWebuiUpdateHandler);
|
||||||
// server.on("/update/webui", HTTP_GET, onUpdateWebUi);
|
server.on("/api/firmware/auto_update", HTTP_GET, onAutoUpdateFirmware);
|
||||||
// server.on("/update/firmware", HTTP_GET, onUpdateFirmware);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server.on("/api/restart", HTTP_GET, onApiRestart);
|
server.on("/api/restart", HTTP_GET, onApiRestart);
|
||||||
@ -142,29 +141,16 @@ void onFirmwareUpdate(AsyncWebServerRequest *request)
|
|||||||
request->send(response);
|
request->send(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onUpdateWebUi(AsyncWebServerRequest *request)
|
void onAutoUpdateFirmware(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
UpdateMessage msg = {UPDATE_WEBUI};
|
UpdateMessage msg = {UPDATE_ALL};
|
||||||
if (xQueueSend(otaQueue, &msg, 0) == pdTRUE)
|
if (xQueueSend(otaQueue, &msg, 0) == pdTRUE)
|
||||||
{
|
{
|
||||||
request->send(200, "text/plain", "WebUI update triggered");
|
request->send(200, "application/json", "{\"msg\":\"Firmware update triggered\"}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
request->send(503, "text/plain", "Update already in progress");
|
request->send(503,"application/json", "{\"msg\":\"Update already in progress\"}");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onUpdateFirmware(AsyncWebServerRequest *request)
|
|
||||||
{
|
|
||||||
UpdateMessage msg = {UPDATE_FIRMWARE};
|
|
||||||
if (xQueueSend(otaQueue, &msg, 0) == pdTRUE)
|
|
||||||
{
|
|
||||||
request->send(200, "text/plain", "Firmware update triggered");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
request->send(503, "text/plain", "Update already in progress");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,10 +26,6 @@ bool processEpdColorSettings(AsyncWebServerRequest *request);
|
|||||||
void onApiStatus(AsyncWebServerRequest *request);
|
void onApiStatus(AsyncWebServerRequest *request);
|
||||||
void onApiSystemStatus(AsyncWebServerRequest *request);
|
void onApiSystemStatus(AsyncWebServerRequest *request);
|
||||||
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
void onApiSetWifiTxPower(AsyncWebServerRequest *request);
|
||||||
void onUpdateWebUi(AsyncWebServerRequest *request);
|
|
||||||
void onUpdateFirmware(AsyncWebServerRequest *request);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void onApiScreenNext(AsyncWebServerRequest *request);
|
void onApiScreenNext(AsyncWebServerRequest *request);
|
||||||
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
void onApiScreenPrevious(AsyncWebServerRequest *request);
|
||||||
@ -58,6 +54,7 @@ void onFirmwareUpdate(AsyncWebServerRequest *request);
|
|||||||
void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
void asyncFirmwareUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||||
void asyncFileUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final, int command);
|
void asyncFileUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final, int command);
|
||||||
void asyncWebuiUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
void asyncWebuiUpdateHandler(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
|
||||||
|
void onAutoUpdateFirmware(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
void onIndex(AsyncWebServerRequest *request);
|
void onIndex(AsyncWebServerRequest *request);
|
||||||
void onNotFound(AsyncWebServerRequest *request);
|
void onNotFound(AsyncWebServerRequest *request);
|
||||||
|
Loading…
Reference in New Issue
Block a user