bonaminandClaude Opus 5.5 46c5c0a846 fix(mqtt): treat broker-replayed retained messages as stale state
The firmware publishes status/heartbeat, system/alerts, system/info and
status/playback with retain=true. On every backend (re)connect - every
restart and every uvicorn --reload - the broker replays the last message on
each of those topics for every device that ever connected. We handled those
replays as if they had just happened:

- heartbeats: a row with received_at=now() for every device, so devices
  that have been dead for months showed ONLINE for 90s after each restart
  and got pinged. ~770k such rows exist locally.
- boot_report: the last boot logged again as a new reboot (the phantom
  PANIC entries on the Health tab).
- alerts / other info events: logged again as new occurrences.

MQTT delivers retain=1 only for replays caused by a new subscription; live
publishes always arrive with retain=0. The flag is now passed through to the
handlers and the WS broadcast:

- heartbeat: replays are not stored. A live heartbeat is.
- {"state":"offline"} heartbeat (LWT / graceful disconnect) is no longer
  stored as a sign of life. It marks the device offline immediately in
  a small in-memory set (mqtt/presence.py) used by /mqtt/status and the ping
  loop; a later live heartbeat clears it. Replayed offline markers also mark
  offline, since a retained message is the device's last word.
- boot_report: live -> always a new boot. Replay -> stored only if it
  differs from the device's latest boot row (i.e. we missed it while down).
- alerts: replay still syncs the current-alert row; history gets a row on a
  live alert (even an identical repeat - faults recur) or on a replay that
  changes state. Replaces the 98dd16b rule that dropped identical live alerts.
- other info events: replays are not logged.
- Frontend (DeviceList, DeviceDetail, LogsTab) ignores retained WS messages
  for live updates, and flips a device offline on the offline marker instead
  of marking it online.

Verified locally after a backend restart: only the 7 actually-live devices
got new heartbeat rows (none from the replays), and no boot/alert/info rows
were created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:01:41 +03:00
2026-02-25 21:29:56 +02:00
2026-02-16 19:57:22 +02:00

BellSystems Admin Panel

Self-hosted web admin panel for managing BellSystems devices, melodies, users, and MQTT communications.

Tech Stack

  • Backend: Python / FastAPI
  • Frontend: React + Tailwind CSS (Vite)
  • Database: Google Firestore (Firebase Admin SDK)
  • MQTT: Mosquitto (paho-mqtt)
  • Auth: JWT with role-based access control
  • Deployment: Docker Compose + Nginx

Getting Started

# Clone the repo
git clone <your-gitea-url>/bellsystems-admin.git
cd bellsystems-admin

# Copy env template and fill in your values
cp .env.example .env

# Place your Firebase service account key in the project root
# (file is gitignored — never commit it)

# Start everything
docker compose up --build

Project Structure

bellsystems-admin/
├── backend/          # FastAPI API server
├── frontend/         # React SPA
├── nginx/            # Reverse proxy config
├── docker-compose.yml
└── .env

Documentation

See BellSystems_AdminPanel_Strategy.md for the full architecture and build plan.

S
Description
BellSystems Contol Panel. Handles everything from Devices to Clients. Firebase / Mosquitto / Device Control / logging...
Readme
33 MiB
Languages
JavaScript 72.3%
Python 12.8%
HTML 12.4%
CSS 2.5%