<!-- head --><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FilaMan - Filament Management Tool</title> <link rel="icon" type="image/png" href="/favicon.ico"> <link rel="stylesheet" href="style.css"> <script> fetch('/api/version') .then(response => response.json()) .then(data => { const versionSpan = document.querySelector('.version'); if (versionSpan) { versionSpan.textContent = 'v' + data.version; } }) .catch(error => console.error('Error fetching version:', error)); </script> </head> <body> <div class="navbar"> <div style="display: flex; align-items: center; gap: 2rem;"> <img src="/logo.png" alt="FilaMan Logo" class="logo"> <div class="logo-text"> <h1>FilaMan<span class="version"></span></h1> <h4>Filament Management Tool</h4> </div> </div> <nav style="display: flex; gap: 1rem;"> <a href="/">Start</a> <a href="/waage">Scale</a> <a href="/spoolman">Spoolman/Bambu</a> <a href="/about">About</a> <a href="/upgrade">Upgrade</a> </nav> <div class="status-container"> <div class="status-item"> <span class="status-dot" id="bambuDot"></span>B </div> <div class="status-item"> <span class="status-dot" id="spoolmanDot"></span>S </div> <div class="ram-status" id="ramStatus"></div> </div> </div> <!-- head --> <div class="content"> <h1>Firmware Upgrade</h1> <div class="warning"> <strong>Warning:</strong> Do not power off the device during update. </div> <div class="update-options"> <div class="update-section"> <h2>Firmware Update</h2> <p>Upload a new firmware file (filaman_*.bin)</p> <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 class="progress-container" style="display: none;"> <div class="progress-bar">0%</div> </div> <div class="status"></div> </div> <script> // Hide status indicators during update const statusContainer = document.querySelector('.status-container'); if (statusContainer) { statusContainer.style.display = 'none'; } const progress = document.querySelector('.progress-bar'); const progressContainer = document.querySelector('.progress-container'); const status = document.querySelector('.status'); let updateInProgress = false; let lastReceivedProgress = 0; // 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'; // 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.reload(true); 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.reload(true); window.location.href = '/'; }, 30000); } else { // Versuche Reconnect bei niedrigem Fortschritt wsReconnectTimer = setTimeout(connectWebSocket, 1000); } } }; 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.className = 'status success'; 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; } // Validate file name pattern if (updateType === 'firmware' && !file.name.startsWith('upgrade_filaman_firmware_')) { alert('Please select a valid firmware file (upgrade_filaman_firmware_*.bin)'); return; } if (updateType === 'webpage' && !file.name.startsWith('upgrade_filaman_website_')) { alert('Please select a valid webpage file (upgrade_filaman_website_*.bin)'); return; } // Reset UI updateInProgress = true; progressContainer.style.display = 'block'; status.style.display = 'none'; status.className = 'status'; 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 && !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.startsWith('100')) { status.textContent = "Network error during update"; status.className = 'status error'; status.style.display = 'block'; updateInProgress = false; document.querySelectorAll('form input[type=submit]').forEach(btn => btn.disabled = false); } }; const formData = new FormData(); formData.append('update', file); xhr.send(formData); } document.getElementById('firmwareForm').addEventListener('submit', handleUpdate); document.getElementById('webpageForm').addEventListener('submit', handleUpdate); </script> </body> </html>