From c67b18c6573abc3d9af81105bb568a71c57393ff Mon Sep 17 00:00:00 2001 From: l5y <220195275+l5yth@users.noreply.github.com> Date: Sun, 14 Sep 2025 09:00:14 +0200 Subject: [PATCH] web: extend the table view to display more records --- web/app.rb | 6 ++-- web/public/index.html | 74 ++++++++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/web/app.rb b/web/app.rb index 210367a..450b594 100644 --- a/web/app.rb +++ b/web/app.rb @@ -13,8 +13,10 @@ def query_nodes(limit) db.results_as_hash = true min_last_heard = Time.now.to_i - 7 * 24 * 60 * 60 rows = db.execute <<~SQL, [min_last_heard, limit] - SELECT node_id, short_name, long_name, hw_model, role, snr, battery_level, - last_heard, position_time, latitude, longitude, altitude + SELECT node_id, short_name, long_name, hw_model, role, snr, + battery_level, voltage, last_heard, uptime_seconds, + channel_utilization, air_util_tx, position_time, + latitude, longitude, altitude FROM nodes WHERE last_heard >= ? ORDER BY last_heard DESC diff --git a/web/public/index.html b/web/public/index.html index 326cb0d..3608b6c 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -33,7 +33,7 @@ .row { display: flex; gap: var(--pad); align-items: center; justify-content: space-between; } .map-row { display: flex; gap: var(--pad); align-items: stretch; } #chat { flex: 0 0 33%; max-width: 33%; height: 60vh; border: 1px solid #ddd; border-radius: 8px; overflow-y: auto; padding: 6px; font-size: 12px; } - .chat-entry { margin-bottom: 4px; font-family: ui-monospace, Menlo, Consolas, monospace; } + .chat-entry-node { font-family: ui-monospace, Menlo, Consolas, monospace; color: #555 } .controls { display: flex; gap: 8px; align-items: center; } button { padding: 6px 10px; border: 1px solid #ccc; background: #fff; border-radius: 6px; cursor: pointer; } button:hover { background: #f6f6f6; } @@ -70,12 +70,17 @@ Node ID Short Long Name - HW Model - Role - Battery Last Seen - Lat - Lon + Role + HW Model + Battery + Voltage + Uptime + Channel Util + Air Util Tx + Latitude + Longitude + Altitude Last Position @@ -135,15 +140,18 @@ // --- Helpers --- function renderShortHtml(short){ return String(short).padStart(4, ' ').replace(/ /g, ' '); } + function addNewNodeChatEntry(n) { const div = document.createElement('div'); const ts = formatDate(new Date(n.last_heard * 1000)); - div.className = 'chat-entry'; + div.className = 'chat-entry-node'; div.innerHTML = `[${ts}] ${n.node_id || ''} ${renderShortHtml(n.short_name || '')} New node: ${n.long_name || ''}`; chatEl.appendChild(div); chatEl.scrollTop = chatEl.scrollHeight; } + function pad(n) { return String(n).padStart(2, "0"); } + function formatDate(d) { return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + @@ -152,20 +160,45 @@ pad(d.getMinutes()) + ":" + pad(d.getSeconds()); } - function fmt(v, d = 5) { + + function fmtCoords(v, d = 5) { if (v == null) return ""; const n = Number(v); return Number.isNaN(n) ? "" : n.toFixed(d); } + function fmtAlt(v, s) { + if (v == null) return ""; + if (v == 0) return ""; + const n = String(v) + String(s); + return n; + } + + function fmtTx(v, d = 3) { + if (v == null) return ""; + let n = Number(v); + n = Number.isNaN(n) ? "" : n.toFixed(d); + n = String(n) + "%"; + return n; + } + + function timeHum(unixSec) { + if (!unixSec) return ""; + if (unixSec < 0) return "0s"; + if (unixSec < 60) return `${unixSec}s`; + if (unixSec < 3600) return `${Math.floor(unixSec/60)}m ${Math.floor((unixSec%60))}s`; + if (unixSec < 86400) return `${Math.floor(unixSec/3600)}h ${Math.floor((unixSec%3600)/60)}m`; + return `${Math.floor(unixSec/86400)}d ${Math.floor((unixSec%86400)/3600)}h`; + } + function timeAgo(unixSec) { if (!unixSec) return ""; const diff = Math.floor(Date.now()/1000 - Number(unixSec)); - if (diff < 0) return "0s ago"; - if (diff < 60) return `${diff}s ago`; - if (diff < 3600) return `${Math.floor(diff/60)}m ago`; - if (diff < 86400) return `${Math.floor(diff/3600)}h ${Math.floor((diff%3600)/60)}m ago`; - return `${Math.floor(diff/86400)}d ago`; + if (diff < 0) return "0s"; + if (diff < 60) return `${diff}s`; + if (diff < 3600) return `${Math.floor(diff/60)}m ${Math.floor((diff%60))}s`; + if (diff < 86400) return `${Math.floor(diff/3600)}h ${Math.floor((diff%3600)/60)}m`; + return `${Math.floor(diff/86400)}d ${Math.floor((diff%86400)/3600)}h`; } async function fetchNodes() { @@ -183,12 +216,17 @@ ${n.node_id || ""} ${n.short_name || ""} ${n.long_name || ""} - ${n.hw_model || ""} - ${n.role || "CLIENT"} - ${n.battery_level ?? ""} ${timeAgo(n.last_heard)} - ${fmt(n.latitude)} - ${fmt(n.longitude)} + ${n.role || "CLIENT"} + ${n.hw_model || ""} + ${fmtAlt(n.battery_level, "%")} + ${fmtAlt(n.voltage, "V")} + ${timeHum(n.uptime_seconds)} + ${fmtTx(n.channel_utilization)} + ${fmtTx(n.air_util_tx)} + ${fmtCoords(n.latitude)} + ${fmtCoords(n.longitude)} + ${fmtAlt(n.altitude, "m")} ${n.pos_time_iso ? `${timeAgo(n.position_time)}` : ""}`; tb.appendChild(tr); }