feat: enhance update process with separate forms for firmware and webpage uploads, including validation and improved UI

This commit is contained in:
Manuel Weiser 2025-02-21 11:57:19 +01:00
parent 8199b283c0
commit 60553255b8
2 changed files with 120 additions and 18 deletions

View File

@ -50,26 +50,64 @@
<!-- head --> <!-- head -->
<div class="content"> <div class="content">
<h1>Firmware Upgrade</h1> <h1>System Update</h1>
<div class="warning"> <div class="warning">
<strong>Warning:</strong> Please do not turn off or restart the device during the update. <strong>Warning:</strong> Please do not turn off or restart the device during the update.
The device will restart automatically after the update. The device will restart automatically after the update.
</div> </div>
<div class="update-form"> <div class="update-options">
<form id="updateForm" enctype='multipart/form-data'> <div class="update-section">
<input type='file' name='update' accept='.bin' required> <h2>Firmware Update</h2>
<input type='submit' value='Start Firmware Update'> <p>Upload a new firmware file (filaman_*.bin)</p>
</form> <div class="update-form">
<form id="firmwareForm" enctype='multipart/form-data' data-type="firmware">
<input type='file' name='update' accept='.bin' required>
<input type='submit' value='Start Firmware Update'>
</form>
</div>
</div>
<div class="update-section">
<h2>Webpage Update</h2>
<p>Upload a new webpage file (webpage_*.bin)</p>
<div class="update-form">
<form id="webpageForm" enctype='multipart/form-data' data-type="webpage">
<input type='file' name='update' accept='.bin' required>
<input type='submit' value='Start Webpage Update'>
</form>
</div>
</div>
</div> </div>
<div class="progress-container"> <div class="progress-container" style="display: none;">
<div class="progress-bar">0%</div> <div class="progress-bar">0%</</div>
</div> </div>
<div class="status"></div> <div class="status"></div>
</div> </div>
<style>
.update-options {
display: flex;
gap: 2rem;
margin: 2rem 0;
}
.update-section {
flex: 1;
background: #f5f5f5;
padding: 1.5rem;
border-radius: 8px;
}
.update-section h2 {
margin-top: 0;
color: #333;
}
.update-section p {
color: #666;
margin-bottom: 1rem;
}
</style>
<script> <script>
// Hide status indicators during update // Hide status indicators during update
const statusContainer = document.querySelector('.status-container'); const statusContainer = document.querySelector('.status-container');
@ -77,12 +115,24 @@
statusContainer.style.display = 'none'; statusContainer.style.display = 'none';
} }
document.getElementById('updateForm').addEventListener('submit', async (e) => { function handleUpdate(e) {
e.preventDefault(); e.preventDefault();
const form = e.target; const form = e.target;
const file = form.update.files[0]; const file = form.update.files[0];
const updateType = form.dataset.type;
if (!file) { if (!file) {
alert('Please select a firmware file.'); alert('Please select a file.');
return;
}
// Validate file name pattern
if (updateType === 'firmware' && !file.name.startsWith('filaman_')) {
alert('Please select a valid firmware file (filaman_*.bin)');
return;
}
if (updateType === 'webpage' && !file.name.startsWith('webpage_')) {
alert('Please select a valid webpage file (webpage_*.bin)');
return; return;
} }
@ -96,11 +146,12 @@
progressContainer.style.display = 'block'; progressContainer.style.display = 'block';
status.style.display = 'none'; status.style.display = 'none';
status.className = 'status'; status.className = 'status';
form.querySelector('input[type=submit]').disabled = true;
// Disable both forms during update
document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = true);
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', '/update', true); xhr.open('POST', '/update', true);
xhr.upload.onprogress = (e) => { xhr.upload.onprogress = (e) => {
if (e.lengthComputable) { if (e.lengthComputable) {
const percentComplete = (e.loaded / e.total) * 100; const percentComplete = (e.loaded / e.total) * 100;
@ -143,13 +194,13 @@
status.style.display = 'block'; status.style.display = 'block';
if (xhr.status !== 200) { if (xhr.status !== 200) {
form.querySelector('input[type=submit]').disabled = false; document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
} }
} catch (error) { } catch (error) {
status.textContent = 'Error: ' + error.message; status.textContent = 'Error: ' + error.message;
status.classList.add('error'); status.classList.add('error');
status.style.display = 'block'; status.style.display = 'block';
form.querySelector('input[type=submit]').disabled = false; document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
} }
}; };
@ -157,11 +208,14 @@
status.textContent = 'Update failed: Network error'; status.textContent = 'Update failed: Network error';
status.classList.add('error'); status.classList.add('error');
status.style.display = 'block'; status.style.display = 'block';
form.querySelector('input[type=submit]').disabled = false; document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false);
}; };
xhr.send(formData); xhr.send(formData);
}); }
document.getElementById('firmwareForm').addEventListener('submit', handleUpdate);
document.getElementById('webpageForm').addEventListener('submit', handleUpdate);
</script> </script>
</body> </body>
</html> </html>

View File

@ -385,3 +385,51 @@ void setupWebserver(AsyncWebServer &server) {
server.begin(); server.begin();
Serial.println("Webserver gestartet"); Serial.println("Webserver gestartet");
} }
void handleOTAUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
// Start eines neuen Uploads
Serial.println("Update Start: " + filename);
// Überprüfe den Dateityp basierend auf dem Dateinamen
bool isFirmware = filename.startsWith("filaman_");
bool isWebpage = filename.startsWith("webpage_");
if (!isFirmware && !isWebpage) {
request->send(400, "application/json", "{\"message\":\"Invalid file type. File must start with 'filaman_' or 'webpage_'\"}");
return;
}
// Wähle den Update-Typ basierend auf dem Dateinamen
if (isWebpage) {
if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) {
Update.printError(Serial);
request->send(400, "application/json", "{\"message\":\"SPIFFS Update failed: " + String(Update.errorString()) + "\"}");
return;
}
} else {
if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASH)) {
Update.printError(Serial);
request->send(400, "application/json", "{\"message\":\"Firmware Update failed: " + String(Update.errorString()) + "\"}");
return;
}
}
}
if (Update.write(data, len) != len) {
Update.printError(Serial);
request->send(400, "application/json", "{\"message\":\"Write failed: " + String(Update.errorString()) + "\"}");
return;
}
if (final) {
if (!Update.end(true)) {
Update.printError(Serial);
request->send(400, "application/json", "{\"message\":\"Update failed: " + String(Update.errorString()) + "\"}");
return;
}
request->send(200, "application/json", "{\"message\":\"Update successful!\", \"restart\": true}");
delay(500);
ESP.restart();
}
}