Files
slmm/CLAUDE.md
T
serversdownandClaude Opus 5 5d0f31e584 docs: adopt the changelog & release convention
Brian asked for one standard across the three repos. seismo-relay and
Terra-View already had the pattern in their history; SLMM did not — earlier
releases used ad-hoc commits like "chore: version bump" with no [Unreleased]
section. So this introduces the practice here rather than documenting it, and
says so.

The rule: write the entry in the same commit as the work under [Unreleased],
cut the version on dev in a dedicated chore(release) commit, never touch the
changelog at a merge boundary. No preamble under [Unreleased] — the themed
paragraph gets written at release time when the whole release is visible.

The operational consequence is mandatory, including when it is "none". For
SLMM that is two things: whether a root-level migrate_*.py script is needed
and whether it is safe to re-run, and whether the change bounces the meter
connection — the NL-43 has a single TCP slot, so that is an operational event
rather than just a code change.

Version lives in app/main.py, the FastAPI version= argument.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qcu9ByJfuKBQxmrWb8rSrN
2026-09-18 18:02:59 +00:00

5.6 KiB

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.


Changelog & release convention

Adopted 2026-09-18, to match seismo-relay and Terra-View. SLMM had no written practice before this — earlier releases used ad-hoc commits like chore: version bump. This is the standard going forward.

Write the entry in the same commit as the work, under ## [Unreleased]. Cut the version on dev in a dedicated release commit. Never touch the changelog at a merge boundary.

  • Entry goes in with the change, not at merge or release time — that is the only moment you still know why. Feature branches edit CHANGELOG.md directly; the occasional conflict is two appended bullets and is trivial.
  • No preamble under ## [Unreleased] — just the ### Added / ### Changed / ### Fixed lists. The themed opening paragraph gets written at release time, when the whole release is visible and can be named honestly. A theme written when the first item landed is stale by the third.
  • ⚠ State the operational consequence, including when it is "none." Silence is ambiguous; "none" is information. For this repo that means:
    • DB migration — whether a migrate_*.py script is needed, which one, and whether it is safe to re-run. These live at the repo root and are easy to forget at deploy time.
    • Meter-connection impact — anything touching the NL-43/NL-53 TCP path, polling cadence, or the monitor fan-out. The meter has a single TCP slot, so a change that bounces the connection is an operational event, not just a code change.
  • Cutting a release is its own chore(release): vX.Y.Z commit on dev, renaming ## [Unreleased] → ## [X.Y.Z] - YYYY-MM-DD and bumping the version in app/main.py (the FastAPI version= argument).
  • main carries only released versions. No ## [Unreleased] section there; it lands via the dev → main PR.

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.