The host's /run/dbus/system_bus_socket is the standard dbus socket for
systemd. Bind-mounting the container's socket onto /run/dbus overwrites
the host's socket, breaking dockerd's ability to manage cgroups via
systemd (manifests as 'org.freedesktop.systemd1 was not provided by any
.service files' on subsequent docker run).
Use tmpfs instead — ephemeral container-local sockets that don't conflict
with host services.
- audio-bridge: alpine + pipewire + pipewire-pulse + wireplumber + bluez + dbus + tini
Routes audio from librespot/mopidy/phone-BT to USB DAC (Q5K) via ALSA.
Privileged + host network for bluetoothd HCI access.
- mopidy: built from python:3.12-slim-bookworm + pip (no ghcr.io dep).
Qobuz (auth from developer.qobuz.com) + local files. PA bridge to audio-bridge.
- librespot: built from rust:1.85.1 on RPi (cross-compile too slow under qemu).
Spotify Connect target named 'Klubhaus'. PA bridge to audio-bridge.
Both mopidy and librespot build locally to avoid ghcr.io auth/rate-limit issues.
PEQ stays on Q5K side (Qudelix app, 20 bands) for source-independent EQ.
ntfy /json?poll=1 is regular polling, not long-poll. Worst-case
lag = 2 × POLL_INTERVAL × N_topics = 120s at 30s. Reducing to 5s
caps lag at ~20s with 2 topics. For sub-second latency, switch to
ntfy SSE (/<topic>/sse) - tracked separately.
NR's automatic package.json install at container start was slow and
required internet. Move npm install to image build time, preserve the
installed node_modules via an anonymous volume so the bind mount of
./nodered:/data doesn't clobber them.
- FLASH_INTERVAL_SECONDS 3 -> 2 (user preference)
- Add network_key / pan_id / ext_pan_id to zigbee2mqtt configuration
so paired devices can rejoin after a config wipe. Network key
preserved from previous working state on RPi.
- Doorbell listener flash logic with even toggle count so plug ends
at starting state
On a fresh (< MAX_AGE_SECONDS) ntfy message, spawn a thread that
publishes N TOGGLEs spaced FLASH_INTERVAL_SECONDS apart so total
runtime is FLASH_DURATION_SECONDS. Defaults: 5 toggles @ 3s = 15s.
Env vars (all in compose.yaml):
FLASH_INTERVAL_SECONDS default 3
FLASH_DURATION_SECONDS default 15
MAX_AGE_SECONDS default 180 (replay protection)
Each alert gets its own thread; concurrent alerts spawn concurrent
flashes. z2m just flips state per toggle, so the lamp alternates
regardless of overlap.
Polls ntfy.sh for ALERT_klubhaus_topic(_test) and publishes
{state: TOGGLE} to zigbee2mqtt/Sideboard Lamp/set on each new
message event. Dual-topic subscription so switching from test to
production requires no code change; sender still uses test topic.
- infra/doorbell-listener/: uv-based Docker service (arm64)
- compose: depends_on mosquitto, persists last_id per topic
- justfile: doorbell-publish, doorbell-logs, doorbell-rebuild,
restart-all now includes doorbell-listener
Captures everything needed to rebuild the stack from scratch:
infra/compose.yaml
- mosquitto (eclipse-mosquitto:latest)
- zigbee2mqtt (ghcr.io/pine64/zigbee2mqtt:latest-dev, BLZ fork)
- nodered (nodered/node-red:latest)
infra/mosquitto/config/mosquitto.conf
- anonymous listener on 1883, file logging, persistence on
infra/zigbee2mqtt/
- configuration.yaml: 5 paired devices with friendly names, 1 group
- coordinator_backup.json: zigbee network key + pan id (recovery)
- state.json: per-device persisted state (ON/OFF, linkquality)
infra/nodered/
- settings.js: nodered defaults (flowFile: flows.json, no auth)
- start.sh: patches Dashboard 2.0 SPA with <base href> at startup
Also:
- Updated z2m groups section to rename Laser & Fog -> DJ Booth
(matches the friendly_name already in devices section)
- Pushed the same change to live /root/zigbee2mqtt/configuration.yaml
and restarted the zigbee2mqtt container
- .gitignore: output/ (regenerated on every build, churns uuid ids)
- README.md: link to infra/README.md for rebuild procedure