webapp: disable OTA firmware dialog for unsupported devices

show a warning instead and cancel uploads with a HTPP 500 response.
This commit is contained in:
Bernhard Kirchen 2024-08-03 20:19:45 +02:00 committed by Bernhard Kirchen
parent 4334e60030
commit 04513d3f22
7 changed files with 57 additions and 2 deletions

View File

@ -9,6 +9,9 @@ public:
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);
};

View File

@ -9,6 +9,7 @@
#include "WebApi.h"
#include "helper.h"
#include <AsyncJson.h>
#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__);
}

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -0,0 +1,3 @@
export interface FirmwareStatus {
ota_supported: boolean;
}

View File

@ -48,6 +48,14 @@
</div>
</CardElement>
<CardElement
:text="$t('firmwareupgrade.OtaError')"
textVariant="text-bg-warning"
v-else-if="!loading && firmwareStatus.ota_supported === false"
>
{{ $t('firmwareupgrade.NoOtaSupport') }}
</CardElement>
<CardElement
:text="$t('firmwareupgrade.FirmwareUpload')"
textVariant="text-bg-primary"
@ -83,8 +91,9 @@
<script lang="ts">
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);