diff --git a/html/upgrade.html b/html/upgrade.html
index cef0b12..f57d301 100644
--- a/html/upgrade.html
+++ b/html/upgrade.html
@@ -154,81 +154,103 @@
const progress = document.querySelector('.progress-bar');
const progressContainer = document.querySelector('.progress-container');
const status = document.querySelector('.status');
-
- // WebSocket für Update-Progress
- const ws = new WebSocket('ws://' + window.location.host + '/ws');
let updateInProgress = false;
+ let lastReceivedProgress = 0;
- ws.onmessage = function(event) {
- try {
- const data = JSON.parse(event.data);
- if (data.type === "updateProgress" && updateInProgress) {
- 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.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';
+ // WebSocket Handling
+ let ws = null;
+ let wsReconnectTimer = null;
+
+ function connectWebSocket() {
+ ws = new WebSocket('ws://' + window.location.host + '/ws');
+
+ ws.onmessage = function(event) {
+ try {
+ const data = JSON.parse(event.data);
+ if (data.type === "updateProgress" && updateInProgress) {
+ // Zeige Fortschrittsbalken
+ progressContainer.style.display = 'block';
- // Versuche die WebSocket-Verbindung sauber zu schließen
- try {
- ws.close();
- } catch (e) {
- console.log('WebSocket already closed');
+ // Aktualisiere den Fortschritt nur wenn er größer ist
+ const newProgress = parseInt(data.progress);
+ if (!isNaN(newProgress) && newProgress >= lastReceivedProgress) {
+ progress.style.width = newProgress + '%';
+ progress.textContent = newProgress + '%';
+ lastReceivedProgress = newProgress;
}
+ // Zeige Status-Nachricht
+ if (data.message || data.status) {
+ status.textContent = data.message || getStatusMessage(data.status);
+ status.className = 'status success';
+ status.style.display = 'block';
+
+ // Starte Reload wenn Update erfolgreich
+ if (data.status === 'success' || lastReceivedProgress >= 98) {
+ clearTimeout(wsReconnectTimer);
+ setTimeout(() => {
+ window.location.href = '/';
+ }, 30000);
+ }
+ }
+ }
+ } catch (e) {
+ console.error('WebSocket message error:', e);
+ }
+ };
+
+ ws.onclose = function() {
+ if (updateInProgress) {
+ // Wenn der Fortschritt hoch genug ist, gehen wir von einem erfolgreichen Update aus
+ if (lastReceivedProgress >= 85) {
+ status.textContent = "Update appears successful! Device is restarting... Page will reload in 30 seconds.";
+ status.className = 'status success';
+ status.style.display = 'block';
+ clearTimeout(wsReconnectTimer);
setTimeout(() => {
window.location.href = '/';
}, 30000);
+ } else {
+ // Versuche Reconnect bei niedrigem Fortschritt
+ wsReconnectTimer = setTimeout(connectWebSocket, 1000);
}
}
- } catch (e) {
- console.error('WebSocket message error:', e);
- }
- };
+ };
- ws.onclose = function() {
- // Wenn das Update läuft und der Fortschritt hoch ist, zeige Success
- if (updateInProgress) {
- const currentProgress = parseInt(progress.textContent);
- if (!isNaN(currentProgress) && currentProgress >= 90) {
+ ws.onerror = function(err) {
+ console.error('WebSocket error:', err);
+ if (updateInProgress && lastReceivedProgress >= 85) {
status.textContent = "Update appears successful! Device is restarting... Page will reload in 30 seconds.";
- status.classList.add('success');
+ status.className = 'status 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.style.display = 'block';
-
setTimeout(() => {
window.location.href = '/';
}, 30000);
}
+ };
+ }
+
+ // Initial WebSocket connection
+ connectWebSocket();
+
+ function getStatusMessage(status) {
+ switch(status) {
+ case 'starting': return 'Starting update...';
+ case 'uploading': return 'Uploading...';
+ case 'finalizing': return 'Finalizing update...';
+ case 'restoring': return 'Restoring configurations...';
+ case 'preparing': return 'Preparing for restart...';
+ case 'success': return 'Update successful! Device is restarting... Page will reload in 30 seconds.';
+ default: return 'Updating...';
}
- };
+ }
function handleUpdate(e) {
e.preventDefault();
const form = e.target;
const file = form.update.files[0];
const updateType = form.dataset.type;
+
if (!file) {
alert('Please select a file.');
return;
@@ -244,6 +266,7 @@
return;
}
+ // Reset UI
updateInProgress = true;
progressContainer.style.display = 'block';
status.style.display = 'none';
@@ -251,83 +274,33 @@
progress.style.width = '0%';
progress.textContent = '0%';
+ // Disable submit buttons
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = true);
+ // Send update
const xhr = new XMLHttpRequest();
xhr.open('POST', '/update', true);
xhr.onload = function() {
- if (xhr.status === 200) {
- try {
- const response = JSON.parse(xhr.responseText);
- if (response.success) {
- if (progress.textContent !== '100%') {
- progress.style.width = '100%';
- progress.textContent = '100%';
- }
- status.textContent = "Update successful! Device is restarting... Page will reload in 30 seconds.";
- status.classList.add('success');
- status.style.display = 'block';
- setTimeout(() => {
- window.location.href = '/';
- }, 30000);
- } else {
- updateInProgress = false;
- status.textContent = response.message || "Update failed";
- status.classList.add('error');
- status.style.display = 'block';
- document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
- }
- } catch (e) {
- if (progress.textContent === '100%') {
- // 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';
- setTimeout(() => {
- window.location.href = '/';
- }, 30000);
- } else {
- handleUpdateError("Invalid server response");
- }
- }
- } else {
- 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("Server error: " + xhr.status);
- }
+ if (xhr.status !== 200 && !progress.textContent.startsWith('100')) {
+ status.textContent = "Update failed: " + (xhr.responseText || "Unknown error");
+ status.className = 'status error';
+ status.style.display = 'block';
+ updateInProgress = false;
+ document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
}
};
xhr.onerror = function() {
- 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');
+ if (!progress.textContent.startsWith('100')) {
+ status.textContent = "Network error during update";
+ status.className = 'status error';
status.style.display = 'block';
- setTimeout(() => {
- window.location.href = '/';
- }, 30000);
- } else {
- handleUpdateError("Network error during update");
+ updateInProgress = false;
+ document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
}
};
- function handleUpdateError(message) {
- updateInProgress = false;
- status.textContent = message;
- status.classList.add('error');
- status.style.display = 'block';
- document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
- }
-
const formData = new FormData();
formData.append('update', file);
xhr.send(formData);
diff --git a/src/website.cpp b/src/website.cpp
index 9d66a80..42f891f 100644
--- a/src/website.cpp
+++ b/src/website.cpp
@@ -30,6 +30,42 @@ String spoolmanUrlBackup;
// Globale Variable für den Update-Typ
static int currentUpdateCommand = 0;
+// Globale Update-Variablen
+static size_t updateTotalSize = 0;
+static size_t updateWritten = 0;
+static bool isSpiffsUpdate = false;
+
+void sendUpdateProgress(int progress, const char* status = nullptr, const char* message = nullptr) {
+ static int lastSentProgress = -1;
+
+ // Verhindere zu häufige Updates
+ if (progress == lastSentProgress && !status && !message) {
+ return;
+ }
+
+ String progressMsg = "{\"type\":\"updateProgress\",\"progress\":" + String(progress);
+ if (status) {
+ progressMsg += ",\"status\":\"" + String(status) + "\"";
+ }
+ if (message) {
+ progressMsg += ",\"message\":\"" + String(message) + "\"";
+ }
+ progressMsg += "}";
+
+ // Sende die Nachricht mehrmals mit Verzögerung für wichtige Updates
+ if (status || abs(progress - lastSentProgress) >= 10 || progress == 100) {
+ for (int i = 0; i < 2; i++) {
+ ws.textAll(progressMsg);
+ delay(100); // Längerer Delay zwischen Nachrichten
+ }
+ } else {
+ ws.textAll(progressMsg);
+ delay(50);
+ }
+
+ lastSentProgress = progress;
+}
+
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_CONNECT) {
Serial.println("Neuer Client verbunden!");
@@ -171,6 +207,105 @@ void sendAmsData(AsyncWebSocketClient *client) {
}
}
+void handleUpdate(AsyncWebServer &server) {
+ AsyncCallbackWebHandler* updateHandler = new AsyncCallbackWebHandler();
+ updateHandler->setUri("/update");
+ updateHandler->setMethod(HTTP_POST);
+
+ updateHandler->onUpload([](AsyncWebServerRequest *request, String filename,
+ size_t index, uint8_t *data, size_t len, bool final) {
+ if (!index) {
+ updateTotalSize = request->contentLength();
+ updateWritten = 0;
+ isSpiffsUpdate = (filename.indexOf("website") > -1);
+
+ if (isSpiffsUpdate) {
+ // Backup vor dem Update
+ sendUpdateProgress(0, "backup", "Backing up configurations...");
+ delay(200);
+ backupJsonConfigs();
+ delay(200);
+
+ const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
+ if (!partition || !Update.begin(partition->size, U_SPIFFS)) {
+ request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
+ return;
+ }
+ sendUpdateProgress(5, "starting", "Starting SPIFFS update...");
+ delay(200);
+ } else {
+ if (!Update.begin(updateTotalSize)) {
+ request->send(400, "application/json", "{\"success\":false,\"message\":\"Update initialization failed\"}");
+ return;
+ }
+ sendUpdateProgress(0, "starting", "Starting firmware update...");
+ delay(200);
+ }
+ }
+
+ if (len) {
+ if (Update.write(data, len) != len) {
+ request->send(400, "application/json", "{\"success\":false,\"message\":\"Write failed\"}");
+ return;
+ }
+
+ updateWritten += len;
+ int currentProgress;
+
+ // Berechne den Fortschritt basierend auf dem Update-Typ
+ if (isSpiffsUpdate) {
+ // SPIFFS: 5-75% für Upload
+ currentProgress = 5 + (updateWritten * 100) / updateTotalSize;
+ } else {
+ // Firmware: 0-100% für Upload
+ currentProgress = 1 + (updateWritten * 100) / updateTotalSize;
+ }
+
+ static int lastProgress = -1;
+ if (currentProgress != lastProgress && (currentProgress % 10 == 0 || final)) {
+ sendUpdateProgress(currentProgress, "uploading");
+ oledShowMessage("Update: " + String(currentProgress) + "%");
+ delay(50);
+ lastProgress = currentProgress;
+ }
+ }
+
+ if (final) {
+ if (Update.end(true)) {
+ if (isSpiffsUpdate) {
+ restoreJsonConfigs();
+ }
+ } else {
+ request->send(400, "application/json", "{\"success\":false,\"message\":\"Update finalization failed\"}");
+ }
+ }
+ });
+
+ updateHandler->onRequest([](AsyncWebServerRequest *request) {
+ if (Update.hasError()) {
+ request->send(400, "application/json", "{\"success\":false,\"message\":\"Update failed\"}");
+ return;
+ }
+
+ // Erste 100% Nachricht
+ ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
+ delay(2000); // Längerer Delay für die erste Nachricht
+
+ AsyncWebServerResponse *response = request->beginResponse(200, "application/json",
+ "{\"success\":true,\"message\":\"Update successful! Restarting device...\"}");
+ response->addHeader("Connection", "close");
+ request->send(response);
+
+ // Zweite 100% Nachricht zur Sicherheit
+ ws.textAll("{\"type\":\"updateProgress\",\"progress\":100,\"status\":\"success\",\"message\":\"Update successful! Restarting device...\"}");
+ delay(3000); // Noch längerer Delay vor dem Neustart
+
+ ESP.restart();
+ });
+
+ server.addHandler(updateHandler);
+}
+
void setupWebserver(AsyncWebServer &server) {
// Deaktiviere alle Debug-Ausgaben
Serial.setDebugOutput(false);
@@ -373,121 +508,8 @@ void setupWebserver(AsyncWebServer &server) {
request->send(response);
});
- // Update-Handler mit verbesserter Fehlerbehandlung
- server.on("/update", HTTP_POST,
- [](AsyncWebServerRequest *request) {
- bool success = !Update.hasError();
-
- if (success && currentUpdateCommand == U_SPIFFS) {
- restoreJsonConfigs();
- delay(200); // Warte auf Restore-Abschluss
- }
-
- 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(
- success ? 200 : 400,
- "application/json",
- "{\"success\":" + String(success ? "true" : "false") + ",\"message\":\"" + message + "\"}"
- );
- response->addHeader("Connection", "close");
- request->send(response);
-
- if (success) {
- oledShowMessage("Update successful");
- delay(2000); // Noch längerer Delay vor Neustart
- ESP.restart();
- } else {
- oledShowMessage("Update failed");
- }
- },
- [](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
- static size_t updateSize = 0;
- static size_t totalWritten = 0;
-
- if (!index) {
- updateSize = request->contentLength();
- totalWritten = 0;
- currentUpdateCommand = (filename.indexOf("website") > -1) ? U_SPIFFS : U_FLASH;
-
- if (currentUpdateCommand == U_SPIFFS) {
- oledShowMessage("SPIFFS Update...");
- backupJsonConfigs();
-
- const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
- if (!partition) {
- String errorMsg = "SPIFFS partition not found";
- request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}");
- return;
- }
-
- if (!Update.begin(partition->size, currentUpdateCommand)) {
- String errorMsg = String("Update begin failed: ") + Update.errorString();
- request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}");
- return;
- }
- } else {
- oledShowMessage("Firmware Update...");
- if (!Update.begin(updateSize, currentUpdateCommand)) {
- String errorMsg = String("Update begin failed: ") + Update.errorString();
- request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}");
- return;
- }
- }
- }
-
- if (len) {
- if (Update.write(data, len) != len) {
- String errorMsg = String("Write failed: ") + Update.errorString();
- request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}");
- return;
- }
-
- 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;
- if (currentProgress != lastProgress) {
- if (currentProgress % 5 == 0) {
- oledShowMessage(String(currentProgress) + "% complete");
- }
- lastProgress = currentProgress;
- ws.textAll("{\"type\":\"updateProgress\",\"progress\":" + String(currentProgress) + "}");
- }
- }
-
- if (final) {
- if (!Update.end(true)) {
- String errorMsg = String("Update end failed: ") + Update.errorString();
- request->send(400, "application/json", "{\"success\":false,\"message\":\"" + errorMsg + "\"}");
- return;
- }
-
- // 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);
- }
- }
- );
+ // Update-Handler registrieren
+ handleUpdate(server);
server.on("/api/version", HTTP_GET, [](AsyncWebServerRequest *request){
String fm_version = VERSION;