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

3.7 KiB
Raw Blame History

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

{ "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.