// Device status bar: always-visible strip (below the page title, above // the tabs -- see _device_status_bar.html) showing last-seen/firmware/ // battery, so it's not tucked away on just the Stats tab. Shared by // every frame page; each sets window.FRAME_API before this loads. let lastDeviceStatus = null; function renderDeviceStatusBar(device) { const el = document.getElementById('device-status'); if (!el) return; el.innerHTML = ''; if (!device || !device.last_seen) { el.innerHTML = '
The frame hasn\'t checked in yet.
'; return; } const now = Date.now() / 1000; const rows = []; const ago = formatDuration(Math.max(0, now - device.last_seen)); rows.push(['Last seen', `${ago} ago`, device.overdue]); if (device.firmware_version) { let fw = `v${device.firmware_version}`; if (device.firmware_available && device.firmware_available !== device.firmware_version) { fw += ` (v${device.firmware_available} waiting)`; } rows.push(['Firmware', fw, false]); } if (device.battery) { rows.push(['Battery', `${device.battery.percent}%`, false]); // Shown as soon as there's any battery reading at all, even before // battery_estimate_s can compute a rate (needs 2h+ span and a 2%+ // drop within the current discharge cycle -- see common.py) -- so // it's clear the number is coming, not that the feature is broken. const hasEstimate = device.battery_estimate_s !== null && device.battery_estimate_s !== undefined; rows.push([ 'Est. battery life left', hasEstimate ? `~${formatDuration(device.battery_estimate_s)}` : 'Not enough data yet', false, ]); } for (const [label, value, alert] of rows) { const stat = document.createElement('span'); stat.className = 'device-stat' + (alert ? ' alert' : ''); const labelPart = document.createTextNode(label + ': '); const valuePart = document.createElement('strong'); valuePart.textContent = value; stat.appendChild(labelPart); stat.appendChild(valuePart); el.appendChild(stat); } } async function loadDeviceStatusBar() { try { const resp = await fetch(`${window.FRAME_API}/queue`); if (!resp.ok) { return; } const data = await resp.json(); lastDeviceStatus = data.device; renderDeviceStatusBar(data.device); } catch (e) { /* retried on the next poll */ } } loadDeviceStatusBar(); document.addEventListener('themechange', () => { if (lastDeviceStatus) { renderDeviceStatusBar(lastDeviceStatus); } }); // Fast tick: re-renders "Last seen" from already-fetched data every // second so it counts up smoothly without hitting the server that often. setInterval(() => { if (lastDeviceStatus) { renderDeviceStatusBar(lastDeviceStatus); } }, 1000); setInterval(loadDeviceStatusBar, 10000);