A self-hosted home telemetry and device-management platform, primarily for conveniently ingesting data into Home Assistant, configuring local automations, and reliably managing and updating devices in bulk even remotely. A central Go service ingests sensor data over MQTT, stores it in SQLite, exposes a web dashboard, and publishes Home Assistant MQTT discovery. Around it sit a handful of small ingest/agent services and some ESP32/nRF firmware.
Everything talks over a single Mosquitto broker using the nokkonen/ topic
namespace. Devices are keyed by a 12-hex-char ID (^[0-9a-f]{12}$), derived
from a MAC or assigned per service.
services/ Go services (each an independent module)
state-machine central hub: MQTT ingest, SQLite, web UI, HA discovery, OTA + control
ble-ingest BLE (BlueZ/D-Bus) -> MQTT temp/humidity sensors
ld-2450-ingest LD2450 presence radar (serial) -> MQTT
bme-680-ingest BME680 environmental sensor (I2C) -> MQTT env (temp/rh/pressure/gas)
machine-monitor per-Linux-host agent: thermals/fans/disk/load + reboot/shutdown control
wol-helper LAN helper: Wake-on-LAN magic packets + ICMP reachability probes
firmware/ ESP32 / nRF (WM1110) sensor firmware
esp32c3-radio_env, esp32s3-radio_env, wm1110-temp_rh
infra/mosquitto/ broker config (nokkonen.conf) and ACLs (nokkonen.acl)
Each service is its own Go module under the github.com/0mlml/nokkonen/services/<name>
module prefix, tied together by a top-level go.work for local development. The
shared module holds the common MQTT-connect and wire helpers used across the
services.
-
state-machine: the hub. Subscribes to all ingest/meta/ack topics, persists to SQLite (schema via
services/state-machine/migrations/), serves a dashboard (default:8080), publishes Home Assistant MQTT discovery, and drives machine control, WoL, and ping. Config via env (MQTT_BROKER,DB_PATH,WEB_ADDR,WEB_BASE_URL, ...).It is also the FOTA control plane: upload a firmware binary, sign it with the server's Ed25519 key, and push an over-the-air update to a single device or to a whole firmware group in bulk, tracking each device's OTA ack. Only signed firmware can be pushed.
-
machine-monitor: runs on each Linux host. Reports thermals/fans/disk/ load/mem, and - only when locally configured
CONTROLLABLE=true- accepts reboot/shutdown. The host's own config is the source of truth: a report-only box (the default) rejects all control commands regardless of what the hub sends. See itsDEPLOY.mdfor the polkit setup controllable hosts need. -
wol-helper: one always-on LAN box. Turns MQTT requests into Wake-on-LAN magic packets, and ICMP-pings machines (via the system
ping) so the hub can tell "shut down" from "alive but not reporting". -
ble-ingest / ld-2450-ingest / bme-680-ingest: bridge local hardware (BLE sensors, LD2450 serial radar, BME680 I2C environmental sensor) onto the MQTT ingest topics.
bme-680-ingestpublishes onto the sharedenvtopic likeble-ingest, adding barometric pressure, gas resistance, and (after a gas burn-in) an approximate air-quality index.
Requires Go 1.26+ and a Mosquitto broker configured with the files in
infra/mosquitto/ (password auth + ACLs; add a mosquitto_passwd entry per
service/device username).
Build any service from its directory:
cd services/state-machine && go build ./Each service ships a systemd unit and a DEPLOY.md with step-by-step
cross-compile, install, and configuration instructions.