Files
hamlab/SENSORS.md
T
Mitchell-JohnsonandClaude Fable 5.1 43bf787696 Sensors: one standard read interface — catalog, single value, history, shaped query
Every number the collector samples becomes a sensor with a stable dotted ID
(cpu.core.3.usage, fan.nct6775.fan2, fs.root.used_pct). Clients discover them
from the catalog and ask for exactly what they want, including as a shaped
query: send the object you want, get it back with the values filled in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-19 07:04:03 -07:00

85 lines
3.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.<n>.usage cpu.temp load.1|5|15 uptime
mem.total|used|avail|free|cached|buffers|used_pct swap.total|used|avail|used_pct
temp.<chip>.<label> fan.<chip>.<label>
net.<iface>.rx|tx|rx_total|tx_total|up
disk.<dev>.read|write|util|size|temp
fs.<mount>.size|used|used_pct
ups.battery|runtime|load|load_pct|in_v|out_v|on_battery|state|source
```
- Segments are lower case `[a-z0-9_-]`: `Package id 0` → `package_id_0`, `/mnt/tank` →
`mnt_tank`, `/` → `root`. The real names are in the sensor's `labels`.
- Built from names the hardware reports, never enumeration order (`hwmon3` moves between
boots). Two identical chips collide; the second gets `_2`.
- Which sensors exist differs per machine. That is the point of the catalog.
## A sensor
```json
{ "id": "temp.coretemp.package_id_0", "name": "coretemp Package id 0",
"kind": "temperature", "unit": "°C", "value": 51, "t": 1789826600878,
"warn": 105, "crit": 105, "labels": { "chip": "coretemp", "label": "Package id 0" } }
```
`value` is a number, bool or string. `t` is when it was really read, in unix ms — slow
sections (filesystems 30 s, UPS 10 s) are older than the tick. `min`/`max` are the scale
(0–100 for percentages, 0–total for used bytes); `warn`/`crit` are the hardware's own
thresholds where it states them.
## Endpoints
All behind the same auth as the rest of `/api` (session cookie or `Authorization: Bearer`).
On a hub, every one is also at `/api/nodes/<machine>/sensors…` for each managed machine.
| | |
|---|---|
| `GET /api/sensors` | Catalog, with current values. `?ids=fan.*,cpu.usage` narrows it. |
| `GET /api/sensors/{id}` | One sensor. `?format=text` → the bare value as text/plain. |
| `GET /api/sensors/{id}/history` | `{id, unit, t[], v[]}` — what the ring holds (300 samples at the sampling interval). Numbers only. |
| `POST /api/sensors/query` | Shaped query, below. |
| `GET /api/sensors/query?k=selector&…` | The same, flat, for consumers that can only fetch a URL. |
A selector is an exact ID or a glob. `*` crosses dots: `fan.*` is every fan,
`cpu.core.*.usage` every core.
## Shaped query
Send the object you want, with selectors where the values go. Get it back filled in.
```
POST /api/sensors/query
{ "cpu": "cpu.usage", "fans": "fan.*", "box": { "temp": "cpu.temp", "up": "uptime" } }
→ { "cpu": 12.4,
"fans": { "fan.nct6775.fan1": 820, "fan.nct6775.fan2": 1140 },
"box": { "temp": 41, "up": 80520 } }
```
- Exact ID → its value; `null` if this machine has no such sensor (so one query works
across machines that differ).
- Glob → an object keyed by ID; `{}` if nothing matches.
- Objects and arrays are walked; anything else in a query is a 400.
- `?full=1` → every value is the whole sensor object instead of the bare value. (In the
GET form `full` is therefore not usable as a key.)
## Not yet
Read-only scoped tokens, Prometheus `/metrics`, a push stream, widgets built on this,
an embeddable widget page, and non-metric sensors (services, SMART, ZFS, updates). Until
scoped tokens exist, **do not hand a Ham Lab token to a dashboard** — today's tokens are
full control of the machine.