[EPIC] Migrate doorbell delivery ntfy→MQTT (push, not poll) #9
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
ESP32 doorbell displays poll
ntfy.shevery 15s (POLL_INTERVAL_MS). ntfy is slow/flaky (shared cloud infra, rate limits, ISP path), so ring→display latency is up to 15s+ and depends on internet health. Use case is an internet doorbell: callers ring from outside all of our networks.Decision
Keep ntfy.sh as the internet ingress (callers/covers unchanged — it stays reachable from anywhere). Add an
ntfy→MQTTbridge and convert ESPs from poll to subscribe (push). This is the LAN/instant layer, independent of ntfy/ISP health for the display path.Topology
Tasks
Phase 1 — Recon (no system changes)
rpi-zigbee-stack(Mosquitto + Zigbee2MQTT + Node-RED).Phase 2 — Bridge (
ntfy→MQTT) on dhg.lol*_klubhaus_topicntfy topics, republish toklubhaus/doorbell/{alert,silence,admin,metrics,status}.DoorbellLogic, event/message/title).mosquitto_sub -h <klubhaus-pi> -t 'klubhaus/doorbell/#'.Phase 3 — Firmware (
klubhaus-doorbell)DoorbellLogicalongside ntfy poll; reuseonAlert/onSilence/onAdmin.MQTT_HOST, creds, topic root inConfig.h+secrets.h.Phase 4 — Hardening & ramp-down
allow_anonymous false, enablepwfile) before doorbell traffic flows.Open questions
Verification criteria
Related
Phase 1 recon results
Mosquitto config (from
david/rpi-zigbee-stack/mosquitto.conf, cloned 2026-08-28):listener 1883,allow_anonymous true, no TLS, no pwfile → bare, LAN-only profileNetwork topology (confirmed):
192.168.8.111(wlp2s0), gateway192.168.8.1= berylpunkztbpapydnj(local172.30.53.219/16)192.168.9.0/24 via 172.30.68.103 dev ztbpapydnjPi address — README is stale: README says
192.168.9.147;known_hostshas SSH keys for192.168.9.167. Both currently unresponsive:.147: ICMP alive (only host responding in scan was.1), MQTT:1883refused.167: ICMP no answer, SSH:22refused192.168.9.0/24→ only.1aliveBlocker: klubhaus Pi is not reachable from this host right now. Need Zebra/tunnel access on-site, or the Pi powered on/reconnected before we can introspect the broker and test the bridge.
Action on unblock: SSH to Pi, verify mosquitto up (
systemctl/docker compose), capture real host/IP + pwfile presence, then Phase 2 (bridge on dhg.lol) can proceed independently of the Pi being reachable for build/test.Plan correction — the bridge already exists (DietPi side)
Phase 1 recon completed by reaching the Pi at its real address
root@192.168.81.147(ZeroTier peer172.30.158.123; earlier.9.147/.167addresses are stale). Unblocked by re-running the recordedzt-mtu-probe.sh(thezerotier-mtu.servicewas enabled-but-dead).Discovered:
doorbell-listeneron the Pi already does ntfy→MQTTSource:
/root/nr-flow-validator/infra/doorbell-listener/doorbell-listener.py(imageinfra-doorbell-listener).ALERT_klubhaus_topic[_test]every 30s (paho v2 API), on fresh alert publishes{"state":"TOGGLE"}tozigbee2mqtt/Sideboard Lamp/set(flash the lamp). Canary/probe alerts taggeddoorbell-probe→zigbee2mqtt/DoorbellProbe/setinstead.429 Too Many Requestsfrom ntfy.sh — polls without theauthtoken (anonymous tier), 2 topics × 30s ≈ 5,760 req/day ≫ anonymous limit. This is the primary cause of observed doorbell slowness AND of the Kumadoorbell-listener(monitor #25) "No heartbeat" history.Revised Phase 2 (replaces old build-on-dhg.lol)
NTFY_TOKENenv +auth=param todoorbell-listener.pypoll URL (mirror frontend'sBearer tk_...handling). Fixes 429.klubhaus/doorbell/alert(plus silence/admin) so ESPs can subscribe.mosquitto_sub -h 192.168.81.147 -t 'klubhaus/doorbell/#'.Revised Phase 3 (firmware)
DoorbellLogic, keep ntfy poll fallback.Mosquitto (
192.168.81.147:1883):allow_anonymous true, haspassword_file+acl.confpresent but anonymous still open. Route to clubhouse restored via MTU probe.Pending: confirm ntfy token for the listener and whether NOTE: the frontend's embedded token (
tk_sw5...) should be reused server-side.429 fix shipped + probe routing regression fixed
Root cause of slowness confirmed:
doorbell-listenerpolled ntfy.sh without theauthtoken → anonymous-tier 429s (≈2 topics × every 5s). Fixed innr-flow-validator/infra/doorbell-listener/doorbell-listener.py:NTFY_TOKENenv +auth_param()(base64Bearer tk_..., mirrors frontendbuildAuthParam)json?poll=1&auth=<b64>&since=<last_id>NTFY_TOKENset ininfra/compose.yaml(same token as frontend — topics are access-controlled)Regression caught during deploy: the tracked
nr-flow-validatorcopy ofdoorbell-listener.pywas BEHIND the Pi's live file — the Pi had uncommitted probe-routing support (PROBE_PLUG_TOPIC→ zigbee2mqtt/DoorbellProbe/set fordoorbell-probe-tagged alerts so E2E tests never flash the real lamp). Deploying the tracked copy clobbered it once (one real Squiggle lamp flash). Fixed by merging the Pi version + token change; tracked repo copy now equals the Pi's live file.Verified:
ALERT_klubhaus_topic_testreceived in 3s → TOGGLE flash tozigbee2mqtt/DoorbellProbe/set(probe device only)infra-doorbell-listenerrebuilt with merged code (2 probe refs, 3 auth refs)Note for future sessions:
doorbell-listener.pyhas uncommitted local state on the Pi innr-flow-validator(M doorbell-listener.py,M compose.yaml,?? doorbell-probe/,?? scripts/). Sync repo ↔ Pi before editing; the Pi copy may be ahead.Kuma doorbell service down — fixed + committed
Root cause: the
doorbell-kuma-heartbeat.shcanary published to ntfy.sh anonymously → anonymous rate limit →ntfy_publish_failed→ pushedstatus=downto Kuma monitor #25. Compounding: the listener polled ntfy.sh every 5s (~34k req/day), exhausting the account window.Fixes (live on DietPi, committed to
64906):doorbell-listener.py: poll withauth=(Bearer token) + probe-tag routing → DoorbellProbekuma-heartbeat.sh: canary publishesAuthorization: Bearer <token>instead of anonymous; token read from.secrets/ntfy-tokenPOLL_INTERVALraised 5s → 60sVerified: Kuma #25 heartbeats
status=1 | doorbell ok; listener logs 0× 429.Commit:
f4d6be0 doorbell: add ntfy token auth to listener poll + authenticated kuma canary(nr-flow-validator).