Voice Assistant & Smart Display¶
Overview¶
Self-hosted Echo Show replacement using a Samsung Galaxy Tab A8 as an always-on smart display with fully local voice assistant, Spotify control, and calling.
Architecture:
Tab A8 (Fully Kiosk Browser + HA Companion App)
|
| wake word detected on-device (microWakeWord)
| audio sent to the server after wake word
v
server (<YOUR_SERVER_IP>)
├── Home Assistant (host network, port 8123)
├── Wyoming Whisper (STT, port 10300)
├── Wyoming Piper (TTS, port 10200)
└── Wyoming openWakeWord (port 10400, for future ESP32 satellites)
The HA Companion App (2026.3+) handles wake word detection on-device using microWakeWord. No audio leaves the tablet until after the wake word is detected. The openWakeWord container is for future ESP32 voice satellites only.
Phase 1: Voice Pipeline Containers¶
Directory Setup¶
sudo mkdir -p /opt/docker/wyoming/{whisper-data,piper-data,openwakeword-data,openwakeword-custom}
sudo chown -R $USER:$USER /opt/docker/wyoming
Compose File¶
/opt/docker/wyoming/compose.yaml
services:
# Speech-to-Text (faster-whisper)
whisper:
container_name: whisper
image: rhasspy/wyoming-whisper:3.8.1
restart: unless-stopped
ports:
- "10300:10300"
volumes:
- ./whisper-data:/data
command:
- "--model"
- "small-int8" # ~1 s per command on NUC-class hardware; noticeably more accurate than tiny
- "--language"
- "en"
dns:
- <YOUR_DNS_SERVER_IP>
# Text-to-Speech (piper)
piper:
container_name: piper
image: rhasspy/wyoming-piper:2.5.2
restart: unless-stopped
ports:
- "10200:10200"
volumes:
- ./piper-data:/data
command:
- "--voice"
- "en_GB-alan-medium" # more voices: https://rhasspy.github.io/piper-samples/
dns:
- <YOUR_DNS_SERVER_IP>
# Wake word detection (for ESP32 satellites; the tablet detects its wake word on-device)
openwakeword:
container_name: openwakeword
image: rhasspy/wyoming-openwakeword:2.1.0
restart: unless-stopped
ports:
- "10400:10400"
volumes:
- ./openwakeword-data:/data
- ./openwakeword-custom:/custom
command:
- "--preload-model"
- "hey_jarvis"
- "--custom-model-dir"
- "/custom"
dns:
- <YOUR_DNS_SERVER_IP>
# The Wyoming port answering is the useful signal; the image has bash but no curl/nc.
healthcheck:
test: ["CMD", "bash", "-c", "</dev/tcp/127.0.0.1/10400"]
interval: 60s
timeout: 5s
retries: 3
start_period: 60s
Pinned images
All three images are pinned to exact versions (Renovate proposes updates). Home Assistant's voice pipeline depends on them, so an unannounced Wyoming protocol or model change breaks "Hey Jarvis" in a way that's annoying to trace.
Wyoming has no authentication
Anything that can reach ports 10200/10300/10400 can use the services. Only expose them to the Home Assistant host (host firewall or VLAN rules), or bind them to the address HA uses.
Deploy¶
Models download on first boot (1-3 minutes). Wait for Ready in each container's logs.
Model Notes¶
- Whisper
small-int8: ~1 second processing on a NUC-class Intel CPU, ~500MB RAM. Drop totiny-int8if too slow, bump tomedium-int8if accuracy is poor. - Piper
en_GB-alan-medium: the voice I use;en_US-lessac-mediumis a good general US English voice. Preview others at rhasspy.github.io/piper-samples. - openWakeWord: Built-in words include
ok_nabu,hey_jarvis,hey_mycroft,alexa. Repeat--preload-modelto preload several. Only used by ESP32 satellites, not the tablet.
To change a model, edit the command in compose.yaml and run docker compose up -d. The new model downloads automatically.
Phase 2: Connect to Home Assistant¶
Add Wyoming Integrations¶
Add three separate Wyoming integrations, one for each container. Use the server's LAN/VLAN IP (not localhost).
- Settings > Devices & Services > + Add Integration > Wyoming Protocol
- Host:
<YOUR_SERVER_IP>, Port:10300(detected as Whisper STT) - Repeat: Host:
<YOUR_SERVER_IP>, Port:10200(detected as Piper TTS) - Repeat: Host:
<YOUR_SERVER_IP>, Port:10400(detected as openWakeWord)
Use the actual IP, not localhost
HA runs with network_mode: host but using 127.0.0.1 can fail depending on Docker's loopback handling. Always use the VLAN IP.
Create Assist Pipeline¶
- Settings > Voice Assistants > + Add Assistant
- Name:
Home(any name) - Speech-to-text: faster-whisper
- Text-to-speech: piper
- Wake word: hey_jarvis (or ok_nabu)
- Conversation agent: Home Assistant (default)
Expose Entities¶
Assist can only control entities explicitly exposed to it.
- Settings > Voice Assistants > Expose tab
- Toggle on entities you want voice-controllable
- Set clear aliases for anything with cryptic names
- Assign entities to Areas (Settings > Areas & Zones) for room-based commands like "turn off the lights in the kitchen"
Phase 3: Tablet Setup¶
Factory Reset & Debloat¶
Factory reset the Tab A8 first. After initial Google sign-in, debloat via ADB before installing anything else.
From any machine with ADB (e.g., your desktop):
Enable Developer Options on tablet (Settings > About tablet > tap Build number 7 times), then enable USB debugging. Connect via USB.
Remove Samsung bloatware:
adb shell pm uninstall -k --user 0 com.samsung.android.app.spage
adb shell pm uninstall -k --user 0 com.samsung.android.bixby.agent
adb shell pm uninstall -k --user 0 com.samsung.android.bixby.service
adb shell pm uninstall -k --user 0 com.samsung.android.visionintelligence
adb shell pm uninstall -k --user 0 com.samsung.android.game.gamehome
adb shell pm uninstall -k --user 0 com.samsung.android.game.gametools
adb shell pm uninstall -k --user 0 com.samsung.android.app.tips
adb shell pm uninstall -k --user 0 com.samsung.android.mobileservice
adb shell pm uninstall -k --user 0 com.samsung.android.app.watchmanagerstub
adb shell pm uninstall -k --user 0 com.samsung.android.ardrawing
adb shell pm uninstall -k --user 0 com.samsung.android.aremoji
adb shell pm uninstall -k --user 0 com.samsung.android.arzone
adb shell pm uninstall -k --user 0 com.samsung.android.app.dressroom
adb shell pm uninstall -k --user 0 com.samsung.android.spay
adb shell pm uninstall -k --user 0 com.samsung.android.spayfw
adb shell pm uninstall -k --user 0 com.samsung.android.app.sbrowseredge
adb shell pm uninstall -k --user 0 com.samsung.android.samsungpass
adb shell pm uninstall -k --user 0 com.samsung.android.samsungpassautofill
adb shell pm uninstall -k --user 0 com.sec.android.app.samsungapps
adb shell pm uninstall -k --user 0 com.samsung.android.themestore
adb shell pm uninstall -k --user 0 com.samsung.android.app.reminder
adb shell pm uninstall -k --user 0 com.samsung.android.calendar
adb shell pm uninstall -k --user 0 com.samsung.android.email.provider
adb shell pm uninstall -k --user 0 com.samsung.android.app.notes
adb shell pm uninstall -k --user 0 com.microsoft.office.officehubrow
adb shell pm uninstall -k --user 0 com.microsoft.skydrive
adb shell pm uninstall -k --user 0 com.microsoft.appmanager
adb shell pm uninstall -k --user 0 com.linkedin.android
adb shell pm uninstall -k --user 0 com.facebook.katana
adb shell pm uninstall -k --user 0 com.facebook.appmanager
adb shell pm uninstall -k --user 0 com.facebook.system
adb shell pm uninstall -k --user 0 com.netflix.mediaclient
adb shell pm uninstall -k --user 0 com.samsung.android.forest
adb shell pm uninstall -k --user 0 com.samsung.android.app.social
To find anything else: adb shell pm list packages -f | grep samsung
To restore if needed: adb shell cmd package install-existing <package.name>
Do NOT remove
com.samsung.android.incallui, com.samsung.android.telecom, com.samsung.android.providers.contacts, com.google.android.gms, com.google.android.gsf, com.android.systemui
Install HA Companion App¶
- Install from Play Store
- Open, enter HA URL:
http://homeassistant.<YOUR_LOCAL_DOMAIN>:8123(or direct IP) - Grant all permissions (microphone, camera, location, notifications)
- Configure wake word: Settings > Companion app > Assist for Android
- Tap Set as default, set Home Assistant as default digital assistant
- Enable Wake word detection, select "Hey Jarvis"
- Select the Assist pipeline you created
Samsung battery optimization will kill wake word detection
- Settings > Apps > Home Assistant > Battery > Unrestricted
- Settings > Device care > Battery > Background usage limits > add HA to Never sleeping
- Consider disabling Adaptive battery entirely since this tablet is always plugged in
Install Fully Kiosk Browser¶
- Install from Play Store (~$9 for Plus license to remove watermark)
- Open, enter start URL:
http://homeassistant.<YOUR_LOCAL_DOMAIN>:8123/tablet/0 - Swipe from left edge to open settings menu
- Configure:
- Web Content Settings > Autoplay Videos: ON
- Web Content Settings > Enable Microphone Access: OFF (critical -- prevents mic conflict with Companion App)
- Device Management > Keep Screen On: ON
- Motion Detection > Enable: ON (use camera, not microphone)
- Motion Detection > Turn Screen On on Motion: ON
- Remote Administration > Enable: ON, set a password
- Kiosk Mode > Enable Kiosk Mode: ON
- Kiosk Mode > Kiosk Exit Gesture: set a gesture you'll remember
- Kiosk Mode > App Whitelist: add one per line:
- Kiosk Mode > Applications to Run on Start in Background:
Microphone conflict
Fully Kiosk and the Companion App cannot use the mic simultaneously. Keep Enable Microphone Access OFF in Fully Kiosk and use camera-based motion detection, not microphone-based. This lets the Companion App hold the mic for wake word detection.
Add Fully Kiosk Integration to HA¶
- Settings > Devices & Services > + Add Integration > Fully Kiosk Browser
- Enter the tablet's IP and Remote Admin password
- Uncheck "Verify SSL"
Phase 4: Dashboard¶
Create a dedicated tablet dashboard: Settings > Dashboards > + Add Dashboard, name "Tablet", URL tablet.
Greeting Card¶
type: markdown
content: >
{% set hour = now().hour %}
{% if hour < 12 %}
<h1 style="font-size: 2.5em; font-weight: 300; margin: 0;">Good morning</h1>
{% elif hour < 17 %}
<h1 style="font-size: 2.5em; font-weight: 300; margin: 0;">Good afternoon</h1>
{% else %}
<h1 style="font-size: 2.5em; font-weight: 300; margin: 0;">Good evening</h1>
{% endif %}
<h3 style="font-size: 1.2em; font-weight: 300; opacity: 0.6; margin: 0;">{{ now().strftime('%A, %B %-d') }}</h3>
Note
HA's markdown renderer strips inline styles from <p> tags but allows them on headings. Use <h1>, <h3>, etc.
Weather¶
Add a Weather Forecast card. If no weather integration exists: Settings > Integrations > + Add > Met.no (no API key needed).
Timer¶
Create a timer helper: Settings > Devices & Services > Helpers > + Create Helper > Timer. Name it, set a default duration.
Add to dashboard:
Tap the card to start. Voice command "Hey Jarvis, set a timer for 5 minutes" works natively via Assist (separate from this entity).
Media / Spotify¶
See Phase 5. Once configured, add:
Call Buttons¶
See Phase 6. Add Button cards for each contact.
Phase 5: Spotify¶
Official Integration¶
- Go to developer.spotify.com/dashboard, log in with Spotify Premium
- Create App:
- Redirect URI:
https://my.home-assistant.io/redirect/oauth(only if you disabled My Home Assistant:http://homeassistant.<YOUR_LOCAL_DOMAIN>:8123/auth/external/callback) - Select "Web API"
- Redirect URI:
- Copy Client ID and Client Secret
- In HA: Settings > Integrations > + Add > Spotify
- Enter credentials, authenticate
Redirect URI must exactly match your HA URL
IP vs hostname mismatch causes auth failures.
Spotcast (for voice-triggered playback)¶
The official integration cannot start playback on idle devices. Spotcast works around this.
- Install HACS if not already installed (hacs.xyz)
- HACS > Integrations > search "Spotcast" > install
- Get Spotify cookies:
- Open incognito window, go to open.spotify.com, log in
- DevTools (F12) > Application > Cookies > open.spotify.com
- Copy
sp_dcandsp_keyvalues - Close window WITHOUT logging out
- Add to
configuration.yaml:
- Restart HA
Note
Spotify cookies expire periodically. Re-extract when playback stops working.
Tablet as Spotify Connect Device¶
Install the Spotify app on the Tab A8 and log in. The tablet appears as a Spotify Connect target for playback through its speakers.
Phase 6: Calling¶
Apps on Tablet¶
- Google Voice: PSTN calls (real phone numbers). Set up at voice.google.com. In app settings, set calls to "Prefer Wi-Fi and mobile data".
- Facebook Messenger: Video calls to contacts on Messenger.
Both apps are whitelisted in Fully Kiosk (done in Phase 3). Incoming calls overlay the kiosk, and Fully Kiosk returns to the dashboard after the call ends.
HA Scripts¶
Create scripts in Settings > Automations & Scenes > Scripts > + Add Script.
Phone calls go through the Companion App's command_activity notification command rather than Fully Kiosk. It
fires Android's ACTION_CALL intent, which dials the number straight away in the default dialer (Google Voice) instead
of just opening the dialer with the number filled in. Messenger and the dialer app itself are opened with Fully
Kiosk's start_application.
Call Mom (Google Voice):
alias: "Call Mom"
sequence:
- action: notify.mobile_app_<TABLET>
data:
message: "command_activity"
data:
intent_action: "android.intent.action.CALL"
intent_uri: "tel:+15551234567"
Call Dad (Google Voice): the same with tel:+15559876543.
Call Grandpa (Messenger):
alias: "Call Grandpa"
sequence:
- action: fully_kiosk.start_application
target:
device_id: YOUR_TABLET_DEVICE_ID
data:
application: "com.facebook.orca"
notify.mobile_app_<TABLET> is the tablet's Companion App notify service (Developer Tools > Actions, search
notify.mobile_app). Find YOUR_TABLET_DEVICE_ID by creating the script via the visual editor and selecting the
tablet as the target device.
Two one-time permissions
The first command_activity opens a settings screen asking you to let Home Assistant display over other apps,
and ACTION_CALL additionally needs the app's Phone permission. Grant both on the tablet, or the command
silently does nothing.
Dashboard Buttons¶
type: button
name: "Call Mom"
icon: mdi:phone
icon_height: 80px
tap_action:
action: perform-action
perform_action: script.call_mom
show_state: false
type: button
name: "Call Grandpa"
icon: mdi:video
icon_height: 80px
tap_action:
action: perform-action
perform_action: script.call_grandpa
show_state: false
Use mdi:phone for voice calls, mdi:video for Messenger video calls.
Voice-Triggered Calling¶
Create config/custom_sentences/en/call_contacts.yaml:
language: "en"
intents:
CallMom:
data:
- sentences:
- "call mom"
- "call mommy"
- "phone mom"
CallDad:
data:
- sentences:
- "call dad"
- "call daddy"
- "phone dad"
CallGrandpa:
data:
- sentences:
- "call grandpa"
- "phone grandpa"
VideoCallGrandpa:
data:
- sentences:
- "video call grandpa"
OpenPhone:
data:
- sentences:
- "open the phone"
- "open dialer"
- "open google voice"
Add to configuration.yaml:
intent_script:
CallMom:
speech:
text: "Calling mom"
action:
- action: notify.mobile_app_<TABLET>
data:
message: "command_activity"
data:
intent_action: "android.intent.action.CALL"
intent_uri: "tel:+15551234567"
CallDad:
speech:
text: "Calling dad"
action:
- action: notify.mobile_app_<TABLET>
data:
message: "command_activity"
data:
intent_action: "android.intent.action.CALL"
intent_uri: "tel:+15559876543"
VideoCallGrandpa:
speech:
text: "Opening messenger for grandpa"
action:
- action: fully_kiosk.start_application
target:
device_id: YOUR_TABLET_DEVICE_ID
data:
application: "com.facebook.orca"
OpenPhone:
speech:
text: "Opening the phone"
action:
- action: fully_kiosk.start_application
target:
device_id: YOUR_TABLET_DEVICE_ID
data:
application: "com.google.android.apps.googlevoice"
# CallGrandpa: same pattern as CallMom with grandpa's number
Restart HA. "Hey Jarvis, call mom" now works.
Do not rely on this tablet for 911
Google Voice E911 is unreliable. Keep a cell phone in the house -- even a deactivated phone with no service can dial 911.
HACS Recommendations¶
- card-mod: Custom CSS styling on any card (remove backgrounds, etc.)
- Mushroom Cards: Clean, modern, touch-friendly cards
- Mini Media Player: Better Spotify/media card than default
- Browser Mod: HA control of the tablet's browser (pop-up camera feeds, navigate views)
- Spotcast: Voice-triggered Spotify playback (covered in Phase 5)
Future: ESP32 Voice Satellites¶
Adding an M5Stack ATOM Echo (~$13) or ESP32-S3-BOX as a dedicated voice satellite:
- Satellite streams audio to HA
- openWakeWord on the server handles wake word detection
- Whisper transcribes, Piper responds
- Audio plays through the satellite speaker
The Wyoming infrastructure deployed here supports this with no changes.
Smart Switches¶
For integrating existing lights with HA/voice, replace dumb switches with Zigbee smart switches.
Recommended: Inovelli Blue Series (VZM31-SN)
- Zigbee 3.0, full Zigbee2MQTT support
- Works with and without neutral wire (important for older homes)
- Dimmer + on/off in one device
- Multi-tap scene control for HA automations
- LED notification bar
- 3-way compatible
- ~$40/switch
Budget alternative: Sonoff ZBMINIL2 (~$10) -- no-neutral relay that fits behind existing switches. Keeps your current switch appearance.
Check for neutral wire before buying
Turn off breaker, pull a switch out, look for a bundle of white wires nutted together in the back of the box not connected to the switch. If present, you have neutral. Houses built before ~1985 often lack neutral in switch boxes.