No description
  • Python 92.3%
  • Dockerfile 5.3%
  • Shell 2.4%
Find a file
Pandi ec16873b76 docs: one start path for the satellite instead of three
The README ruled out a satellite image, then started one with docker run, while compose builds the satellite itself. Only compose runs today (no systemd unit, no venv install, and the referenced image 404s), so that is the documented path now.
2026-11-05 09:24:14 +01:00
satellite chore: put the voice stack under version control 2026-11-01 09:08:05 +01:00
.gitignore chore: put the voice stack under version control 2026-11-01 09:08:05 +01:00
attach_usb.py chore: put the voice stack under version control 2026-11-01 09:08:05 +01:00
docker-compose.yml feat(piper): thorsten-high is the voice that is on disk and in use 2026-11-04 09:14:40 +01:00
ha_pipeline.py chore: put the voice stack under version control 2026-11-01 09:08:05 +01:00
README.md docs: one start path for the satellite instead of three 2026-11-05 09:24:14 +01:00
selftest.py chore: put the voice stack under version control 2026-11-01 09:08:05 +01:00
test_attach_usb.py test(voice-stack): prove the TN_HOST guard aborts cleanly 2026-11-04 09:24:31 +01:00

Voice-Stack (lokal, ohne Cloud)

Alle vier Dienste — Whisper, Piper, openWakeWord und der Satellit — laufen als ein Compose-Stack auf dem Rechner, der Zugriff auf /dev/snd hat. Home Assistant läuft auf einem anderen Host und verbindet sich per Wyoming.

Die Adresse dieses Rechners steht nicht im Repo. Vor den Befehlen einmal setzen:

export VM_IP=<Adresse-des-Docker-Hosts>

Start: ein Weg, docker compose up -d

cd ~/coding/projects/voice-stack
docker compose up -d

Damit läuft der komplette Stack:

Dienst Port Image
whisper (STT) 10300 rhasspy/wyoming-whisper:latest
piper (TTS) 10200 rhasspy/wyoming-piper:latest
openwakeword 10400 rhasspy/wyoming-openwakeword:latest
satellite 10700 lokal gebaut aus satellite/Dockerfile

Der Satellit hat kein fertiges Image auf Docker Hub (rhasspy/wyoming-satellite → 404), deshalb baut ihn docker-compose.yml selbst: build: ./satellite, pip install wyoming-satellite. Er läuft mit network_mode: host (erreicht openWakeWord auf 127.0.0.1:10400, ist für HA auf 10700 erreichbar) und bekommt /dev/snd durchgereicht; Mikrofon und Lautsprecher sind die Skripte satellite/mic.sh und satellite/snd.sh.

Nur den Satelliten neu starten, z. B. nach dem Anstecken des Mikrofons:

docker compose up -d satellite

Es gibt keinen zweiten Startweg: kein venv-Install-Skript, kein systemd-Service, kein separates docker run. Ein zweiter Prozess würde dieselbe ALSA-Karte öffnen wollen, die schon der Container hält — ALSA vergibt sie exklusiv, einer bliebe ohne Gerät.

Logs:

docker logs wyoming-whisper --tail 20
docker logs wyoming-satellite --tail 20

Audio-Geräte prüfen

arecord -l    # Karte/Device des QuadCast
aplay -l      # Line-Out-Karte
ls /dev/snd   # muss pcmC*D*c/p zeigen, nicht nur seq und timer

satellite/mic.sh und satellite/snd.sh adressieren das Gerät als plughw:1,0. Das USB-Mikrofon hängt per Passthrough an der Maschine und fällt bei einer USB-Re-Enumeration heraus; dann fehlt pcmC* in /dev/snd und der Satellit bekommt kein Audio. Zurückholen:

export TN_HOST=<Host-der-TrueNAS-Box>
.venv/bin/python attach_usb.py

Sicherheit

Die drei Wyoming-Dienste (10300/10200/10400) haben keine Authentifizierung und sind im Compose auf allen Host-Interfaces veröffentlicht. Sie gehören damit ins LAN, nicht ins WAN — wer den Port erreicht, darf STT/TTS ohne Anmeldung benutzen.

HA

  1. Einstellungen → Geräte & Dienste → Integration hinzufügen → Wyoming Protocol
    • $VM_IP:10300 (Whisper) → wird zur STT-Engine
    • $VM_IP:10200 (Piper) → TTS
    • $VM_IP:10700 (Satellite) → wird assist_satellite
  2. Einstellungen → Voice-Assistenten → Pipeline: STT = faster-whisper, TTS = piper, Wake-Word = openWakeWord

Wake-Word „Pandi"

hey_jarvis ist nur Platzhalter (beweist die Kette). „Pandi" ist kein vortrainiertes Modell; openWakeWord-Modelle werden synthetisch trainiert (Piper-Stimmen als Samples). Erst machen, wenn die Kette läuft — sonst debuggt man zwei Dinge gleichzeitig.