259 lines
6.2 KiB
Markdown
259 lines
6.2 KiB
Markdown
# 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
|
|
|
|
<https://github.com/rhasspy/wyoming-satellite>
|
|
|
|
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
|
|
|
|
<https://github.com/rhasspy/wyoming-openwakeword/>
|
|
|
|
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
|
|
|
|
<https://github.com/fwartner/home-assistant-wakewords-collection?tab=readme-ov-file>
|
|
|
|
<https://github.com/fwartner/ha-openwakeword-installer>
|
|
|
|
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. |