Skip to content

feat(settings): System tab — host CPU, memory, disk, network IPs - #179

Merged
erikarenhill merged 3 commits into
masterfrom
feat/settings-system-tab
Apr 23, 2026
Merged

erikarenhill merged 3 commits into
masterfrom
feat/settings-system-tab

Conversation

@erikarenhill

@erikarenhill erikarenhill commented Apr 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • New read-only System tab in the Settings modal that surfaces host OS resources: hostname, uptime, CPU %, memory %, root-disk %, and non-loopback network interfaces with their IPs.
  • New backend endpoint GET /api/system/info (in its own api_sysinfo.go per the package's one-file-per-feature rule) backed by github.com/shirou/gopsutil/v4 for cross-platform CPU/memory/disk reads. Network IPs use stdlib net.Interfaces().
  • CPU + memory + disk + network all refresh on a single 5 s poll loop (same setInterval pattern as ha.js / ev.js).
  • Network section defaults to IPv4 only with a Show IPv6 checkbox; the toggle re-renders from the cached payload — no extra fetch round-trip.

Notes

  • Cross-platform metrics so make dev keeps working on macOS as well as the Linux Pi targets — gopsutil is pure Go, no CGo.
  • Tab is positioned last (after Notifications) — it's a host-diagnostics view, not a configuration-edit surface.
  • New tab follows the existing per-tab IIFE registration pattern (window.FTWSettings.tabs.system = { render, after }).

Test plan

  • Open Settings → System on the desktop dashboard. Verify hostname, uptime, CPU %, memory bar, disk bar, and at least one non-loopback interface render.
  • Watch CPU + memory bars update every ~5 s.
  • Toggle the Show IPv6 checkbox; IPv6 addresses appear without a network round-trip and the next 5 s poll preserves the toggle state.
  • Verify on a real Pi (linux/arm64) that disk shows the SD-card root and network shows eth0 / wlan0 with the expected LAN IP.

🤖 Generated with Claude Code

New read-only Settings tab that surfaces host OS resources.

Backend
- New api_sysinfo.go (one file per feature, per the api package's
  CLAUDE.md rule) exposing GET /api/system/info. Returns hostname,
  uptime, CPU percent + cores, memory (total/used/percent), disk
  (root mount, total/used/percent), and one entry per UP non-loopback
  network interface with its non-loopback IPv4 + IPv6 addresses.
- Added github.com/shirou/gopsutil/v4 for cross-platform CPU /
  memory / disk reads (works on the Linux Pi targets and the macOS
  dev machine alike). Network IPs use stdlib net.Interfaces() so
  loopback / down filtering stays in the standard library.
- Route registered in api.go:routes() next to /api/health.

Frontend
- New web/settings/tabs/system.js. Render shows Host (hostname,
  uptime), CPU & memory (percentage + bar + cores / "X used of Y"),
  Disk (path + percent + bar + bytes detail), and Network.
- Network defaults to IPv4-only with a "Show IPv6" checkbox; toggle
  re-renders from the cached payload (no extra fetch). Filter is
  string-based (presence of ":" → IPv6) — covers fe80::, ::1,
  IPv4-mapped ::ffff: forms.
- After-render hook polls /api/system/info every 5 s, reusing the
  setInterval + window._timer pattern from ha.js / ev.js. Disk +
  network update on the same tick (free, same payload).
- Tab button + script tag wired into web/index.html alongside the
  other tabs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new read-only System tab in the Settings modal and a backing API endpoint to display host diagnostics (CPU/memory/disk/network).

Changes:

  • Add GET /api/system/info endpoint using gopsutil + stdlib net to expose hostname/uptime/CPU/memory/disk and interface IPs.
  • Add new Settings → System tab that polls the endpoint every 5s and renders resource bars plus interface addresses (IPv4 default, optional IPv6).
  • Wire the new tab into the Settings modal (button + script include).

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
web/settings/tabs/system.js Implements the new System settings tab UI + 5s polling/rendering logic.
web/index.html Adds “System” tab button and loads the new tab script.
go/internal/api/api_sysinfo.go Implements /api/system/info and network interface IP collection.
go/internal/api/api.go Registers the new route.
go/go.mod Adds github.com/shirou/gopsutil/v4 dependency.
go/go.sum Updates module checksums for new/updated dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread go/internal/api/api_sysinfo.go
Comment thread web/settings/tabs/system.js
Comment thread go/internal/api/api_sysinfo.go
Comment thread go/internal/api/api_sysinfo.go
Comment thread go/internal/api/api_sysinfo.go
erikarenhill and others added 2 commits April 23, 2026 07:19
… escape, smoke test

Three of five Copilot review comments were valid:

- api_sysinfo.go: hardcoded "/" broke the windows/amd64 release target
  (make build-windows-amd64). Added diskRoot() which returns %SystemDrive%\
  on Windows (fallback C:\) and "/" elsewhere, threaded through both the
  Disk.Path default and the disk.UsageWithContext call.
- web/settings/tabs/system.js: renderNetwork() built HTML via innerHTML
  with raw interface names and IPs from the host. Low realistic threat
  (IFNAMSIZ + kernel charset limits) but inconsistent with the
  escHtml-everywhere pattern used by devices.js. Captured ctx.escHtml
  in after() and escape both fields.
- api_sysinfo_test.go: new smoke test covering 200 + decodable shape +
  the two fields that don't depend on host probes (Cores, Disk.Path),
  plus a GOOS guard on diskRoot(). Follows the api_selfupdate_test.go
  pattern.

The other two comments (ParseCIDR vs IPAddr, nil→null JSON slice) were
resolved as wontfix: net.Interface.Addrs() only returns *IPNet on linux
and darwin per stdlib source, and the frontend already guards
Array.isArray(d.network) ? d.network : [].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@erikarenhill
erikarenhill merged commit e2b3f46 into master Apr 23, 2026
1 check passed
@erikarenhill
erikarenhill deleted the feat/settings-system-tab branch April 23, 2026 07:32
frahlg added a commit that referenced this pull request Oct 14, 2026
Move the pin to srcfl/device-drivers afc9739 (#179). Two Sungrow tests
polled back to back and expected the running state on every poll;
1.6.0 reads it on a full read every five seconds, so they move the
driver's clock five seconds before each poll.

Signed-off-by: Fredrik Ahlgren <fredrik@sourceful-labs.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants