Homie is a Raspberry Pi voice assistant built for always-on use at home. It listens for a wake word, records a spoken command, transcribes it, generates a response with either local or cloud models, and speaks the answer back through Bluetooth or wired audio.
It is designed to run on a Raspberry Pi 5 while still remaining easy to develop over SSH.
- Wake-word driven interaction with
hey homie - Speech-to-text with either local Whisper or OpenAI cloud transcription
- Text generation with either a local llama.cpp model or OpenAI models
- Text-to-speech with either local Piper or OpenAI TTS
- Real-time tool calling through OpenAI function calling
- Wikipedia lookup for stable background knowledge
- Web/news lookup for current information like football results or recent events
- Spotify playback for tracks, artists, albums, and playlists
- Bluetooth speaker output with dynamic fallback to USB-to-AUX or other wired output
- Boot automatically on Raspberry Pi startup via
systemd
Homie currently uses this high-level flow:
- Vosk listens continuously for the wake word.
- After wake word detection, the microphone audio is buffered and captured.
- The spoken request is transcribed with either cloud STT or local Whisper.
- The text request is sent to either OpenAI or a local LLM.
- If the LLM decides a tool is needed, Homie executes the tool and feeds the result back to the model.
- The final answer is spoken with either cloud TTS or local Piper.
- Wake word:
hey homie - Command transcription language: currently configured as Dutch with
STT_LANGUAGE = "nl" - LLM responses: same language as the user input when using the cloud LLM path
Each major stage can run either locally or through OpenAI:
USE_CLOUD_STTUSE_CLOUD_LLMUSE_CLOUD_TTS
This makes it possible to run:
- fully cloud-backed
- fully local
- hybrid combinations, for example local wake word + cloud LLM + local TTS
Homie selects output sinks dynamically at playback time.
Priority order:
- USB / wired audio adapter
- Bluetooth speaker
- Any remaining generic system sink
This means:
- Plugging in a USB-to-AUX adapter before the next response should make Homie use it automatically.
- Unplugging it should make Homie fall back to Bluetooth on the next response if Bluetooth is available.
- No restart is required.
- The output sink is checked when playback starts, not only at application boot.
homie.py: main runtime, wake word handling, STT, TTS, LLM calls, tool routing, sink selectionconfig.py: configuration flags, model names, API environment variables, audio settingstools/search.py: Wikipedia and current web/news search helperstools/spotify.py: Spotify playback helpertools/weather.py: weather helpersystemd/homie.service: user service for auto-start on boot.env: local secrets and API keys, not committed to git
Typical environment for this project:
- Raspberry Pi 5
- Python virtual environment in
.venv - PipeWire / PulseAudio available for audio output routing
- Bluetooth support if using a Bluetooth speaker
- Internet access for OpenAI, Wikipedia, web search, and Spotify features
If you are using local models, you also need the relevant model files in models/.
Main configuration lives in config.py.
Important flags:
USE_CLOUD_STT = True
USE_CLOUD_LLM = True
USE_CLOUD_TTS = True
STT_LANGUAGE = "nl"
WAKE_WORD = "hey homie"Current cloud model defaults:
OPENAI_STT_MODEL = "gpt-4o-mini-transcribe"
OPENAI_LLM_MODEL = "gpt-5.4-mini"
OPENAI_TTS_MODEL = "gpt-4o-mini-tts"
OPENAI_TTS_VOICE = "alloy"Store secrets in .env.
Minimum OpenAI setup:
OPENAI_API_KEY=your_openai_api_keySpotify setup:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888/callback
SPOTIFY_DEVICE_NAME=HomieSpotify playback on the Raspberry Pi uses a local Raspotify Spotify Connect device named Homie.
Homie sets the system default sink immediately before Spotify playback, using the same priority order as TTS:
- USB / wired audio
- Bluetooth speaker
- Any remaining sink
That means Spotify music should follow the same USB-first, Bluetooth-fallback routing as spoken responses.
Activate the virtual environment and start Homie:
cd ~/projects/homie
source .venv/bin/activate
python homie.pyOr run directly without activating:
cd ~/projects/homie
./.venv/bin/python homie.pyAfter changing Homie code, restart the user service to load the new code immediately:
cd ~/projects/homie
./restart_homie.shYou do not need to reboot the Raspberry Pi. Restarting the service is enough.
If you want to restart and immediately watch live output in your SSH terminal:
cd ~/projects/homie
./restart_homie.sh --followHomie can run automatically at startup while the Raspberry Pi remains fully available over SSH.
Install the user service:
mkdir -p ~/.config/systemd/user
cp ~/projects/homie/systemd/homie.service ~/.config/systemd/user/homie.service
systemctl --user daemon-reload
systemctl --user enable --now homie.service
loginctl enable-linger homieUseful service commands:
systemctl --user status homie.service
journalctl --user -u homie.service -f
systemctl --user restart homie.service
systemctl --user stop homie.serviceQuick restart helper from the project root:
./restart_homie.shShow live Homie output after restart:
./restart_homie.sh --followWhy this approach works well:
- Homie starts automatically at boot
- SSH remains available for development and debugging
- The service runs in the user session, which matches PipeWire, PulseAudio, and Bluetooth audio better than a root system service
If the Bluetooth speaker disconnects or idles out after silence, disable the Bluetooth idle timeout.
Edit the Bluetooth config:
sudo nano /etc/bluetooth/main.confUnder the [General] section, add or update this active setting:
IdleTimeout=0Do not prefix the line with #, or it will remain a comment.
Restart Bluetooth after saving:
sudo systemctl restart bluetoothInspect current PulseAudio / PipeWire sinks:
pactl list short sinksQuick sink-selection test from Python:
./.venv/bin/python - <<'PY'
from homie import list_audio_sinks, choose_output_sink
sinks = list_audio_sinks()
print('SINKS:', sinks)
print('CHOSEN:', choose_output_sink(sinks))
PYIf you want to see service logs while testing audio:
journalctl --user -u homie.service -fWhen using the OpenAI LLM path, Homie exposes tools to the model so it can fetch or control things proactively.
Current tools:
get_timeget_weatherset_remindercontrol_devicesearch_wikipediasearch_webspotify_play
The prompt is configured to encourage proactive usage when tools improve accuracy, especially for:
- stable knowledge topics via Wikipedia
- current or fast-changing information via web/news search
- music playback via Spotify
Spotify playback requires API credentials in .env.
Create a Spotify app at:
https://developer.spotify.com/dashboard
Add these environment variables to .env:
SPOTIFY_CLIENT_ID=your_client_id
SPOTIFY_CLIENT_SECRET=your_client_secret
SPOTIFY_REDIRECT_URI=http://127.0.0.1:8888/callbackThe same redirect URI must also be configured in the Spotify developer dashboard.
The first real Spotify use may require one-time OAuth authorization.
This project includes a Raspotify setup so the Raspberry Pi itself appears in Spotify as a device named Homie.
Install and enable it with:
cd ~/projects/homie
curl -sL https://dtcooper.github.io/raspotify/install.sh | sh
mkdir -p ~/.config/raspotify ~/.config/systemd/user
chmod +x ~/projects/homie/run_raspotify_service.sh ~/projects/homie/run_raspotify_event_hook.sh
cp ~/projects/homie/raspotify/raspotify.conf ~/.config/raspotify/conf
cp ~/projects/homie/systemd/raspotify-homie.service ~/.config/systemd/user/raspotify-homie.service
sudo systemctl disable --now raspotify.service
systemctl --user daemon-reload
systemctl --user enable --now raspotify-homie.serviceRaspotify uses Spotify Connect discovery, so there is no separate local build step. Once the service is running, the device should appear as Homie in Spotify clients on the same network.
Useful Raspotify commands:
systemctl --user status raspotify-homie.service
journalctl --user -u raspotify-homie.service -f
tail -f ~/projects/homie/raspotify.log
systemctl --user restart raspotify-homie.serviceTo shut down the Raspberry Pi cleanly:
sudo shutdown -h nowEquivalent command:
sudo poweroffSchedule shutdown in 5 minutes:
sudo shutdown -h +5Cancel a scheduled shutdown:
sudo shutdown -cLocal-only files are ignored by .gitignore, including:
.env.spotify_cache.venv/models/- recordings and Python cache files
If secret files were already committed, remove them from git tracking before pushing:
git rm --cached .env .spotify_cache
git add .gitignore
git commit --amend --no-edit
git push --force-with-lease origin main- Cloud-only mode avoids loading local Whisper, Piper, and llama.cpp models unless needed.
- Wake word detection remains local even when other stages use cloud services.
- The local audio stack and available sinks depend on the Pi's current PipeWire / PulseAudio state.
- If audio routing changes while Homie is running, the next playback re-checks the best sink automatically.