feat: enhance update process with separate forms for firmware and webpage uploads, including validation and improved UI
This commit is contained in:
parent
8199b283c0
commit
60553255b8
@ -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,18 +115,30 @@
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('update', file);
|
formData.append('update', file);
|
||||||
|
|
||||||
const progress = document.querySelector('.progress-bar');
|
const progress = document.querySelector('.progress-bar');
|
||||||
const progressContainer = document.querySelector('.progress-container');
|
const progressContainer = document.querySelector('.progress-container');
|
||||||
const status = document.querySelector('.status');
|
const status = document.querySelector('.status');
|
||||||
@ -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>
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user