From 04513d3f22a02cd2cd45cbf5ef6ed7d12821479a Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Sat, 3 Aug 2024 20:19:45 +0200 Subject: [PATCH] webapp: disable OTA firmware dialog for unsupported devices show a warning instead and cancel uploads with a HTPP 500 response. --- include/WebApi_firmware.h | 3 +++ src/WebApi_firmware.cpp | 28 ++++++++++++++++++++++++ webapp/src/locales/de.json | 1 + webapp/src/locales/en.json | 1 + webapp/src/locales/fr.json | 1 + webapp/src/types/FirmwareStatus.ts | 3 +++ webapp/src/views/FirmwareUpgradeView.vue | 22 +++++++++++++++++-- 7 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 webapp/src/types/FirmwareStatus.ts diff --git a/include/WebApi_firmware.h b/include/WebApi_firmware.h index 990a5e064..d4822afa3 100644 --- a/include/WebApi_firmware.h +++ b/include/WebApi_firmware.h @@ -9,6 +9,9 @@ class WebApiFirmwareClass { void init(AsyncWebServer& server, Scheduler& scheduler); private: + bool otaSupported() const; + void onFirmwareUpdateFinish(AsyncWebServerRequest* request); void onFirmwareUpdateUpload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); + void onFirmwareStatus(AsyncWebServerRequest* request); }; diff --git a/src/WebApi_firmware.cpp b/src/WebApi_firmware.cpp index 9491f935d..6988b7fc9 100644 --- a/src/WebApi_firmware.cpp +++ b/src/WebApi_firmware.cpp @@ -9,6 +9,7 @@ #include "WebApi.h" #include "helper.h" #include +#include "esp_partition.h" void WebApiFirmwareClass::init(AsyncWebServer& server, Scheduler& scheduler) { @@ -22,6 +23,15 @@ void WebApiFirmwareClass::init(AsyncWebServer& server, Scheduler& scheduler) server.on("/api/firmware/update", HTTP_POST, std::bind(&WebApiFirmwareClass::onFirmwareUpdateFinish, this, _1), std::bind(&WebApiFirmwareClass::onFirmwareUpdateUpload, this, _1, _2, _3, _4, _5, _6)); + + server.on("/api/firmware/status", HTTP_GET, std::bind(&WebApiFirmwareClass::onFirmwareStatus, this, _1)); +} + +bool WebApiFirmwareClass::otaSupported() const +{ + const esp_partition_t* pOtaPartition = esp_partition_find_first( + ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL); + return (pOtaPartition != nullptr); } void WebApiFirmwareClass::onFirmwareUpdateFinish(AsyncWebServerRequest* request) @@ -46,6 +56,10 @@ void WebApiFirmwareClass::onFirmwareUpdateUpload(AsyncWebServerRequest* request, return; } + if (!otaSupported()) { + return request->send(500, "text/plain", "OTA updates not supported"); + } + // Upload handler chunks in data if (!index) { if (!request->hasParam("MD5", true)) { @@ -78,3 +92,17 @@ void WebApiFirmwareClass::onFirmwareUpdateUpload(AsyncWebServerRequest* request, return; } } + +void WebApiFirmwareClass::onFirmwareStatus(AsyncWebServerRequest* request) +{ + if (!WebApi.checkCredentialsReadonly(request)) { + return; + } + + AsyncJsonResponse* response = new AsyncJsonResponse(); + auto& root = response->getRoot(); + + root["ota_supported"] = otaSupported(); + + WebApi.sendJsonResponse(request, response, __FUNCTION__, __LINE__); +} diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index d164aa26a..7771a0c22 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -766,6 +766,7 @@ "firmwareupgrade": { "FirmwareUpgrade": "Firmware-Aktualisierung", "Loading": "@:base.Loading", + "NoOtaSupport": "Diese Plattform unterstützt keine OTA-Updates. Neue Firmware kann über die USB-Schnittstelle in den Flash-Speicher geschrieben werden.", "OtaError": "OTA-Fehler", "Back": "Zurück", "Retry": "Wiederholen", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 2cc073d0d..0be700f01 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -769,6 +769,7 @@ "firmwareupgrade": { "FirmwareUpgrade": "Firmware Upgrade", "Loading": "@:base.Loading", + "NoOtaSupport": "This platform does not support OTA updates. New firmware can be written to flash memory using the USB interface.", "OtaError": "OTA Error", "Back": "Back", "Retry": "Retry", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index d33a66e5d..316d19d69 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -742,6 +742,7 @@ "firmwareupgrade": { "FirmwareUpgrade": "Mise à jour du firmware", "Loading": "@:base.Loading", + "NoOtaSupport": "Cette plateforme ne prend pas en charge les mises à jour OTA. Les nouveaux microprogrammes peuvent être enregistrés dans la mémoire flash à l'aide de l'interface USB.", "OtaError": "Erreur OTA", "Back": "Retour", "Retry": "Réessayer", diff --git a/webapp/src/types/FirmwareStatus.ts b/webapp/src/types/FirmwareStatus.ts new file mode 100644 index 000000000..37aaeb055 --- /dev/null +++ b/webapp/src/types/FirmwareStatus.ts @@ -0,0 +1,3 @@ +export interface FirmwareStatus { + ota_supported: boolean; +} diff --git a/webapp/src/views/FirmwareUpgradeView.vue b/webapp/src/views/FirmwareUpgradeView.vue index 3828d00e9..df8e43046 100644 --- a/webapp/src/views/FirmwareUpgradeView.vue +++ b/webapp/src/views/FirmwareUpgradeView.vue @@ -48,6 +48,14 @@ + + {{ $t('firmwareupgrade.NoOtaSupport') }} + + import BasePage from '@/components/BasePage.vue'; +import type { FirmwareStatus } from '@/types/FirmwareStatus'; import CardElement from '@/components/CardElement.vue'; -import { authHeader, isLoggedIn } from '@/utils/authentication'; +import { authHeader, isLoggedIn, handleResponse } from '@/utils/authentication'; import { BIconArrowLeft, BIconArrowRepeat, BIconCheckCircle, BIconExclamationCircleFill } from 'bootstrap-icons-vue'; import SparkMD5 from 'spark-md5'; import { defineComponent } from 'vue'; @@ -108,6 +117,7 @@ export default defineComponent({ type: 'firmware', file: {} as Blob, hostCheckInterval: 0, + firmwareStatus: {} as FirmwareStatus, }; }, methods: { @@ -219,6 +229,14 @@ export default defineComponent({ console.log('Browser is offline. Cannot check remote host.'); } }, + getFirmwareStatus() { + fetch('/api/firmware/status', { headers: authHeader() }) + .then((response) => handleResponse(response, this.$emitter, this.$router)) + .then((data) => { + this.firmwareStatus = data; + this.loading = false; + }); + }, }, mounted() { if (!isLoggedIn()) { @@ -227,7 +245,7 @@ export default defineComponent({ query: { returnUrl: this.$router.currentRoute.value.fullPath }, }); } - this.loading = false; + this.getFirmwareStatus(); }, unmounted() { clearInterval(this.hostCheckInterval);