const serialResultsDiv = document.getElementById("serialResults"); const readBar = document.getElementById("readBar"); // Buffer for accumulating received data before logging let receivedDataBuffer = ''; // Software tool specific variables let deviceSerialNumber = ""; let deviceVersion = ""; let deviceHwVersion = ""; let selectedSoftwareUrl = ""; // Serial port variables (port, writer, textEncoder, writableStreamClosed declared in PHP) let reader; let readableStreamClosed; let keepReading = true; // Function to log communication to API (reused from scripts.js) async function logCommunication(data, direction) { // Only log if debug mode is enabled if (typeof DEBUG === 'undefined' || !DEBUG) { return; } try { const serviceToken = document.getElementById("servicetoken")?.innerHTML || ''; let serialNumber = ''; if (deviceSerialNumber) { serialNumber = deviceSerialNumber; } const logData = { data: data, direction: direction, timestamp: new Date().toISOString(), serial_number: serialNumber, maintenance_run: 0 }; const url = link + '/v2/com_log/log'; const bearer = 'Bearer ' + serviceToken; const response = await fetch(url, { method: 'POST', withCredentials: true, credentials: 'include', headers: { 'Authorization': bearer, 'Content-Type': 'application/json' }, body: JSON.stringify(logData) }); if (!response.ok) { console.warn('Failed to log communication:', response.status); } } catch (error) { console.warn('Error logging communication:', error); } } // Progress bar function (reused from scripts.js) function progressBar(percentage, message, color){ readBar.style.background = color; readBar.style.width = percentage +"%"; readBar.innerHTML = message; } // Connect device for software tool async function connectDeviceForSoftware() { //clear input readBar.innerHTML = ''; serialResultsDiv.innerHTML = ''; document.getElementById("softwareCheckStatus").style.display = "none"; document.getElementById("softwareOptions").style.display = "none"; document.getElementById("noUpdatesMessage").style.display = "none"; document.getElementById("uploadSection").style.display = "none"; // Reset data receivedDataBuffer = ''; deviceSerialNumber = ""; deviceVersion = ""; deviceHwVersion = ""; //set progress bar progressBar("1", "", ""); // Check if DEBUG mode is enabled - use mock device data if (typeof DEBUG !== 'undefined' && DEBUG) { // TEST MODE: Use mock device data deviceSerialNumber = "22110095"; deviceVersion = "03e615af"; deviceHwVersion = "0000080"; document.getElementById("Device_output").style.display = "block"; serialResultsDiv.innerHTML = `DEBUG MODE - Simulated Device Data:
SN=${deviceSerialNumber}
FW=${deviceVersion}
HW=${deviceHwVersion}`; progressBar("60", "DEBUG MODE - Device data read - SN: " + deviceSerialNumber, "#ff9800"); await logCommunication(`DEBUG MODE: Simulated device - SN=${deviceSerialNumber}, Version=${deviceVersion}, HW=${deviceHwVersion}`, 'handshake'); // Proceed to check software availability checkSoftwareAvailability(); return; } // Check if port is already open - if so, refresh the page to start clean if (port) { await logCommunication('Port already in use - refreshing page for clean reconnect', 'info'); location.reload(); return; } // Reset flags for new connection keepReading = true; try { // Prompt user to select any serial port. const filters = [{ usbVendorId: 1027, usbProductId: 24597 }]; port = await navigator.serial.requestPort({ filters }); // Log selected port details const portInfo = port.getInfo(); const portDetails = { processStep: 'Software', usbVendorId: portInfo.usbVendorId, usbProductId: portInfo.usbProductId, readable: !!port.readable, writable: !!port.writable, opened: port.readable !== null && port.writable !== null }; await logCommunication(`Selected USB device - ${JSON.stringify(portDetails)}`, 'connected'); await port.open({ baudRate: 56700, dataBits: 8, stopBits: 1, parity: 'none', flowControl: 'none' }); progressBar("10", "Connecting", "#04AA6D"); // Log successful connection await logCommunication('Port opened successfully', 'connected'); textEncoder = new TextEncoderStream(); writableStreamClosed = textEncoder.readable.pipeTo(port.writable); writer = textEncoder.writable.getWriter(); progressBar("20", "Connected, reading device...", "#04AA6D"); // Read device output await readDeviceOutput(); // Close the port immediately after reading await closePortAfterRead(); // Check for software updates (port is now closed) checkSoftwareAvailability(); } catch (error) { await logCommunication(`Connection error: ${error.message}`, 'error'); progressBar("0", "Error: " + error.message, "#ff6666"); } } async function readDeviceOutput() { const textDecoder = new TextDecoderStream(); readableStreamClosed = port.readable.pipeTo(textDecoder.writable); reader = textDecoder.readable.getReader(); document.getElementById("Device_output").style.display = "block"; let dataCompleteMarkerFound = false; const startTime = Date.now(); const timeout = 10000; // 10 second timeout try { progressBar("40", "Reading device...", "#04AA6D"); while (keepReading) { const { value, done } = await reader.read(); if (done || !keepReading) { break; } receivedDataBuffer += value; serialResultsDiv.innerHTML = receivedDataBuffer; // Check if we have received PLUGTY which comes after all device data // Or wait for at least FWDATE which comes near the end if (receivedDataBuffer.indexOf("PLUGTY") > 0 || receivedDataBuffer.indexOf("FWDATE=") > 0) { dataCompleteMarkerFound = true; } // Only parse and exit if we found the completion marker if (dataCompleteMarkerFound) { // Parse device info from complete output parseDeviceInfo(receivedDataBuffer); // Check if we have all required data if (deviceSerialNumber && deviceVersion && deviceHwVersion) { progressBar("60", "Device data read - SN: " + deviceSerialNumber, "#04AA6D"); await logCommunication(`Device identification data received: SN=${deviceSerialNumber}, Version=${deviceVersion}, HW=${deviceHwVersion}`, 'handshake'); // Exit cleanly break; } } // Timeout check if (Date.now() - startTime > timeout) { await logCommunication('Device read timeout - proceeding with partial data', 'error'); parseDeviceInfo(receivedDataBuffer); break; } } progressBar("65", "Device reading completed", "#04AA6D"); } catch (error) { if (keepReading) { await logCommunication(`Read error: ${error.message}`, 'error'); progressBar("0", "Error reading device: " + error.message, "#ff6666"); } } finally { // Clean up reader and cancel the stream try { if (reader) { await reader.cancel(); reader.releaseLock(); } } catch (e) { console.log('Reader cleanup error:', e); } } } function parseDeviceInfo(data) { // Extract SN, FW (firmware/software version), and HW from device output // Device format: SN=12345678;FW=12345678;HW=12345678;STATE=... // Match exact logic from scripts.js and readdevice.js // Use exact same approach as scripts.js line 153 and readdevice.js line 649 const x = Array.from(new Set(data.split(";"))).toString(); // Parse SN= (8 characters, exact logic from scripts.js line 185-189) if (x.indexOf("SN=") > 0 && !deviceSerialNumber) { const a = x.indexOf("SN="); const b = a + 3; const c = b + 8; deviceSerialNumber = x.substring(b, c); console.log("Found SN:", deviceSerialNumber); } // Parse FW= (8 characters, exact logic from readdevice.js line 672-676) if (x.indexOf("FW=") > 0 && !deviceVersion) { const a = x.indexOf("FW="); const b = a + 3; const c = b + 8; deviceVersion = x.substring(b, c); console.log("Found FW/Version:", deviceVersion); } // Parse HW= (8 characters, exact logic from readdevice.js line 665-670) if (x.indexOf("HW=") > 0 && !deviceHwVersion) { const a = x.indexOf("HW="); const b = a + 3; const c = b + 8; deviceHwVersion = x.substring(b, c); console.log("Found HW Version:", deviceHwVersion); } } async function closePortAfterRead() { if (port) { try { await logCommunication('Closing port after reading device data', 'info'); // Reader is already cancelled and released by readDeviceOutput finally block // Now abort the writer if (writer) { try { await writer.abort(); } catch (e) { console.log('Writer abort error:', e); } } // Give time for streams to cleanup await new Promise(resolve => setTimeout(resolve, 300)); // Close the port await port.close(); await logCommunication('Port closed successfully', 'info'); // Reset for next connection reader = null; writer = null; readableStreamClosed = null; writableStreamClosed = null; port = null; } catch (error) { console.error('Error closing port after read:', error); await logCommunication(`Error closing port: ${error.message}`, 'error'); // Force reset even on error reader = null; writer = null; readableStreamClosed = null; writableStreamClosed = null; port = null; } } } async function closePort() { if (port) { try { keepReading = false; // Wait for read operations to complete await new Promise(resolve => setTimeout(resolve, 300)); // Release reader if still locked if (reader) { try { reader.releaseLock(); } catch (e) { console.log('Reader release error:', e); } } // Wait a bit more await new Promise(resolve => setTimeout(resolve, 200)); // Now close the port try { await port.close(); await logCommunication('Port closed', 'info'); } catch (e) { console.log('Port close error (may already be closed):', e); } // Reset for next connection reader = null; port = null; } catch (error) { console.error('Error closing port:', error); } } } async function checkSoftwareAvailability() { if (!deviceSerialNumber) { progressBar("0", "Error: Serial number not found", "#ff6666"); await logCommunication('Serial number not found in device output', 'error'); return; } document.getElementById("softwareCheckStatus").style.display = "block"; progressBar("70", "Checking for software updates...", "#04AA6D"); try { // Call software_available API const availableUrl = link + "/v2/software_available/sn=" + deviceSerialNumber + (deviceVersion ? "&version=" + deviceVersion : "") + (deviceHwVersion ? "&hw_version=" + deviceHwVersion : ""); console.log("Calling API:", availableUrl); await logCommunication(`Checking software availability for SN: ${deviceSerialNumber}`, 'sent'); const availableResponse = await fetch(availableUrl, { method: "GET", headers: { "Authorization": "Bearer " + document.getElementById("servicetoken").textContent, "Content-Type": "application/json" } }); const availableData = await availableResponse.json(); console.log("Available response:", availableData); await logCommunication(`Software availability response: ${JSON.stringify(availableData)}`, 'received'); if (availableData.software_available === "error" || availableData.error) { // Error checking for updates (e.g., invalid hardware version) document.getElementById("softwareCheckStatus").style.display = "none"; progressBar("0", "Error: " + (availableData.error || "Unable to check for updates"), "#ff6666"); alert("Error checking for updates: " + (availableData.error || "Unknown error")); } else if (availableData.software_available === "yes") { // Software updates available, fetch options progressBar("80", "Software updates found, loading options...", "#04AA6D"); await fetchSoftwareOptions(); } else { // No updates available document.getElementById("softwareCheckStatus").style.display = "none"; document.getElementById("noUpdatesMessage").style.display = "block"; progressBar("100", "No software updates available", "#04AA6D"); } } catch (error) { await logCommunication(`Software check error: ${error.message}`, 'error'); progressBar("0", "Error checking software: " + error.message, "#ff6666"); await closePort(); } } async function fetchSoftwareOptions() { try { // Call software_update API to get options const updateUrl = link + "/v2/software_update/sn=" + deviceSerialNumber + (deviceVersion ? "&version=" + deviceVersion : "") + (deviceHwVersion ? "&hw_version=" + deviceHwVersion : ""); console.log("Fetching options from:", updateUrl); await logCommunication(`Fetching software options for SN: ${deviceSerialNumber}`, 'sent'); const updateResponse = await fetch(updateUrl, { method: "GET", headers: { "Authorization": "Bearer " + document.getElementById("servicetoken").textContent, "Content-Type": "application/json" } }); const options = await updateResponse.json(); console.log("Software options:", options); await logCommunication(`Software options response: ${JSON.stringify(options)}`, 'received'); if (options.error) { document.getElementById("softwareCheckStatus").style.display = "none"; document.getElementById("noUpdatesMessage").style.display = "block"; progressBar("100", "No software updates available", "#04AA6D"); await closePort(); return; } // Display options in table displaySoftwareOptions(options); document.getElementById("softwareCheckStatus").style.display = "none"; document.getElementById("softwareOptions").style.display = "block"; progressBar("100", "Software options loaded", "#04AA6D"); } catch (error) { await logCommunication(`Software options error: ${error.message}`, 'error'); progressBar("0", "Error loading options: " + error.message, "#ff6666"); await closePort(); } } function displaySoftwareOptions(options) { const grid = document.getElementById("softwareOptionsGrid"); grid.innerHTML = ""; options.forEach((option, index) => { const price = parseFloat(option.price); const isFree = price === 0; const isCurrent = option.is_current === true || option.is_current === 1; // Create card const card = document.createElement("div"); card.style.cssText = ` background: ${isCurrent ? '#f5f5f5' : 'white'}; border: 2px solid ${isCurrent ? '#bbb' : (isFree ? '#e0e0e0' : '#e0e0e0')}; border-radius: 4px; padding: 15px; transition: 0.3s; display: flex; flex-direction: column; position: relative; overflow: hidden; transform: translateY(0px); box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px; opacity: ${isCurrent ? '0.6' : '1'}; pointer-events: ${isCurrent ? 'none' : 'auto'}; `; if (!isCurrent) { card.onmouseenter = () => { card.style.transform = 'translateY(-5px)'; card.style.boxShadow = '0 8px 16px rgba(0,0,0,0.15)'; }; card.onmouseleave = () => { card.style.transform = 'translateY(0)'; card.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)'; }; } // Badge for current/free/paid const badge = document.createElement("div"); badge.style.cssText = ` position: absolute; top: 15px; right: 15px; background: ${isCurrent ? '#6c757d' : '#04AA6D'}; color: white; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; display:none; `; if (isCurrent) { badge.textContent = "CURRENT VERSION"; } else if (isFree) { badge.textContent = "Included"; } if (isCurrent || isFree) { card.appendChild(badge); } // Name const name = document.createElement("h4"); name.style.cssText = ` margin: 0 0 10px 0; color: #333; font-size: 20px; font-weight: 600; `; name.textContent = option.name || "Software Update"; card.appendChild(name); // Version const version = document.createElement("div"); version.style.cssText = ` color: #666; font-size: 14px; margin-bottom: 15px; `; version.innerHTML = ` Version: ${option.version || "N/A"}`; card.appendChild(version); // Description const desc = document.createElement("p"); desc.style.cssText = ` color: #555; font-size: 14px; line-height: 1.6; margin: 0 0 20px 0; flex-grow: 1; `; desc.textContent = option.description || "No description available"; card.appendChild(desc); // Price section const priceSection = document.createElement("div"); priceSection.style.cssText = ` border-top: 1px solid #e0e0e0; padding-top: 15px; margin-top: auto; `; const priceText = document.createElement("div"); priceText.style.cssText = ` font-size: 24px; font-weight: bold; color: ${isCurrent ? '#6c757d' : (isFree ? '#04AA6D' : '#333')}; margin-bottom: 15px; `; if (isCurrent) { priceText.textContent = "INSTALLED"; } else { priceText.textContent = isFree ? "Included" : `${option.currency || "€"} ${price.toFixed(2)}`; } priceSection.appendChild(priceText); // Action button const actionBtn = document.createElement("button"); actionBtn.className = "btn"; actionBtn.style.cssText = ` width: 100%; background: ${isCurrent ? '#6c757d' : '#04AA6D'}; color: white; border: none; padding: 12px; border-radius: 6px; font-size: 16px; font-weight: 600; cursor: ${isCurrent ? 'not-allowed' : 'pointer'}; transition: background 0.3s ease; opacity: ${isCurrent ? '0.5' : '1'}; `; if (isCurrent) { actionBtn.innerHTML = ' Currently Installed'; actionBtn.disabled = true; } else if (isFree) { actionBtn.innerHTML = ''; actionBtn.onclick = () => selectUpgrade(option); actionBtn.onmouseenter = () => actionBtn.style.background = '#038f5a'; actionBtn.onmouseleave = () => actionBtn.style.background = '#04AA6D'; } else { actionBtn.innerHTML = ''; actionBtn.onclick = () => selectUpgrade(option); actionBtn.onmouseenter = () => actionBtn.style.background = '#038f5a'; actionBtn.onmouseleave = () => actionBtn.style.background = '#04AA6D'; } priceSection.appendChild(actionBtn); card.appendChild(priceSection); grid.appendChild(card); }); } async function selectUpgrade(option) { const price = parseFloat(option.price || 0); const isFree = price === 0; // If paid upgrade, show payment modal first if (!isFree) { showPaymentModal(option); return; } // Free upgrade - show confirmation modal first showFreeInstallModal(option); } function showFreeInstallModal(option) { // Create modal overlay const modal = document.createElement("div"); modal.id = "freeInstallModal"; modal.style.cssText = ` display: flex; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; `; // Create modal content const modalContent = document.createElement("div"); modalContent.style.cssText = ` background: white; border-radius: 8px; max-width: 500px; width: 90%; max-height: 90vh; overflow-y: auto; margin: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); `; modalContent.innerHTML = `

Confirm Software Installation

${option.name || "Software Update"}

Version: ${option.version || "N/A"}

${option.description || ""}

`; modal.appendChild(modalContent); document.body.appendChild(modal); // Prefill form with customer data from sessionStorage if available const savedCustomerData = sessionStorage.getItem('customerData'); if (savedCustomerData) { try { const customerData = JSON.parse(savedCustomerData); if (customerData.name) document.getElementById("freeInstallName").value = customerData.name; if (customerData.email) document.getElementById("freeInstallEmail").value = customerData.email; if (customerData.address) document.getElementById("freeInstallAddress").value = customerData.address; if (customerData.city) document.getElementById("freeInstallCity").value = customerData.city; if (customerData.postal) document.getElementById("freeInstallPostal").value = customerData.postal; if (customerData.country) document.getElementById("freeInstallCountry").value = customerData.country; } catch (e) { console.warn('Error parsing saved customer data:', e); } } // Close modal on cancel document.getElementById("cancelFreeInstall").onclick = () => { document.body.removeChild(modal); }; // Handle form submission document.getElementById("freeInstallForm").onsubmit = async (e) => { e.preventDefault(); const formData = new FormData(e.target); const customerData = { name: formData.get("name"), email: formData.get("email"), address: formData.get("address"), city: formData.get("city"), postal: formData.get("postal"), country: formData.get("country"), version_id: option.version_id, price: 0, currency: option.currency || "€" }; // Save customer data to sessionStorage for future use sessionStorage.setItem('customerData', JSON.stringify({ name: customerData.name, email: customerData.email, address: customerData.address, city: customerData.city, postal: customerData.postal, country: customerData.country })); // Close modal document.body.removeChild(modal); // Proceed to download and install with customer data await downloadAndInstallSoftware(option, customerData); }; } function showPaymentModal(option) { const price = parseFloat(option.price || 0); const currency = option.currency || "€"; // Create modal overlay const modal = document.createElement("div"); modal.id = "paymentModal"; modal.style.cssText = ` display: flex; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; `; // Create modal content const modalContent = document.createElement("div"); modalContent.style.cssText = ` background: white; border-radius: 8px; max-width: 500px; width: 90%; max-height: 90vh; overflow-y: auto; margin: 20px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); `; modalContent.innerHTML = `

Purchase Software Upgrade

${option.name || "Software Update"}

Version: ${option.version || "N/A"}

${option.description || ""}

${currency} ${price.toFixed(2)}
`; modal.appendChild(modalContent); document.body.appendChild(modal); // Prefill form with customer data from sessionStorage if available const savedCustomerData = sessionStorage.getItem('customerData'); if (savedCustomerData) { try { const customerData = JSON.parse(savedCustomerData); if (customerData.name) document.getElementById("paymentName").value = customerData.name; if (customerData.email) document.getElementById("paymentEmail").value = customerData.email; if (customerData.address) document.getElementById("paymentAddress").value = customerData.address; if (customerData.city) document.getElementById("paymentCity").value = customerData.city; if (customerData.postal) document.getElementById("paymentPostal").value = customerData.postal; if (customerData.country) document.getElementById("paymentCountry").value = customerData.country; } catch (e) { console.warn('Error parsing saved customer data:', e); } } // Close modal on cancel document.getElementById("cancelPayment").onclick = () => { document.body.removeChild(modal); }; // Handle form submission document.getElementById("paymentForm").onsubmit = async (e) => { e.preventDefault(); const formData = new FormData(e.target); const paymentData = { name: formData.get("name"), email: formData.get("email"), address: formData.get("address"), city: formData.get("city"), postal: formData.get("postal"), country: formData.get("country"), payment_method: formData.get("payment_method"), version_id: option.version_id, price: price, currency: currency }; // Save customer data to sessionStorage for future use sessionStorage.setItem('customerData', JSON.stringify({ name: paymentData.name, email: paymentData.email, address: paymentData.address, city: paymentData.city, postal: paymentData.postal, country: paymentData.country })); await processPayment(paymentData, option, modal); }; } async function logSoftwareInstallationToHistory(option, customerData = null) { try { const serviceToken = document.getElementById("servicetoken")?.innerHTML || ''; // Create payload matching the structure expected by v2/history const payload = { version: option.version, version_id: option.version_id, name: option.name, description: option.description, HW: deviceHwVersion, HEX_FW: option.version, price: option.price || 0, currency: option.currency || "€", installation_source: 'softwaretool' }; // Add customer data if provided if (customerData) { payload.customer = { name: customerData.name, email: customerData.email, address: customerData.address, city: customerData.city, postal: customerData.postal, country: customerData.country }; } const historyData = { sn: deviceSerialNumber, type: 'firmware', sn_service: 'Portal', payload: payload }; const url = link + '/v2/history'; const bearer = 'Bearer ' + serviceToken; console.log("Logging installation to history:", historyData); await logCommunication(`Logging software installation to history: ${JSON.stringify(historyData)}`, 'sent'); const response = await fetch(url, { method: 'POST', withCredentials: true, credentials: 'include', headers: { 'Authorization': bearer, 'Content-Type': 'application/json' }, body: JSON.stringify(historyData) }); if (!response.ok) { console.warn('Failed to log to history:', response.status); await logCommunication(`Failed to log to history: ${response.status}`, 'error'); } else { const result = await response.json(); console.log("History logged successfully:", result); await logCommunication(`History logged successfully: ${JSON.stringify(result)}`, 'received'); } } catch (error) { console.warn('Error logging to history:', error); await logCommunication(`Error logging to history: ${error.message}`, 'error'); // Don't block the installation if history logging fails } } async function processPayment(paymentData, option, modal) { try { progressBar("10", "Processing payment...", "#04AA6D"); // TODO: Call payment provider API // For now, simulate payment processing await logCommunication(`Payment initiated: ${JSON.stringify(paymentData)}`, 'sent'); // Simulate API call to payment provider // const paymentResponse = await fetch(link + '/v2/payment/process', { // method: 'POST', // headers: { // 'Authorization': 'Bearer ' + document.getElementById("servicetoken").textContent, // 'Content-Type': 'application/json' // }, // body: JSON.stringify(paymentData) // }); // For now, simulate success after 2 seconds await new Promise(resolve => setTimeout(resolve, 2000)); await logCommunication(`Payment successful`, 'received'); // Close payment modal document.body.removeChild(modal); // Proceed to download and install await downloadAndInstallSoftware(option); } catch (error) { await logCommunication(`Payment error: ${error.message}`, 'error'); progressBar("0", "Payment failed: " + error.message, "#ff6666"); alert("Payment failed: " + error.message); } } async function downloadAndInstallSoftware(option, customerData = null) { selectedSoftwareUrl = option.source; if (!selectedSoftwareUrl) { alert("Error: No download URL available"); await logCommunication('No download URL available for selected option', 'error'); return; } console.log("Download URL:", selectedSoftwareUrl); await logCommunication(`Downloading software from: ${selectedSoftwareUrl}`, 'sent'); // Log to v2/history before starting download (with customer data if provided) await logSoftwareInstallationToHistory(option, customerData); try { // Download the software file progressBar("10", "Downloading software...", "#04AA6D"); const response = await fetch(selectedSoftwareUrl, { method: "GET", headers: { "Authorization": "Bearer " + document.getElementById("servicetoken").textContent } }); if (!response.ok) { // Try to parse error message const errorText = await response.text(); let errorMessage = `Download failed: ${response.status}`; try { const errorJson = JSON.parse(errorText); errorMessage = errorJson.message || errorJson.error || errorMessage; } catch (e) { // Not JSON, use status text } console.error("Download failed:", response.status, errorText); await logCommunication(`Download failed: ${response.status} - ${errorText}`, 'error'); progressBar("0", "Error: " + errorMessage, "#ff6666"); alert("Download failed: " + errorMessage); return; } // Get the blob const blob = await response.blob(); progressBar("50", "Download complete, preparing installation...", "#04AA6D"); await logCommunication(`Software downloaded successfully, size: ${blob.size} bytes`, 'received'); // Create a blob URL for the downloaded file const blobUrl = URL.createObjectURL(blob); // Set global variables expected by upload.js window.firmwarelocation = blobUrl; window.upgraded_version = option.version || ""; // DEBUG MODE: Don't auto-trigger upload, let user manually test if (typeof DEBUG !== 'undefined' && DEBUG) { // Show upload section and button for manual testing document.getElementById("uploadSection").style.display = "block"; const uploadBtn = document.getElementById("uploadSoftware"); uploadBtn.style.display = "block"; uploadBtn.disabled = false; progressBar("100", "DEBUG MODE: Download complete. Click 'Install Software' button to test upload.js manually", "#ff9800"); console.log("=== DEBUG MODE: Manual Upload Test ==="); console.log("Firmware downloaded successfully"); console.log("Blob size:", blob.size, "bytes"); console.log("Blob URL:", blobUrl); console.log("window.firmwarelocation =", window.firmwarelocation); console.log("window.upgraded_version =", window.upgraded_version); console.log("Upload button is now visible and enabled"); console.log("Click the 'Install Software' button to test if upload.js can handle the file"); alert("DEBUG MODE: Download complete!\n\nBlob size: " + blob.size + " bytes\n\nClick the 'Install Software' button to test upload.js"); } else { // PRODUCTION MODE: Show upload button and automatically trigger document.getElementById("uploadSection").style.display = "block"; const uploadBtn = document.getElementById("uploadSoftware"); uploadBtn.style.display = "block"; uploadBtn.disabled = false; progressBar("60", "Ready to install, starting upload...", "#04AA6D"); uploadBtn.click(); } } catch (error) { await logCommunication(`Download error: ${error.message}`, 'error'); progressBar("0", "Error downloading software: " + error.message, "#ff6666"); alert("Error: " + error.message); } }