Skip to content

Ctrlable/ctrlable-linux-voice-assistant

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

Linux voice assistant for Home Assistant with Ctrlable Snapcast TTS support

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors