I’ve been building privacy-first smart home setups for years, and one pattern keeps coming back: you can have tightly integrated Zigbee and Z-Wave devices without handing control or metadata to a cloud service — including voice control. In this guide I’ll walk you through how I build a home assistant hub that keeps Zigbee and Z-Wave radios offline (no cloud pairing, no vendor servers) and still gives you reliable, local voice automation that never leaves your LAN.

Why keep radios offline, and what I mean by “voice automation”

When I say “keep radios offline” I mean the Zigbee and Z-Wave controllers themselves have no route to the internet. They talk only to your local home automation instance. The reasons are practical and privacy-minded:

  • Device privacy: Many bulbs, locks and sensors leak metadata or require cloud APIs.
  • Resilience: Local automation keeps working if a vendor cloud goes down or if your internet connection fails.
  • Security: Limiting outbound connections reduces attack surface.
  • By “voice automation” I mean local voice control: microphone capture at the edge, local speech-to-text, intent processing, and local TTS (text-to-speech) for responses and announcements. No audio is uploaded unless you explicitly allow it.

    Overview of the architecture I use

    At a high level, my setup looks like this:

  • A dedicated host running Home Assistant Core (or Home Assistant Supervised on a controlled VM) or an alternative like Home Assistant OS on a local machine.
  • Local Zigbee and Z-Wave radios attached to that host (USB sticks or dedicated hubs) but isolated from the internet by firewall/VLAN rules.
  • Local MQTT broker (e.g., Mosquitto) for device messaging and integrations like Zigbee2MQTT.
  • Voice stack running locally: wake word engine + STT + intent processing (Rhasspy or Mycroft/Rasa combos). Audio input devices (Raspberry Pi + USB mic array or ReSpeaker) captured locally.
  • Automation engine: Home Assistant automations or Node-RED for flows triggered by detected intents.
  • Hardware and software I recommend

    Here are the parts I choose depending on the scale of the house and my need for headroom:

    • Host: Raspberry Pi 4 (4–8GB) for small installs; Intel NUC or small fanless PC for larger setups or running models locally.
    • Zigbee radio: ConBee II or a Sonoff Zigbee 3.0 USB dongle. Use Zigbee2MQTT or ZHA locally.
    • Z-Wave radio: Aeotec Z-Stick Gen5+ or Zooz S2 stick. Use OpenZWave or Z-Wave JS locally.
    • Mic/voice device: ReSpeaker Mic Array, USB microphone, or a small Raspberry Pi-based voice nodes with a hotword device like Snowboy replacement (Porcupine).
    • Local STT/TTS: VOSK for STT, Picovoice or Porcupine for wake word, and Coqui TTS or eSpeak NG/OpenTTS for offline voice responses.
    • Optional: NTP server, UPS for reliability.

    Step-by-step: setting up the local hub

    This is the workflow I follow; I include configuration choices that enforce offline-only behavior.

  • Install Home Assistant Core on a host: I prefer a VM or Docker install on an NUC for performance. Home Assistant OS is fine, but make sure you can manage network routing for attached USB devices and firewall rules. If you want a minimal footprint, Home Assistant Core in Docker gives more control.
  • Plug in Zigbee/Z-Wave radios locally: Attach USB radios directly to the host. For placement, I sometimes put the radio on a short USB extension cable to avoid interference and get better coverage.
  • Run a local MQTT broker: I install Mosquitto in Docker and configure it with passwords and TLS for local use. Zigbee2MQTT talks to Mosquitto, and Home Assistant subscribes to device topics for state and commands.
  • Run Zigbee2MQTT or Z-Wave JS locally: Configure these integrations to use the local MQTT broker (for Zigbee2MQTT) or the serial stick for Z-Wave JS. Important: disable any cloud integrations and remove any cloud account keys from these services.
  • Network isolation: This is crucial. I create a VLAN or use a firewall to block outbound internet access for the host’s Zigbee/Z-Wave USB devices. Concretely, that means firewall rules that prevent the host from making outbound TCP/UDP connections except to local IPs and essential update servers if you choose. Better: firewall rules can block traffic from the Docker container or VM that runs Zigbee2MQTT/Z-Wave JS to the internet.
  • Local voice stack: I deploy Rhasspy (open-source voice assistant) connected to Home Assistant via MQTT, running on the same host or a dedicated Raspberry Pi. Rhasspy handles wake word, STT (I use VOSK models), intent recognition and TTS. All models are stored locally.
  • Automations: Home Assistant listens to intent MQTT topics from Rhasspy and triggers scripts or Node-RED flows. Because everything is local, voice commands trigger instant device actions without cloud latency.
  • Block cloud fallbacks: Some voice stacks try cloud fallback. Make sure you disable them in configuration and close their outbound ports at the firewall level. Test with outbound blocked to ensure nothing leaks.
  • Optional ML locally: If you want higher STT accuracy you can run a local Whisper model on an x86 NUC with a GPU, but that’s heavier. VOSK strikes a good balance for many setups.
  • Sample local configuration details I use

    My minimal Rhasspy-to-Home Assistant flow:

  • Rhasspy (MQTT) -> intents/ -> Home Assistant automation listens on MQTT topic -> Home Assistant calls service to switch, speak, or run scene.
  • For TTS I use OpenTTS with Coqui models; Home Assistant calls the TTS service to play responses on local media players (e.g., a Raspberry Pi running Mopidy or Snapcast client).

    Security and privacy hardening

    These are rules I apply every time:

    • Least privilege: Give each service only the network access it needs. Keep radios only talking to local services.
    • Firewall/VLAN: Put the hub (or the containers) in a VLAN with a rule that denies outbound internet by default.
    • Disable cloud integrations: Don’t install vendor cloud add-ons. If you need device updates, do them manually in a controlled way.
    • Encrypted backups: Home Assistant backups saved to an encrypted external disk or to an encrypted cloud account if you accept that trade-off.
    • Monitoring: Use a simple IDS (like Pi-hole + logging or Home Assistant’s built-in logs) to detect unexpected outbound connections.

    Trade-offs and practical considerations

    Keeping everything local has trade-offs — I make them consciously:

  • Convenience vs. privacy: You give up some convenience (e.g., vendor mobile app cloud voice features, remote access without secure VPN) but gain privacy and reliability.
  • Updates: Local stacks need manual attention. I schedule monthly maintenance windows for updates and model retraining if needed.
  • Accuracy: Local STT can be slightly less accurate than cloud ASR for noisy environments. I mitigate this with good microphones, room tuning and custom wake-word thresholds.
  • Quick comparison: components

    Component Why I choose it
    ConBee II Works well with Zigbee2MQTT, stable local operation
    Aeotec Z-Stick Good Z-Wave support and local control with Z-Wave JS
    Rhasspy + VOSK Fully local voice stack, MQTT-friendly
    Coqui/OpenTTS Offline TTS with good-sounding voices

    If you want, I can export the exact Docker Compose, Home Assistant YAML snippets and firewall rules I use so you can copy-paste them into your setup. I also keep a checklist for on-site testing (range test, wake-word false positives, STT accuracy) that helps catch issues before you rely on voice for critical automations.