Skip to content

Ceddan/cedlora

Repository files navigation

cedlora

Python tooling for a RAK7391 / ChirpStack v4 LoRaWAN edge — gRPC-web client with idempotent OTAA provisioning, MQTT subscriber, Milesight + Elsys sensor decoders, RAK-local SQLite uplink store, and a live dashboard.


Stack

layer component
Gateway RAK7391 WisGate Connect on Raspberry Pi OS Lite Trixie (cedlora-edge-v1 target)
Network server ChirpStack v4 native systemd on RAK7391 — gRPC-web on :8080, MQTT on :1883
Persistence RAK-local SQLite WAL at CEDLORA_DB_PATH; DuckDB optional for analytics
Dashboard FastAPI app (cedlora.api:app) — ced-charts series + live uplinks table
Updates Native apt/systemd workflow; no Docker or Portainer
Decoders Milesight-IoT/SensorDecoders (git submodule) + Elsys (vendored) + Node subprocess

Repository layout

path what
docs/INDEX.md grep route index: active owners, historical-only files, generated/cache/build paths, and field-role workflows
cedlora/ RAK-local SQLite store, CS4 MQTT subscriber, FastAPI dashboard, CLI, sensor decode pipeline
cedlora/sensors/ Decoder registry, canonical.py Node runner, _runner.js, handler.py
cs4/ RAK7391 CS4 gRPC-web client (idempotent provision_* chain, OTAA, devices, gateways)
vendor/milesight-decoders/ Official Milesight JS decoders (git submodule)
vendor/third-party/ Elsys and other third-party sensor decoders
frontend/ dashboard.html (live charts + uplinks table), topology.html, blog.html
docs/ device registry (devices/<eui>.md), site SSOT (sites/<id>.md), gateway SSOT
migrations/ SQLite + legacy PostgreSQL migrations
scripts/ provision-sensor.sh, add_sensor.py, env-sync.sh, env-verify.sh, probe_cs4_wire.py
crates/ Rust edge runtime (cedlora-protocol + cedlora-edge: uplink-publisher, edge-ui)
.claude/ LLM-agent context — rules, lexicon, stack-map, plans, decisions

Quick start

git clone --recurse-submodules <repo>
cd cedlora
python3 -m venv .venv && source .venv/bin/activate
pip install -e .

cp .env.example .env   # fill in CS4_HOST + CS4_API_KEY
cedlora tail           # stream uplinks from RAK-local CS4 MQTT → SQLite
cedlora serve          # dashboard at http://localhost:8000

Native RAK update dry-run:

scripts/rak-native-update.sh --host [email protected] --dry-run

Provision an OTAA sensor:

bash scripts/provision-sensor.sh \
  --eui <16-hex> --key <32-hex> \
  --join-eui <16-hex> \
  --model elsys-ers2-lite \
  --site cedlora-test-01

Environment

cp .env.example .env
# edit .env — required vars:
#   CS4_HOST          http://<rak7391-wg-ip>:8080
#   CS4_API_KEY       create with: ssh rak@<rak7391-wg-ip> sudo chirpstack --config /etc/chirpstack create-api-key --name cedlora
#   CEDLORA_DB_PATH   /var/lib/cedlora/cedlora.db on RAK; .data/cedlora.db for dev
#   MQTT_BROKER       localhost on RAK; <rak7391-wg-ip> from WSL dev
#   LORAWAN_APP_KEY   32-char uppercase hex (default AppKey for batch provisioning)

LLM-agent entrypoint

CLAUDE.md is the agent boot card — route table for every domain concept, stack table, veto list.

.claude/rules/ contains the domain vocabulary:

file what
cedlora-lexicon.md canonical words per domain (LoRaWAN, sensors, CS4, persistence, frontend)
cedlora-stack-map.md word topology — layers + roads + boundary-word table
cedlora-word-standards.md per-word SSOT + SOTA + rule + shape + verify + anti
cedlora-edge-contract.md identity, IP plan, services, ingest envelope, banned patterns, falsifiers
auto-improve.md append-only session learnings (corrections + calibration results)

.claude/plans/open/ — active feature plans. .claude/decisions/ — architecture decisions (append-only, criteria + alternatives + falsifier).

Field-role routes live in docs/INDEX.md: electrician gateway health, plumber placement handoff, installer OTAA provision + join proof, property technician dashboard checks, and software maintainer gates/bootstrap.


Status

Last updated: 2026-05-21

area status notes
CS4 gRPC-web client ✅ operational provision_tenant/app/profile/device, OTAA chain, idempotent find-or-create
CS4 MQTT stream ✅ operational RAK-local Mosquitto; dashboard ingest subscribes to application/+/device/+/event/up; gateway bridge publishes {region}/gateway/+/event/up upstream of CS4
RAK-local persistence ✅ operational SQLite WAL via cedlora/db.py; non-blocking sink, batched flush
Sensor decoders ✅ operational Milesight EM/AM/CT catalog + Elsys ERS2 Lite vendored
Device registry ✅ operational DB catalog + docs/devices/<eui>.md + drift gate
Dashboard ⚠️ cutover pending FastAPI app exists; target deployment is on the RAK
Topology page ✅ done LR node tree — sensor → gateway → NS → broker → DB → UI
Autonomous OTAA provisioning ✅ done scripts/provision-sensor.sh + cs4/client.py:provision_device_otaa, idempotent
Gateway register ⚠️ direct SQL gw.create_gateway() not yet in client; use ChirpStack UI or INSERT INTO gateway for now
WireGuard mesh ✅ operational cedlora-test-01 at 10.30.0.12 via koligo hub 89.167.42.17:51820

Changelog

2026-05-21 — cedlora-test-01-edge brought up; UG63/UG65 removed

  • RAK7391 freshly imaged with Raspberry Pi OS Lite Trixie; static eth0=10.42.0.1, WG=10.30.0.12
  • LoRa data flow proven end-to-end: lora-pkt-fwd → gateway-bridge → MQTT → CS4 → SQLite
  • CS4 cleaned to 1 tenant (cedlora-test-01); single admin api-key (cedlora); gateway 0016c001f1563ea5 registered
  • Milesight UG63 + UG65 codepaths removed — RAK7391/CS4 is the only gateway/NS stack going forward
  • ug63/sensors/ relocated to cedlora/sensors/ (gateway-agnostic decode)

2026-05-16 — RAK-local runtime target

  • Runtime moved off propella-PC dependency: RAK7391 owns CS4, MQTT, persistence, dashboard
  • RAK runtime is native systemd/apt only; Docker/Portainer is not an accepted runtime or update path
  • Live probe: RAK has ChirpStack 4.18, gateway-bridge, Mosquitto, PostgreSQL, Redis active
  • DATABASE_URL no longer required for normal runtime; CEDLORA_DB_PATH is the active storage knob

2026-04-29 — CS4 provisioning + device registry + dashboard

  • cs4/client.py with [PROBED] field numbers from live JS bundle — full Create chain, idempotent provision_device_otaa, delete_device
  • Device catalog table + docs/devices/ MD mirror + scripts/verify-devices-ssot.py drift gate
  • Dashboard charts: ced-charts crosshair sync fixed (dispatches domain time value, receivers compute nearest index)
  • API /topology + /api/health endpoints
  • scripts/probe_cs4_wire.py — extracts CS4 proto field numbers from the live frontend JS bundle

2026-04-27 — RAK7391 CS4 native install

  • ChirpStack v4 native systemd install on RAKPiOS (no Docker)
  • pg_trgm extension required before first migration (documented in auto-improve)
  • CS4 gRPC-web client: port 8080 only (not 8090), /api.{Service}/{Method} paths
  • api-key Bearer auth — static JWT from chirpstack create-api-key

Architecture decisions

decisions.md + .claude/decisions/ — every non-trivial choice documented with criteria, alternatives, reversal condition, and falsifier.

Key decisions:

  • RAK-local SQLite WAL for live edge persistence; DuckDB optional for analytics; Timescale/PostgreSQL not load-bearing
  • RAK7391 as deploy host for normal runtime; PC/propella is no longer a runtime dependency
  • Central MQTT broker on RAK7391
  • CS4 gRPC-web via handcrafted protobuf (no .proto files in deployed CS4 native install)
  • Milesight UG63 + UG65 superseded 2026-05-21 — RAK7391/CS4 is the only gateway stack

Contributing / dev

ruff check .          # linting
pytest tests/         # smoke tests (requires LAN/WG access to RAK7391)

About

LoRaWAN gateway tooling — UG63 (CS3) + UG65 (loraserver) + RAK7391 (CS4) — Python clients, sensor decoders, TimescaleDB sink, live dashboard

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors