services: mosquitto: image: eclipse-mosquitto:latest container_name: mosquitto restart: unless-stopped user: "1883:1883" ports: - "1883:1883" volumes: - ./mosquitto/config:/mosquitto/config - ./mosquitto/data:/mosquitto/data - ./mosquitto/log:/mosquitto/log zigbee2mqtt: image: ghcr.io/pine64/zigbee2mqtt:latest-dev # BLZ fork, NOT koenkk container_name: zigbee2mqtt restart: unless-stopped depends_on: - mosquitto ports: - "8080:8080" volumes: - ./zigbee2mqtt:/app/data devices: - /dev/ttyUSB0:/dev/ttyUSB0 environment: TZ: America/Los_Angeles # govee2mqtt: # image: ghcr.io/wez/govee2mqtt:latest # container_name: govee2mqtt # restart: unless-stopped # network_mode: host # environment: # GOVEE_EMAIL: your@email.com # GOVEE_PASSWORD: yourpassword # GOVEE_API_KEY: your-api-key # MQTT_HOST: 127.0.0.1 # TZ: America/Los_Angeles # nodered: build: ./nodered container_name: nodered restart: unless-stopped depends_on: - mosquitto user: "1000:1000" ports: - "1880:1880" volumes: - ./nodered:/data # bind: settings.js, flows.json, start.sh, package.json - /data/node_modules # anonymous: preserves build-time npm install environment: TZ: America/Los_Angeles DASHBOARD_BASE_HREF: /dashboard/ command: ["sh", "/data/start.sh"] # Listens to ntfy.sh for doorbell events and toggles a smart plug on each alert. # Topic: ALERT_klubhaus_topic_test (matches ESP32 firmware DEBUG_MODE suffix) # Action: TOGGLE on zigbee2mqtt/Sideboard Lamp/set # On a fresh alert, flash the plug: N toggles spaced FLASH_INTERVAL_SECONDS # so the total runtime is FLASH_DURATION_SECONDS. doorbell-listener: build: ./doorbell-listener container_name: doorbell-listener restart: unless-stopped depends_on: - mosquitto volumes: - ./doorbell-listener/state:/data/doorbell-listener environment: MQTT_HOST: mosquitto MQTT_PORT: "1883" PLUG_TOPIC: "zigbee2mqtt/Sideboard Lamp/set" POLL_INTERVAL: "5" MAX_AGE_SECONDS: "180" FLASH_INTERVAL_SECONDS: "1" FLASH_DURATION_SECONDS: "14" TZ: America/Los_Angeles # Audio router: PipeWire + WirePlumber + PulseAudio bridge + BlueZ. # Routes audio from librespot / mopidy / phone-Bluetooth to USB DAC (Q5K) # via ALSA. Falls back to RPi 3.5mm / HDMI if no USB DAC. # PEQ is applied on the DAC side (Q5K's Qudelix app, 20 bands), not here. # NOTE: /run/dbus + /run/user/0 use tmpfs (NOT host bind-mount) — the host's # dbus socket is /run/dbus/system_bus_socket and bind-mounting our container's # socket overwrites it, breaking dockerd's ability to talk to systemd. audio-bridge: build: ./audio-bridge container_name: audio-bridge restart: unless-stopped privileged: true network_mode: host devices: - /dev/snd:/dev/snd tmpfs: - /run/dbus:size=1M - /run/user/0:size=1M cap_add: - SYS_ADMIN # bluetoothd needs CAP_SYS_ADMIN for HCI - NET_ADMIN # Spotify Connect: phone's Spotify app shows "Klubhaus" as a Connect target. # Outputs audio to audio-bridge via PulseAudio (TCP localhost:4713). # Built locally on RPi (cross-compile is too slow under qemu). librespot: build: ./librespot image: infra-librespot:latest container_name: librespot restart: unless-stopped depends_on: - audio-bridge network_mode: host # CMD is set in Dockerfile; do not override here. volumes: - librespot_cache:/var/cache/librespot # Mopidy: Qobuz + local files + internet radio. Web UI on http://rpi:6680. # Outputs audio to audio-bridge via PulseAudio (localhost:4713). # Get Qobuz app_id + secret from https://developer.qobuz.com/api/v1/oauth # Built locally from python:3.12-slim + pip install (no ghcr.io image). mopidy: build: ./mopidy image: infra-mopidy:latest container_name: mopidy restart: unless-stopped depends_on: - audio-bridge network_mode: host ports: - "6680:6680" volumes: - ./mopidy/data:/var/lib/mopidy - ./mopidy/music:/music # drop FLACs in infra/mopidy/music/ environment: TZ: America/Los_Angeles QOBUZ_APP_ID: "${QOBUZ_APP_ID:-}" QOBUZ_APP_SECRET: "${QOBUZ_APP_SECRET:-}" QOBUZ_USERNAME: "${QOBUZ_USERNAME:-}" QOBUZ_PASSWORD: "${QOBUZ_PASSWORD:-}" volumes: librespot_cache: