feat: improve update progress reporting and enhance WebSocket notifications

This commit is contained in:
Manuel Weiser 2025-02-22 18:49:45 +01:00
parent d2da501b94
commit 5c59016f94
2 changed files with 135 additions and 41 deletions

View File

@ -157,25 +157,70 @@
// WebSocket für Update-Progress // WebSocket für Update-Progress
const ws = new WebSocket('ws://' + window.location.host + '/ws'); const ws = new WebSocket('ws://' + window.location.host + '/ws');
let updateInProgress = false;
ws.onmessage = function(event) { ws.onmessage = function(event) {
try { try {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
if (data.type === "updateProgress") { if (data.type === "updateProgress" && updateInProgress) {
progressContainer.style.display = 'block'; progressContainer.style.display = 'block';
// Setze den Fortschritt nur wenn er größer ist als der aktuelle
const currentProgress = parseInt(progress.textContent);
const newProgress = parseInt(data.progress);
if (isNaN(currentProgress) || newProgress > currentProgress) {
progress.style.width = data.progress + '%'; progress.style.width = data.progress + '%';
progress.textContent = data.progress + '%'; progress.textContent = data.progress + '%';
} }
// Zeige verschiedene Status-Nachrichten
if (data.status === "finalizing") {
status.textContent = "Finalizing update...";
status.classList.add('success');
status.style.display = 'block';
} else if (data.status === "complete" || data.status === "success") {
status.textContent = "Update successful! Device is restarting... Page will reload in 30 seconds.";
status.classList.add('success');
status.style.display = 'block';
// Versuche die WebSocket-Verbindung sauber zu schließen
try {
ws.close();
} catch (e) {
console.log('WebSocket already closed');
}
setTimeout(() => {
window.location.href = '/';
}, 30000);
}
}
} catch (e) { } catch (e) {
console.error('WebSocket message error:', e); console.error('WebSocket message error:', e);
} }
}; };
ws.onclose = function() { ws.onclose = function() {
// Wenn der WebSocket geschlossen wird während eines Updates, zeige eine Nachricht // Wenn das Update läuft und der Fortschritt hoch ist, zeige Success
if (progressContainer.style.display !== 'none' && progress.style.width !== '100%') { if (updateInProgress) {
status.textContent = "Connection lost. Update may still be in progress..."; const currentProgress = parseInt(progress.textContent);
if (!isNaN(currentProgress) && currentProgress >= 90) {
status.textContent = "Update appears successful! Device is restarting... Page will reload in 30 seconds.";
status.classList.add('success');
status.style.display = 'block';
setTimeout(() => {
window.location.href = '/';
}, 30000);
} else {
status.textContent = "Connection lost. Please wait 30 seconds and check if the update was successful...";
status.classList.add('warning'); status.classList.add('warning');
status.style.display = 'block'; status.style.display = 'block';
setTimeout(() => {
window.location.href = '/';
}, 30000);
}
} }
}; };
@ -188,6 +233,7 @@
alert('Please select a file.'); alert('Please select a file.');
return; return;
} }
// Validate file name pattern // Validate file name pattern
if (updateType === 'firmware' && !file.name.startsWith('upgrade_filaman_firmware_')) { if (updateType === 'firmware' && !file.name.startsWith('upgrade_filaman_firmware_')) {
alert('Please select a valid firmware file (upgrade_filaman_firmware_*.bin)'); alert('Please select a valid firmware file (upgrade_filaman_firmware_*.bin)');
@ -198,13 +244,13 @@
return; return;
} }
updateInProgress = true;
progressContainer.style.display = 'block'; progressContainer.style.display = 'block';
status.style.display = 'none'; status.style.display = 'none';
status.className = 'status'; status.className = 'status';
// Reset progress bar
progress.style.width = '0%'; progress.style.width = '0%';
progress.textContent = '0%'; progress.textContent = '0%';
// Disable both forms during update
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = true); document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = true);
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
@ -215,44 +261,72 @@
try { try {
const response = JSON.parse(xhr.responseText); const response = JSON.parse(xhr.responseText);
if (response.success) { if (response.success) {
status.textContent = "Update successful! Restarting device... The page will reload in 30 seconds."; if (progress.textContent !== '100%') {
status.classList.add('success');
status.style.display = 'block';
// Setze Progress auf 100%
progress.style.width = '100%'; progress.style.width = '100%';
progress.textContent = '100%'; progress.textContent = '100%';
}
// Automatischer Redirect nach 30 Sekunden status.textContent = "Update successful! Device is restarting... Page will reload in 30 seconds.";
status.classList.add('success');
status.style.display = 'block';
setTimeout(() => { setTimeout(() => {
window.location.href = '/'; window.location.href = '/';
}, 30000); }, 30000);
} else { } else {
updateInProgress = false;
status.textContent = response.message || "Update failed"; status.textContent = response.message || "Update failed";
status.classList.add('error'); status.classList.add('error');
status.style.display = 'block'; status.style.display = 'block';
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false); document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
} }
} catch (e) { } catch (e) {
status.textContent = "Error: Invalid server response"; if (progress.textContent === '100%') {
status.classList.add('error'); // Wenn 100% erreicht wurden, nehmen wir an, dass das Update erfolgreich war
status.textContent = "Update appears successful! Device is restarting... Page will reload in 30 seconds.";
status.classList.add('success');
status.style.display = 'block'; status.style.display = 'block';
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false); setTimeout(() => {
window.location.href = '/';
}, 30000);
} else {
handleUpdateError("Invalid server response");
}
} }
} else { } else {
status.textContent = "Update failed with status: " + xhr.status; if (progress.textContent === '100%') {
status.classList.add('error'); // Bei 100% Fortschritt gehen wir von einem erfolgreichen Update aus
status.textContent = "Update appears successful! Device is restarting... Page will reload in 30 seconds.";
status.classList.add('success');
status.style.display = 'block'; status.style.display = 'block';
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false); setTimeout(() => {
window.location.href = '/';
}, 30000);
} else {
handleUpdateError("Server error: " + xhr.status);
}
} }
}; };
xhr.onerror = function() { xhr.onerror = function() {
status.textContent = "Network error during update"; if (progress.textContent === '100%') {
// Bei 100% Fortschritt gehen wir von einem erfolgreichen Update aus
status.textContent = "Update appears successful! Device is restarting... Page will reload in 30 seconds.";
status.classList.add('success');
status.style.display = 'block';
setTimeout(() => {
window.location.href = '/';
}, 30000);
} else {
handleUpdateError("Network error during update");
}
};
function handleUpdateError(message) {
updateInProgress = false;
status.textContent = message;
status.classList.add('error'); status.classList.add('error');
status.style.display = 'block'; status.style.display = 'block';
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false); document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
}; }
const formData = new FormData(); const formData = new FormData();
formData.append('update', file); formData.append('update', file);

View File

@ -376,15 +376,19 @@ void setupWebserver(AsyncWebServer &server) {
// Update-Handler mit verbesserter Fehlerbehandlung // Update-Handler mit verbesserter Fehlerbehandlung
server.on("/update", HTTP_POST, server.on("/update", HTTP_POST,
[](AsyncWebServerRequest *request) { [](AsyncWebServerRequest *request) {
// Nach Update-Abschluss
bool success = !Update.hasError(); bool success = !Update.hasError();
// Bei SPIFFS Update und Erfolg: Restore Configs vor dem Neustart
if (success && currentUpdateCommand == U_SPIFFS) { if (success && currentUpdateCommand == U_SPIFFS) {
restoreJsonConfigs(); restoreJsonConfigs();
delay(200); // Warte auf Restore-Abschluss
} }
String message = success ? "Update successful" : String("Update failed: ") + Update.errorString(); String message = success ? "Update successful" : String("Update failed: ") + Update.errorString();
// Sende finale Bestätigung über WebSocket mit eindeutigem Status
ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"complete\",\"success\":true}");
delay(1000); // Längerer Delay für WebSocket
AsyncWebServerResponse *response = request->beginResponse( AsyncWebServerResponse *response = request->beginResponse(
success ? 200 : 400, success ? 200 : 400,
"application/json", "application/json",
@ -394,26 +398,26 @@ void setupWebserver(AsyncWebServer &server) {
request->send(response); request->send(response);
if (success) { if (success) {
oledShowMessage("Upgrade successful Rebooting"); oledShowMessage("Update successful");
delay(500); delay(2000); // Noch längerer Delay vor Neustart
ESP.restart(); ESP.restart();
} } else {
else { oledShowMessage("Update failed");
oledShowMessage("Upgrade failed");
} }
}, },
[](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
static size_t updateSize = 0; static size_t updateSize = 0;
static size_t totalWritten = 0;
if (!index) { if (!index) {
updateSize = request->contentLength(); updateSize = request->contentLength();
totalWritten = 0;
currentUpdateCommand = (filename.indexOf("website") > -1) ? U_SPIFFS : U_FLASH; currentUpdateCommand = (filename.indexOf("website") > -1) ? U_SPIFFS : U_FLASH;
if (currentUpdateCommand == U_SPIFFS) { if (currentUpdateCommand == U_SPIFFS) {
oledShowMessage("SPIFFS Update..."); oledShowMessage("SPIFFS Update...");
backupJsonConfigs(); backupJsonConfigs();
// Get the actual SPIFFS partition size from ESP32
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
if (!partition) { if (!partition) {
String errorMsg = "SPIFFS partition not found"; String errorMsg = "SPIFFS partition not found";
@ -443,15 +447,25 @@ void setupWebserver(AsyncWebServer &server) {
return; return;
} }
// Update OLED Display alle 5% und Webseite bei jeder Änderung totalWritten += len;
int currentProgress;
// Unterschiedliche Fortschrittsberechnung für SPIFFS und Firmware
if (currentUpdateCommand == U_SPIFFS) {
// SPIFFS Update: Fortschritt basierend auf Upload-Größe
currentProgress = (totalWritten * 100) / updateSize;
// Skaliere den Fortschritt auf 0-90%, da das Schreiben ins SPIFFS länger dauert
currentProgress = (currentProgress * 90) / 100;
} else {
// Firmware Update: Normaler Fortschritt
currentProgress = (totalWritten * 100) / updateSize;
}
static int lastProgress = -1; static int lastProgress = -1;
int currentProgress = (index + len) * 100 / updateSize;
if (currentProgress != lastProgress) { if (currentProgress != lastProgress) {
// OLED nur alle 5% aktualisieren
if (currentProgress % 5 == 0) { if (currentProgress % 5 == 0) {
oledShowMessage(String(currentProgress) + "% complete"); oledShowMessage(String(currentProgress) + "% complete");
} }
// Webseite bei jeder Änderung aktualisieren
lastProgress = currentProgress; lastProgress = currentProgress;
ws.textAll("{\"type\":\"updateProgress\",\"progress\":" + String(currentProgress) + "}"); ws.textAll("{\"type\":\"updateProgress\",\"progress\":" + String(currentProgress) + "}");
} }
@ -463,8 +477,14 @@ void setupWebserver(AsyncWebServer &server) {
request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}"); request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}");
return; return;
} }
// Sende finale Progress-Nachricht
ws.textAll("{\"type\":\"updateProgress\",\"progress\":100}"); // Bei SPIFFS Update zeige 95% an, da noch das Restore kommt
if (currentUpdateCommand == U_SPIFFS) {
ws.textAll("{\"type\":\"updateProgress\",\"progress\":95,\"status\":\"finalizing\"}");
} else {
ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"finalizing\"}");
}
delay(200);
} }
} }
); );