/**
* System Health – IIFE module
*
* Always-on monitoring that auto-connects when the mode is entered.
* Streams real-time system metrics via SSE and provides SDR device enumeration.
*/
const SystemHealth = (function () {
'use strict';
let eventSource = null;
let connected = false;
let lastMetrics = null;
// -----------------------------------------------------------------------
// Helpers
// -----------------------------------------------------------------------
function formatBytes(bytes) {
if (bytes == null) return '--';
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let i = 0;
let val = bytes;
while (val >= 1024 && i < units.length - 1) { val /= 1024; i++; }
return val.toFixed(1) + ' ' + units[i];
}
function barClass(pct) {
if (pct >= 85) return 'crit';
if (pct >= 60) return 'warn';
return 'ok';
}
function barHtml(pct, label) {
if (pct == null) return 'N/A';
const cls = barClass(pct);
const rounded = Math.round(pct);
return '
' +
barHtml(cpu.percent, '') +
'
Load: ' + cpu.load_1 + ' / ' + cpu.load_5 + ' / ' + cpu.load_15 + '
' +
'
Cores: ' + cpu.count + '
' +
'
';
}
function renderMemoryCard(m) {
const el = document.getElementById('sysCardMemory');
if (!el) return;
const mem = m.memory;
if (!mem) { el.innerHTML = '' +
barHtml(mem.percent, '') +
'
' + formatBytes(mem.used) + ' / ' + formatBytes(mem.total) + '
' +
'
Swap: ' + formatBytes(swap.used) + ' / ' + formatBytes(swap.total) + '
' +
'
';
}
function renderDiskCard(m) {
const el = document.getElementById('sysCardDisk');
if (!el) return;
const disk = m.disk;
if (!disk) { el.innerHTML = '' +
barHtml(disk.percent, '') +
'
' + formatBytes(disk.used) + ' / ' + formatBytes(disk.total) + '
' +
'
Path: ' + (disk.path || '/') + '
' +
'
';
}
function _extractPrimaryTemp(temps) {
if (!temps) return null;
// Prefer common chip names
const preferred = ['cpu_thermal', 'coretemp', 'k10temp', 'acpitz', 'soc_thermal'];
for (const name of preferred) {
if (temps[name] && temps[name].length) return temps[name][0];
}
// Fall back to first available
for (const key of Object.keys(temps)) {
if (temps[key] && temps[key].length) return temps[key][0];
}
return null;
}
function renderSdrCard(devices) {
const el = document.getElementById('sysCardSdr');
if (!el) return;
let html = '';
html += '';
if (!devices || !devices.length) {
html += '
No devices found';
} else {
devices.forEach(function (d) {
html += '
' +
'
' +
'
' + d.type + ' #' + d.index + '' +
'
' + (d.name || 'Unknown') + '
' +
(d.serial ? '
S/N: ' + d.serial + '
' : '') +
'
';
});
}
html += '
';
el.innerHTML = html;
}
function renderProcessCard(m) {
const el = document.getElementById('sysCardProcesses');
if (!el) return;
const procs = m.processes || {};
const keys = Object.keys(procs).sort();
let html = '';
if (!keys.length) {
html += '
No data';
} else {
keys.forEach(function (k) {
const running = procs[k];
const dotCls = running ? 'running' : 'stopped';
const label = k.charAt(0).toUpperCase() + k.slice(1);
html += '
' +
' ' +
'' + label + '' +
'
';
});
}
html += '
';
el.innerHTML = html;
}
function renderSystemInfoCard(m) {
const el = document.getElementById('sysCardInfo');
if (!el) return;
const sys = m.system || {};
const temp = _extractPrimaryTemp(m.temperatures);
let html = '';
html += '
Host: ' + (sys.hostname || '--') + '
';
html += '
OS: ' + (sys.platform || '--') + '
';
html += '
Python: ' + (sys.python || '--') + '
';
html += '
App: v' + (sys.version || '--') + '
';
html += '
Uptime: ' + (sys.uptime_human || '--') + '
';
if (temp) {
html += '
Temp: ' + Math.round(temp.current) + '°C';
if (temp.high) html += ' / ' + Math.round(temp.high) + '°C max';
html += '
';
}
html += '
';
el.innerHTML = html;
}
function updateSidebarQuickStats(m) {
const cpuEl = document.getElementById('sysQuickCpu');
const tempEl = document.getElementById('sysQuickTemp');
const ramEl = document.getElementById('sysQuickRam');
const diskEl = document.getElementById('sysQuickDisk');
if (cpuEl) cpuEl.textContent = m.cpu ? Math.round(m.cpu.percent) + '%' : '--';
if (ramEl) ramEl.textContent = m.memory ? Math.round(m.memory.percent) + '%' : '--';
if (diskEl) diskEl.textContent = m.disk ? Math.round(m.disk.percent) + '%' : '--';
const temp = _extractPrimaryTemp(m.temperatures);
if (tempEl) tempEl.innerHTML = temp ? Math.round(temp.current) + '°C' : '--';
// Color-code values
[cpuEl, ramEl, diskEl].forEach(function (el) {
if (!el) return;
const val = parseInt(el.textContent);
el.classList.remove('sys-val-ok', 'sys-val-warn', 'sys-val-crit');
if (!isNaN(val)) el.classList.add('sys-val-' + barClass(val));
});
}
function updateSidebarProcesses(m) {
const el = document.getElementById('sysProcessList');
if (!el) return;
const procs = m.processes || {};
const keys = Object.keys(procs).sort();
if (!keys.length) { el.textContent = 'No data'; return; }
const running = keys.filter(function (k) { return procs[k]; });
const stopped = keys.filter(function (k) { return !procs[k]; });
el.innerHTML =
(running.length ? '