feat(webui,dashboard): add RAM Usage card between Disk Usage and Uptime, live-verified on Pager

This commit is contained in:
c4ch3c4d3
2026-08-24 18:12:32 -06:00
parent e683691a53
commit 18bd6e3f9a
3 changed files with 37 additions and 2 deletions
@@ -3025,6 +3025,23 @@ def disk_data():
return {}
def mem_data(path='/proc/meminfo'):
try:
vals = {}
with open(path) as f:
for line in f:
k, v = line.split(':')
vals[k] = int(v.strip().split()[0])
total = vals.get('MemTotal', 0)
avail = vals.get('MemAvailable', vals.get('MemFree', 0))
if not total:
return {}
used = max(total - avail, 0)
return {'size': total * 1024, 'used': used * 1024, 'avail': avail * 1024}
except (OSError, ValueError):
return {}
def uptime_data():
rc, out, err = device_run(['cat', '/proc/uptime'])
try:
@@ -3066,6 +3083,7 @@ def status_data():
'wifi': [wifi_iface_info(n) for n in ifaces],
'clients': assoc_clients(ifaces),
'disk': disk_data(),
'mem': mem_data(),
'uptime': uptime_data(),
'hostname': hostname_data(),
}
@@ -192,7 +192,7 @@ views.dashboard = (root) => {
root.appendChild(grid);
const defs = [
['clients', 'Clients Connected'], ['handshakes', 'Handshakes Captured'],
['disk', 'Disk Usage'], ['uptime', 'Uptime']
['disk', 'Disk Usage'], ['mem', 'RAM Usage'], ['uptime', 'Uptime']
];
const cards = {};
defs.forEach(([k, label]) => {
@@ -318,6 +318,8 @@ views.dashboard = (root) => {
cards.uptime.textContent = s.uptime == null ? 'Unavailable' : fmtDur(s.uptime);
cards.disk.textContent = s.disk && s.disk.size != null
? fmtBytes(s.disk.used) + ' / ' + fmtBytes(s.disk.size) : 'Unavailable';
cards.mem.textContent = s.mem && s.mem.size != null
? fmtBytes(s.mem.used) + ' / ' + fmtBytes(s.mem.size) : 'Unavailable';
if (typeof MiniChart !== 'undefined') {
MiniChart.draw(canvas, [
{ label: 'Clients', color: '#1976d2', points: history.clients }
@@ -329,6 +331,7 @@ views.dashboard = (root) => {
.catch(() => {
cards.clients.textContent = '0';
cards.disk.textContent = '—';
cards.mem.textContent = '—';
cards.uptime.textContent = '—';
});
PagerAPI.get('/api/pineap/handshakes').then((r) => {
+15 -1
View File
@@ -43,6 +43,20 @@ class StatusTest(unittest.TestCase):
d = server.disk_data()
self.assertEqual(d['avail'], 7364608 * 1024)
def test_mem_parses_meminfo(self):
import tempfile
base = tempfile.mkdtemp()
with open(os.path.join(base, 'meminfo'), 'w') as f:
f.write('MemTotal: 262144 kB\nMemFree: 102400 kB\nMemAvailable: 122880 kB\nBuffers: 20480 kB\n')
d = server.mem_data(os.path.join(base, 'meminfo'))
self.assertEqual(d['size'], 262144 * 1024)
self.assertEqual(d['used'], (262144 - 122880) * 1024)
self.assertEqual(d['avail'], 122880 * 1024)
def test_mem_missing_returns_empty(self):
import tempfile
self.assertEqual(server.mem_data(os.path.join(tempfile.mkdtemp(), 'nope')), {})
def test_wifi_ifaces_extracts_names(self):
server.device_run = lambda args, timeout=20: (0, 'wlan0 ESSID: "Pineapple"\nwlan1 ESSID: "Pineapple"\n', '') if args == ['iwinfo'] else (0, '', '')
self.assertEqual(server.wifi_ifaces(), ['wlan0', 'wlan1'])
@@ -89,7 +103,7 @@ class StatusTest(unittest.TestCase):
args = ()
status, payload = server.h_status(Ctx())
self.assertEqual(status, 200)
for k in ('battery', 'firmware', 'daemon', 'wifi', 'clients', 'disk', 'uptime', 'hostname'):
for k in ('battery', 'firmware', 'daemon', 'wifi', 'clients', 'disk', 'mem', 'uptime', 'hostname'):
self.assertIn(k, payload)
def test_status_discovers_wifi_interfaces_once(self):