From b5cdce18f28bf96d7002359aabd92e7cc70265f6 Mon Sep 17 00:00:00 2001 From: MarekWo Date: Sun, 18 Jan 2026 17:02:42 +0100 Subject: [PATCH] feat: Replace Settings with Device Info modal - Rename Settings to Device Info with new cpu icon - Display device parameters in readable table format - Add copy-to-clipboard buttons for Name and Public Key - Add map button for device location coordinates - Show human-readable parameter names and values - Hide telemetry parameters (not commonly needed) Co-Authored-By: Claude Opus 4.5 --- app/static/js/app.js | 106 ++++++++++++++++++++++++++++++++++++---- app/templates/base.html | 19 +++---- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/app/static/js/app.js b/app/static/js/app.js index f239784..d47d61d 100644 --- a/app/static/js/app.js +++ b/app/static/js/app.js @@ -425,9 +425,9 @@ function setupEventListeners() { isUserScrolling = !isAtBottom; }); - // Load device info when settings modal opens - const settingsModal = document.getElementById('settingsModal'); - settingsModal.addEventListener('show.bs.modal', function() { + // Load device info when modal opens + const deviceInfoModal = document.getElementById('deviceInfoModal'); + deviceInfoModal.addEventListener('show.bs.modal', function() { loadDeviceInfo(); }); @@ -767,24 +767,110 @@ async function loadStatus() { } } +/** + * Copy text to clipboard with visual feedback + */ +async function copyToClipboard(text, btnElement) { + try { + await navigator.clipboard.writeText(text); + const icon = btnElement.querySelector('i'); + const originalClass = icon.className; + icon.className = 'bi bi-check'; + setTimeout(() => { icon.className = originalClass; }, 1500); + } catch (err) { + console.error('Failed to copy:', err); + } +} + /** * Load device information */ async function loadDeviceInfo() { - const infoEl = document.getElementById('deviceInfo'); - infoEl.innerHTML = '
Loading...'; + const container = document.getElementById('deviceInfoContent'); + container.innerHTML = '
Loading...
'; try { const response = await fetch('/api/device/info'); const data = await response.json(); - if (data.success) { - infoEl.innerHTML = `
${escapeHtml(data.info)}
`; - } else { - infoEl.innerHTML = `Error: ${escapeHtml(data.error)}`; + if (!data.success) { + container.innerHTML = `
${escapeHtml(data.error)}
`; + return; } + + // Parse JSON from the info string + let info; + try { + // Extract JSON part (skip the header lines like "MarWoj|*...") + const jsonMatch = data.info.match(/\{[\s\S]*\}/); + info = jsonMatch ? JSON.parse(jsonMatch[0]) : null; + } catch (e) { + container.innerHTML = `
${escapeHtml(data.info)}
`; + return; + } + + if (!info) { + container.innerHTML = `
${escapeHtml(data.info)}
`; + return; + } + + // Type mapping + const typeNames = { 1: 'Companion', 2: 'Repeater', 3: 'Room Server', 4: 'Sensor' }; + const typeName = typeNames[info.adv_type] || `Unknown (${info.adv_type})`; + + // Shorten public key for display + const pubKey = info.public_key || ''; + const shortKey = pubKey.length > 12 ? `${pubKey.slice(0, 6)}...${pubKey.slice(-6)}` : pubKey; + + // Location + const hasLocation = info.adv_lat && info.adv_lon && (info.adv_lat !== 0 || info.adv_lon !== 0); + const coords = hasLocation ? `${info.adv_lat.toFixed(6)}, ${info.adv_lon.toFixed(6)}` : 'Not available'; + + // Build table rows + const rows = [ + { label: 'Name', value: escapeHtml(info.name || 'Unknown'), copyValue: info.name }, + { label: 'Type', value: typeName }, + { label: 'Public Key', value: `${escapeHtml(shortKey)}`, copyValue: pubKey }, + { label: 'Location', value: coords, showMap: hasLocation, lat: info.adv_lat, lon: info.adv_lon, name: info.name }, + { label: 'TX Power', value: `${info.tx_power || 0} / ${info.max_tx_power || 0} dBm` }, + { label: 'Frequency', value: `${info.radio_freq || 0} MHz` }, + { label: 'Bandwidth', value: `${info.radio_bw || 0} kHz` }, + { label: 'Spreading Factor', value: info.radio_sf || 0 }, + { label: 'Coding Rate', value: `4/${info.radio_cr || 0}` }, + { label: 'Multi Acks', value: info.multi_acks ? 'Enabled' : 'Disabled' }, + { label: 'Location Sharing', value: info.adv_loc_policy ? 'Enabled' : 'Disabled' }, + { label: 'Manual Add Contacts', value: info.manual_add_contacts ? 'Yes' : 'No' } + ]; + + let html = ''; + html += ''; + + for (const row of rows) { + html += ''; + html += ``; + html += ''; + html += ''; + } + + html += '
${row.label}'; + html += row.value; + + // Copy button + if (row.copyValue) { + html += ` `; + } + + // Map button + if (row.showMap) { + html += ` `; + } + + html += '
'; + container.innerHTML = html; + } catch (error) { - infoEl.innerHTML = 'Failed to load device info'; + console.error('Error loading device info:', error); + container.innerHTML = '
Failed to load device info
'; } } diff --git a/app/templates/base.html b/app/templates/base.html index 4e6f6d3..52f37e3 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -132,9 +132,9 @@ All contacts with GPS - @@ -260,18 +260,19 @@ - -