docs: add CLAUDE.md for SLMM

Split out of the shared ~/CLAUDE.md, which had drifted badly — it described
SLMM at v0.1.0/v0.2.1 with none of the v0.4.0 design in it.

Written from the changelog and the actual tree. Leads with the three things
that define the current architecture: the fan-out monitor (the NL-43 has ONE
TCP control connection, which is the constraint the whole design works
around), the alert engine that pins the monitor on so rules evaluate 24/7,
and the nl43_readings history trail for live-chart backfill.

Defers to CHANGELOG.md for history rather than duplicating a version list.

Stack-level context lives in ../terra-view/docs/tmi-stack.md, loaded as
~/CLAUDE.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgTe8CamXAHcAmaQ6QNcog
This commit is contained in:
2026-08-29 07:52:52 +00:00
co-authored by Claude Opus 5
parent 43b8e53d2d
commit 518e6f287a
+91
View File
@@ -0,0 +1,91 @@
# CLAUDE.md — SLMM (Sound Level Meter Manager)
Device module for **Rion NL-43 / NL-53** sound level meters. Translates
Terra-View's REST calls into the meter's ASCII command protocol over TCP, and
pulls stored data over FTP. Current version: **v0.4.0**.
Stack-level context (which repo owns what, version pairing) lives in
`../terra-view/docs/tmi-stack.md`, which is also loaded as `~/CLAUDE.md`.
**Terra-View never talks to a meter directly** — it always goes through here.
SLMM owns the protocol; Terra-View owns the UI and the measurement records.
---
## Where things stand (updated 2026-08-29)
v0.4.0 (2026-06-22) is the current release. The three things that define the
current design:
- **Fan-out live monitor.** The NL-43 has **one** TCP control connection, and
every dashboard used to fight for it. Now a single poller reads the device and
all subscribers share one cached feed: `WS /api/nl43/{unit_id}/monitor`
delivers an instant first frame from cache, then live updates. Poll rate
adapts to demand, unreachable devices back off, and the background poller
skips units already covered by an active monitor so nothing double-polls.
- **Alert engine.** Per-device threshold rules (metric + threshold + cooldown)
with full CRUD, an onset/clear state machine, and acknowledgement. Enabled
rules **pin the monitor on**, so they evaluate 24/7 with no UI client
connected. Editing or deleting a rule resets its state and closes any open
event.
- **History for chart backfill.** A downsampled trail persists to `nl43_readings`
and is served from `GET /api/nl43/{unit_id}/history`, so live charts can fill
in recent history on load. L1/L10 percentiles are surfaced in status and the
live feed.
`CHANGELOG.md` is the authority for anything older; prefer it over prose here.
---
## Layout
```
app/
main.py FastAPI app + health checks
routers.py all REST endpoints
services.py NL43Client — TCP + FTP protocol
monitor.py the fan-out live feed
background_poller.py scheduled polling
alerts.py rule evaluation + event state machine
models.py NL43Config, NL43Status, nl43_readings
database.py SQLAlchemy / SQLite (data/slmm.db)
device_logger.py
docs/ manuals/ templates/ nl52/ SLM-stress-test/
```
Key docs: `docs/API.md` (endpoint reference with curl examples),
`docs/COMMUNICATION_GUIDE.md` and `docs/nl43_Command_ref.md` (protocol).
---
## API surface
```
GET/PUT /api/nl43/{unit}/config
GET /api/nl43/{unit}/status cached snapshot
GET /api/nl43/{unit}/live fresh read
POST /api/nl43/{unit}/{start|stop|pause|resume|reset|store}
GET /api/nl43/{unit}/{battery|clock|results|settings}
WS /api/nl43/{unit}/monitor fan-out live feed
POST /api/nl43/{unit}/monitor/{start|stop}
GET /api/nl43/_monitor/status
GET /api/nl43/{unit}/history backfill trail
.../alerts/rules .../alerts/events .../events/{id}/ack
POST/GET /api/nl43/{unit}/ftp/{enable|disable|status|files|download}
GET /api/nl43/{unit}/overwrite-check BEFORE changing a store name
```
---
## Gotchas
- **One TCP connection per meter.** This is the constraint the whole monitor
design exists to work around. Never add a code path that opens its own
connection alongside the monitor.
- **1-second minimum between commands.** The NL-43 protocol requires it; SLMM
enforces it automatically. Do not "optimise" it away.
- **Check `overwrite-check` before changing a store name** — otherwise you can
destroy data on the meter's SD card.
- **FTP is active mode** — the device connects *back* to the server on port 21.
- **`network_mode: host`** in compose, for direct network access to meters.
- Environment: `PORT` (8100), `CORS_ORIGINS`, `TIMEZONE_OFFSET`, `TIMEZONE_NAME`.