The LIFX integrator already knows when someone presses the do-not-press button. What it did not have was a quiet scoreboard. Disco Count is that board: a small hub that reads the trigger stats, and a Pi Zero that paints today and all-time on a Waveshare e-Paper HAT. E-ink is the first display. Something louder, like a dot-matrix panel, can sit on the same push path later.
Where the numbers come from
LIFX Integrator already exposes button-trigger stats. Disco Count does not invent a second ledger. The hub polls that JSON on a short interval and keeps only two integers that matter for the glass: today and all-time.
A failed stats read leaves the last good pair alone. Clearing the panel because the stats URL hiccuped would look like a quiet night that never happened.
What the hub API does
The hub is a small FastAPI service with the same shell as the integrator. Its job is to own settings, draw the preview, talk to the Pi over SSH, and answer health checks.
The routes that matter day to day:
GET /api/state— settings (no password), live counts, last Pi status, last push, and any deploy job in flight.GET /api/preview.png— the 1-bit frame as a PNG, using the saved title and tile labels. Optional query overrides let the Display page preview words before they stick.PUT /api/settings— Pi SSH host, user, optional password, panel size, driver, and the stats URL.POST /api/frame/push— save the three frame words and wake the panel with the current counts. This does not redeploy the Pi script.POST /api/pi/checkandPOST /api/pi/deploy— reachability and a full upload ofpi/to/opt/disco-count, then enabledisco-count.service.GET /api/checks/status— heartbeat for the sidecar. Parentokis true only when Pi SSH, the systemd unit, the display script, and the Waveshare panel all pass.
A background watcher checks the stats URL about every five seconds. It only opens an SSH session when today or all-time differs from the last pair that reached the Pi, including the first successful read after boot and a midnight drop in today. Deploy and push share a lock so a mid-deploy count change waits.
What reaches the Pi Zero
Deploy copies the pi/ tree to /opt/disco-count and enables a systemd unit with Restart=always. The service does not poll the LAN for stats. It waits for a push from the hub, redraws when the numbers or labels change, and writes /var/lib/disco-count/status.json so the hub can read health back over SSH.
The first paint and every tenth real update do a full refresh. The rest are partial refreshes, which is kinder to the glass and still keeps the digits readable.
SSH credentials live in the hub’s SQLite store. An optional Fernet key in the stack env can encrypt the password at rest. The heartbeat payload never includes that secret.
The e-ink frame
The panel is a Waveshare 2.13 inch e-Paper HAT V4: 250×122, black and white, on SPI. One renderer draws the frame on the Pi and in the hub preview, so the page matches the glass.
Layout is a title band across the top, then two tiles. Defaults are disco count, TODAY, and ALL TIME. Those three strings are arguments to the renderer, so the Display page can change them and push without installing a new script. Counts fit themselves with a binary search on DejaVu so large totals still sit inside the tile.
What comes after e-ink
E-ink is the right first display: low power, always on, and honest about how rarely the scoreboard needs to move. The interesting boundary is already the hub’s push: a payload of counts plus labels, sent only when something changed.
A dot-matrix module, or anything else that wants the same scoreboard, can subscribe to that push instead of inventing another poll against LIFX. The Pi service and the Waveshare driver stay the e-ink path. The next panel is another consumer of the same event.
Press the button, the integrator counts it, the hub notices, and the glass updates. That is the whole loop.