diff --git a/PLAN.md b/PLAN.md index ebafed8..ecce4af 100644 --- a/PLAN.md +++ b/PLAN.md @@ -176,6 +176,18 @@ something long in one, switch away and back, refresh — it's all still there. the daemon's sampling clock; the chart window is always 300 samples, so 30 s at 100 ms and 10 min at 2 s. Still to do: persistent history (SQLite ring, e.g. 10 s × 7 days), SMART (`smartctl -j`), longer time ranges. +- **Sensors** — **step 1 shipped 2026-09-19** (`internal/sensors`, + `internal/metrics/sensors.go`, reference in `SENSORS.md`): everything measured is a + sensor with a stable dotted ID; catalog, single value (`?format=text`), per-sensor + history, and a **shaped query** where the client sends the object it wants and gets it + back filled in (GraphQL's idea without GraphQL: sensors differ per machine, so a static + schema would collapse to `sensor(id:)` anyway). `/api/metrics` and the Dashboard are + untouched so far. **Remaining, in order:** (2) read-only scoped tokens + Prometheus + `/metrics`; (3) push stream + widget library, Dashboard rebuilt on its own sensors; + (4) embeddable widget page + Glance config (Glance's `custom-api` only refetches on page + load, an `iframe` of our embed is what's live); (5) non-metric sensors — services, + SMART, ZFS, updates, containers. One sampler rate, set in one place, is part of (3): + today the open dashboard still drives it. - **Multi-host** — **Phase 1 shipped 2026-09-18** (`internal/nodes`, `internal/tlsauto`, `MachineSwitcher.tsx`, `Machines.tsx`). One binary, hub and spoke: a host whose `config.json` lists `nodes` is a hub; any other is a standalone node with its full UI. diff --git a/SENSORS.md b/SENSORS.md new file mode 100644 index 0000000..d7782c9 --- /dev/null +++ b/SENSORS.md @@ -0,0 +1,84 @@ +# Sensors — the standard read interface + +Everything Ham Lab measures is a **sensor**: a stable dotted ID, a little metadata, a +current value. Any consumer — Ham Lab's own dashboard, Glance, Home Assistant, a shell +script — discovers them from the catalog and asks for exactly the ones it wants. + +Code: `internal/sensors` (types, registry, selectors, shaped query) and +`internal/metrics/sensors.go` (the collector as a provider). The registry measures +nothing; providers do. A new source of sensors implements `sensors.Provider` and is +added to the registry in `api.New`. + +## IDs + +``` +cpu.usage cpu.core..usage cpu.temp load.1|5|15 uptime +mem.total|used|avail|free|cached|buffers|used_pct swap.total|used|avail|used_pct +temp..