Skip to content

Latest commit

 

History

History
162 lines (114 loc) · 5.96 KB

File metadata and controls

162 lines (114 loc) · 5.96 KB

Ctrlable Linux Voice Assistant

A ready-to-deploy wrapper around linux-voice-assistant that integrates with the Ctrlable Snapcast TTS Streamer.

Run as many satellites as you have USB sound cards on a single machine. Each instance is an independent systemd service managed through a single ctrlable-va command.

How it works

linux-voice-assistant acts as an ESPHome device — Home Assistant connects to it, runs the Assist pipeline, and sends the TTS URL when a response is ready. With AUDIO_OUTPUT=snapcast (the default), this wrapper intercepts that URL and sends a HomeassistantActionRequest back to HA over the same ESPHome API connection — no token, no separate HTTP call — which triggers ctrlable_snapcast_tts.announce.

Wake word → satellite → HA Assist pipeline → TTS URL
  → HomeassistantActionRequest → ctrlable_snapcast_tts.announce
  → Snapcast → all clients in the group play the response

Requirements

Install

git clone https://github.com/Ctrlable/ctrlable-linux-voice-assistant
cd ctrlable-linux-voice-assistant
sudo bash install.sh

install.sh creates a shared virtualenv at /opt/ctrlable-linux-va, installs the upstream linux-voice-assistant package, and registers the systemd template ctrlable-voice-assistant@.service. A ctrlable-va shortcut is added to /usr/local/bin.

Multi-instance setup

Each satellite is one .env file + one systemd instance. No separate directories, no duplicate installs.

Add satellites

# First satellite — auto-assigns port 6053
sudo ctrlable-va add living-room \
  --input  hw:CARD=USB0,DEV=0 \
  --output hw:CARD=USB0,DEV=0

# Second satellite — auto-assigns port 6054
sudo ctrlable-va add kitchen \
  --input  hw:CARD=USB1,DEV=0 \
  --output hw:CARD=USB1,DEV=0

# Third satellite — local mpv playback instead of Snapcast
sudo ctrlable-va add office \
  --input  hw:CARD=USB2,DEV=0 \
  --output hw:CARD=USB2,DEV=0 \
  --local

Each command creates satellites/<name>.env, enables, and starts the service immediately.

Manage instances

sudo ctrlable-va list                    # show all instances + status + port
sudo ctrlable-va logs living-room        # follow logs
sudo ctrlable-va edit kitchen            # edit config, then restart
sudo ctrlable-va disable bedroom         # stop + disable
sudo ctrlable-va devices                 # list available audio devices

List output

INSTANCE                 STATUS     PORT     SATELLITE ID
--------                 ------     ----     ------------
kitchen                  active     6054     kitchen [snapcast]
living-room              active     6053     living-room [snapcast]
office                   active     6055     office [local]

Add to Home Assistant

Each instance listens on its own port. Add each as a separate ESPHome integration:

Settings → Devices & Services → Add Integration → ESPHome

Instance Host Port
living-room <machine-ip> 6053
kitchen <machine-ip> 6054
office <machine-ip> 6055

Wake word

Wake word engine and model are configured per-pipeline in the Home Assistant UI:

Settings → Voice Assistants → your pipeline → Wake word

Available options depend on which wake word packages are installed. See the linux-voice-assistant docs for installing on-device models (OpenWakeWord, MicroWakeWord).

To pass wake word flags to a specific instance, use a systemd drop-in:

sudo systemctl edit ctrlable-voice-assistant@living-room
[Service]
ExecStart=
ExecStart=/opt/ctrlable-linux-va/venv/bin/python /opt/ctrlable-linux-va/run.py \
    --port ${PORT} \
    --audio-input-device ${AUDIO_INPUT_DEVICE} \
    --audio-output-device ${AUDIO_OUTPUT_DEVICE} \
    --wake-word-engine on-device \
    --wake-word okay-nabu

Per-instance configuration

Each satellites/<name>.env supports:

Variable Default Description
AUDIO_OUTPUT snapcast snapcast or local
SNAPCAST_SATELLITE_ID (instance name) Snapcast satellite ID; defaults to the systemd instance name so you usually don't need to set this
PORT auto (6053+) ESPHome API port — must be unique per instance
AUDIO_INPUT_DEVICE default Microphone device
AUDIO_OUTPUT_DEVICE default Speaker device (used when AUDIO_OUTPUT=local)

Stable USB device names (recommended)

USB sound cards change their hw: index when replugged or on reboot. Create persistent udev symlinks so your configs survive reboots:

# Find vendor/product ID and serial of your USB card
udevadm info -a -n /dev/snd/controlC1 | grep -E 'idVendor|idProduct|serial'

Create /etc/udev/rules.d/85-usb-sound.rules:

SUBSYSTEM=="sound", ATTRS{idVendor}=="0d8c", ATTRS{serial}=="living_room_mic", SYMLINK+="snd/living-room"
SUBSYSTEM=="sound", ATTRS{idVendor}=="0d8c", ATTRS{serial}=="kitchen_mic",     SYMLINK+="snd/kitchen"

Then reference the stable name in your .env:

AUDIO_INPUT_DEVICE=hw:living-room

If your USB cards don't expose a unique serial, use the physical USB port path instead (KERNELS=="1-1.2" etc.).

Snapcast satellite ID

SNAPCAST_SATELLITE_ID defaults to the systemd instance name. If your instances are named to match your ESPHome Atom Echo device names (e.g. ctrlable-living-room), no extra config is needed:

sudo ctrlable-va add ctrlable-living-room --input hw:CARD=USB0,DEV=0 ...

Upstream

This project wraps OHF-Voice/linux-voice-assistant. Microphone capture, wake word detection, STT, and the ESPHome API protocol are all provided by that package.