ADR 0018: Runtime sensor inventory
Status: Accepted Date: 2026-10-31
Context
Adding a probe such as an Atlas EZO pH circuit meant hand-editing
/etc/seedypod/hal.toml and restarting hald. That edit has a trap: any
[[sensor]] table replaces the whole stock list, including the water ranger
the dry-run interlock depends on. hald cannot write /etc/seedypod under
ProtectSystem=strict, it read its config once, a sensor that failed to probe
silently vanished, and the UI hard-coded which quantities it showed. Future
probes (dissolved oxygen, ORP, other vendors) should not need UI work.
Decision
-
Sensor kinds are an interface.
hald::kinds::SensorKinddescribes a kind: label, description, the quantities it reports, its bus and default address, and its calibration steps. It also builds the simulated or real driver, already probed. Its serialised form,DeviceKindInfo, is published, so the UI, the MCP server and the simulator render any kind without per-kind code. Today: EZO pH (0x63), EC (0x64) and RTD (0x66).hal/contract/fixtures/device_kinds.jsonis checked against the implementations, and core’s simulator imports it. -
Two sources.
hal.tomldefines the built-in (config) sensors. Sensors added from the app (user) live in<state_dir>/sensors.json, which is appended to the configured list, so it never trips the replace-stock rule. A bad overlay is ignored with a warning and never stopshaldfrom starting. -
Live apply.
devices/setadds or removes a user sensor in the running registry and republishessystem, Home Assistant discovery and the retaineddevicesinventory. A removed sensor’s HA entity is deleted with an empty retained config.devices/resultanswers each request byrequestId. -
Guard rails. An add is refused when:
- the kind isn’t addable;
- the address is outside 0x08–0x77 or already in use;
- the quantity is already installed;
- the probe doesn’t answer.
Removing a built-in sensor is always refused. The inventory reports sensors that failed to probe at boot as
absent, with the reason. -
Human-only. Agents get
list_devicesandseedypod://devices(read-only). Adding, removing and calibrating probes need a person at the unit, so they’re available only in the app and the REST API.
Consequences
- A new probe type needs one
SensorKindimplementation, aSensorIdif it measures a new quantity, and a regenerated fixture (SEEDYPOD_WRITE_FIXTURES=1 cargo test). The add dialog and the calibration wizard pick it up automatically. hal.tomlstays the place for built-in parts. Explicit[[sensor]]tables still replace the stock list; that behaviour is documented, not changed.
Sources: ADR 0004: Two tiers — hardware daemon and core · ADR 0007: HAL contract v1 · hald reference
Imported from docs/adr/0018-runtime-sensor-inventory.md in seedypod/core. Edit it there, not here.