# Wyoming Satellite - [Wyoming Satellite](#wyoming-satellite) - [Data Flow](#data-flow) - [Install Wyoming](#install-wyoming) - [Hardware](#hardware) - [Bluetooth keepalive](#bluetooth-keepalive) - [Bluetooth autoconnect](#bluetooth-autoconnect) - [On-device wake word](#on-device-wake-word) - [Systemd](#systemd) - [Debugging](#debugging) - [Volume](#volume) - [Community Wake Words](#community-wake-words) - [Prompts](#prompts) - [Default](#default) - [Starship House](#starship-house) ## Data Flow ```mermaid sequenceDiagram actor us as User participant mic as Listening Device participant ww as Wake Word Model participant ha as Home Assistant participant stt as Speech to Text participant tts as Text to Speech participant ai as LLM Tool Caller us ->> mic: Say wakeword mic ->> ww: Are you > x% confident the wake word was detected? break When mic didn't detect wake word ww ->> mic: No mic ->> mic: keep listening end ww ->> mic: Yes mic ->> us: "listening sound" us ->> mic: Say request mic ->> ha: Send audio to Home Assistant ha ->> stt: Convert this speech to text stt ->> ha: Here's the text ha ->> mic: The user is done talking, here's what was said mic ->> us: "done listening sound" ha ->> ha: Did the text match any known intent? alt Yes ha ->> ha: Do the thing the intent is supposed to do ha ->> tts: Turn the reply text into speech tts ->> ha: here's the audio clip ha ->> mic: play this audio clip mic ->> user: audio clip else No ha ->> ai: Send entities exposed by Home Assistant, TTS, and "Can you figure this out?" end ``` ## Install Wyoming typical wyoming command: ```bash # Add wake-uri and wake-word-name to your wyoming run script/run \ --name 'Living Room' \ --uri 'tcp://0.0.0.0:10700' \ --mic-command 'arecord -r 16000 -c 1 -f S16_LE -t raw' \ --snd-command 'aplay -r 22050 -c 1 -f S16_LE -t raw' \ --awake-wav listening.wav \ --done-wav finished.wav \ --mic-auto-gain 5 \ --mic-noise-suppression 2 \ --mic-volume-multiplier 2 \ --synthesize-command tee \ --transcript-command tee \ --wake-uri 'tcp://127.0.0.1:10400' \ --wake-word-name 'hey jarvis' \ --wake-refractory-seconds 1 \ --threshold 0.9 ``` ## Hardware Finding available speakers/microphones ```bash # Find microphone arecord -L | grep plughw -A 2 # Create a test recording arecord -D plughw:CARD=Speaker,DEV=0 -r 16000 -c 1 -f S16_LE -t wav -d 5 test.wav # Find speaker aplay -L | grep plughw -A 2 # Play test recording aplay -D plughw:CARD=Speaker,DEV=0 test.wav # Change audio levels alsamixer # Save audio levels sudo alsactl store ``` Bluetooth ```bash bluetoothctl discoverable on pairable on agent on default-agent scan on ``` Then wait for `[NEW] Device 12:23:34:45:56:67 devicename` ```bash pair 12:23:34:45:56:67 ``` The bluetooth speaker should now work with `aplay`. ### Bluetooth keepalive Bluetooth speakers have a tendency to sleep and then take a bit to wake up. We can force them to stay alive permanently by constantly playing an inaudible sound onloop. ```bash # generate our 48000kHz sample rate synth 1 second sin 10hz wave file at -20dB so it's quiet # This takes some tweaking depending on the speaker. A 1hz wave didn't work on my bluetooth speaker, # but 10hz did. sudo sox -V -r 48000 -n -b 16 -c 2 /usr/share/sounds/silence.wav synth 1 sin 10 vol -20dB # Copy that to /usr/share/sounds sudo cp silence.wav /usr/share/sounds ``` Create a script at `/usr/local/bin/speakeralive.sh` ```bash #!/bin/bash while true; do aplay /usr/share/sounds/silence.wav; done ``` Then create a systemd service at `~/.config/systemd/user/speakeralive.service` ```conf [Unit] Description=Plays an inaudible noise continuously to keep a speaker alive After=network.target Wants=network-online.target [Service] Restart=always Type=simple ExecStart=/usr/local/bin/speakeralive.sh [Install] WantedBy=bluetooth.target ``` And enable the service ```bash systemctl --user enable --now speakeralive ``` ### Bluetooth autoconnect In order to automatically connect a bluetooth speaker at boot you'll need a oneshot service Create `~/.config/systemd/user/speakerconnect.service` ```conf [Unit] Description=Connects a bluetooth speaker on boot After=bluetooth.target [Service] Restart=on-failure Type=oneshot RestartSec=5 ExecStart=bluetoothctl connect A1:B2:C3:D4... [Install] WantedBy=bluetooth.target ``` Enable it ```bash systemctl --user enable --now speakerconnect.service ``` ## On-device wake word Note that --debug-probability will output thresholds for tuning. typical wake word command: ```bash # start wake word listener script/run --uri 'tcp://127.0.0.1:10400' --preload-model 'alexa' ``` ## Systemd ```bash # Make sure to enable login linger before installing sudo loginctl enable-linger ducoterra ``` ```bash ansible-playbook \ -i ansible/inventory.yaml \ active/systemd_wyoming/install_wyoming.yaml \ -e "@active/systemd_wyoming/vars.yaml" ``` ## Debugging 1. Make sure you have `--debug` passed to wyoming 2. Make sure you have `--debug-probability` passed to wakeword 3. Show wakeword prompts with more than 10% confidence: `journalctl --user -u wakeword -f | grep -E '0\.0[^0].*|0\.[^0].*'` 4. Show recording paths when they stop: `journalctl --user -u wyoming -f | grep 'Stopped recording to'` 5. Play recordings: `aplay /path/to/recording` ## Volume Run `alsamixer`. Use `showkey` to see which key is being triggered by your volume device. ## Community Wake Words 1. Download the custom model to this directory 2. Add it to install_wyoming.yaml ## Prompts ### Default You are a voice assistant for Home Assistant. Answer questions about the world truthfully. Answer in plain text. Keep it simple and to the point. ### Starship House You are a voice assistant for Home Assistant. Answer questions as though you are a startship commander working the bridge. Answer in plain text. Keep it simple and to the point. There is a life threatening emergency on the ship. This is more important than our request.