How to Build a Local Voice Assistant with Home Assistant (Wyoming, Whisper and Piper — No Alexa, No Cloud)

Say it, and the lights change — with nothing sent anywhere: Whisper, Piper and a wake word on the server, Assist in Home Assistant, a £15 satellite on the shelf.

Alexa and Google Home send every utterance to a data centre and sell you things. Home Assistant's Assist does the same job locally: Whisper turns speech into text, Assist's intent engine (or a local LLM, 78) works out what you meant, Piper speaks the reply — all on the server, with openWakeWord listening for "okay nabu" on a satellite. The satellite can be the phone app, a Pi with a mic, or an ESP32-S3 box for £15 (the Home Assistant Voice PE if you'd rather buy one ready-made).

Quick reference

PieceWhat
Whisper (STT)rhasspy/wyoming-whisper, port 10300 — base-int8 on CPU, small with a GPU
Piper (TTS)rhasspy/wyoming-piper, port 10200 — voice en_GB-alan-medium
openWakeWordrhasspy/wyoming-openwakeword, port 10400
Home AssistantSettings → Integrations → Wyoming ×3 → Settings → Voice assistants → Assist pipeline
SatelliteHA Companion app (phone), ESPHome voice satellite (123), or wyoming-satellite on a Pi
Expose devicesSettings → Voice assistants → Expose: lights, switches, covers, scripts

1. The three containers

mkdir -p ~/docker/voice/{whisper,piper,wakeword} && cd ~/docker/voice
nano compose.yaml

services:
whisper:
image: rhasspy/wyoming-whisper:latest
container_name: whisper
command: --model base-int8 --language en --beam-size 1
ports:
- "10300:10300"
volumes:
- ./whisper:/data
restart: unless-stopped

piper:
image: rhasspy/wyoming-piper:latest
container_name: piper
command: --voice en_GB-alan-medium
ports:
- "10200:10200"
volumes:
- ./piper:/data
restart: unless-stopped

openwakeword:
image: rhasspy/wyoming-openwakeword:latest
container_name: openwakeword
command: --preload-model ok_nabu
ports:
- "10400:10400"
volumes:
- ./wakeword:/data
restart: unless-stopped

base-int8 runs a short command in about a second on an N100; tiny-int8 on a Pi 4; small/medium with a GPU for accuracy. en_GB-alan-medium is a decent British voice; Piper has dozens.

docker compose up -d
sudo ufw allow from 192.168.1.0/24 to any port 10200,10300,10400 proto tcp

2. Add them to Home Assistant

Settings → Devices & services → Add integration → Wyoming Protocol: host 192.168.1.50, port 10300 (Whisper). Again for 10200 (Piper) and 10400 (openWakeWord). Then Settings → Voice assistants → Add assistant: name "Home", language English, conversation agent Home Assistant, speech-to-text faster-whisper, text-to-speech piper, wake word ok_nabu. Set it as preferred.

3. Expose what it may control

Settings → Voice assistants → Expose: tick the lights, switches, covers, media players and scripts Assist may touch — and give them aliases ("the big lamp", "telly"). Assist understands "turn on the kitchen lights", "set the thermostat to 20", "is the garage door open", "turn off everything upstairs" (areas), timers ("set a timer for ten minutes"), and your own sentences via a YAML file or the UI for anything custom ("start the pump" → a script).

4. Talk to it

Phone: the Companion app → the Assist button (or assign it to the long-press action), speak, see the transcript and the reply, hear Piper. On Android it can be the default assistant.

A satellite on the shelf: an ESP32-S3-BOX-3 or an M5Stack Atom Echo (£15) flashed from the ESPHome dashboard (123) with the voice-assistant project — it listens for the wake word (on the server via openWakeWord, or on-device for the S3), streams audio to HA, plays the reply. The Home Assistant Voice Preview Edition is the ready-made version. A Pi with a USB mic and speaker runs wyoming-satellite for a room with a bigger speaker (Snapcast's rooms, 154, can play the responses).

5. Make it smarter

Assist's built-in intents are deliberate and local. For "what's the weather like for a bike ride tomorrow" or "add the things I just said to the shopping list", set the conversation agent to a local LLM: Ollama (78) with a model like llama3.2 or qwen2.5:7b via HA's Ollama integration, with Assist control enabled so it can still run device commands. Latency rises to a few seconds on CPU; a GPU makes it feel instant. Cloud models work too, but then it isn't local — the whole point.

6. Tune

Troubleshooting

Assist says "unexpected error" on speech. Whisper not reachable from HA (host networking in HA → use the LAN IP), or the model still downloading — docker compose logs whisper.

It hears but does nothing. The device isn't exposed, or the name doesn't match; try the alias. The debug view under Voice assistants shows each step.

Slow. A too-large Whisper model on CPU; base-int8, beam-size 1.

Wake word triggers constantly / never. Sensitivity in the openWakeWord integration options; mic gain on the satellite.

Wrapping up

Three containers, three Wyoming integrations, one Assist pipeline, the devices exposed with friendly aliases, a phone or a £15 box as the ears. Lights, heating, timers and your own scripts by voice, with every word processed on your server — and a local model behind it when you want conversation rather than commands.

Call us