#!/usr/bin/env bash # homeai-voice/setup.sh — P3: Voice pipeline (STT / TTS / Wyoming) # # Components: # - Whisper.cpp — speech-to-text (Apple Silicon / CUDA optimised) # - wyoming-faster-whisper — Wyoming STT adapter (port 10300) # - Kokoro TTS — fast text-to-speech via ONNX # - wyoming-kokoro — Wyoming TTS adapter (port 10301) # - wyoming-satellite — Home Assistant voice satellite (port 10700) # - openWakeWord — always-on wake word detection # # Prerequisites: # - P1 (homeai-infra) completed — Home Assistant running # - P2 (homeai-llm) completed — Ollama running # - Python 3.10+ installed # - macOS: Xcode Command Line Tools, SoX audio toolkit set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" source "${REPO_DIR}/scripts/common.sh" log_section "P3: Voice Pipeline" detect_platform # ─── Prerequisite check ──────────────────────────────────────────────────────── log_info "Checking prerequisites..." prereq_ok=true if ! curl -sf http://localhost:8123 -o /dev/null 2>/dev/null; then log_warn "Home Assistant (P1) not reachable at :8123" prereq_ok=false fi if ! command_exists python3; then log_warn "python3 not found — required for STT/TTS adapters" prereq_ok=false fi if ! command_exists brew && [[ "$PLATFORM" == "macos" ]]; then log_warn "Homebrew not found — required for SoX installation" prereq_ok=false fi if [[ "$prereq_ok" == "false" ]]; then log_warn "Some prerequisites not met. Continuing anyway..." fi # ─── Install SoX (audio toolkit) ─────────────────────────────────────────────── log_info "Installing SoX audio toolkit..." if [[ "$PLATFORM" == "macos" ]]; then if ! command_exists rec || ! command_exists play; then brew install sox log_ok "SoX installed" else log_ok "SoX already installed" fi else log_warn "Please install SoX manually on Linux: apt-get install sox libsox-fmt-all" fi # ─── Create Python virtual environment ───────────────────────────────────────── VENV_DIR="$HOME/homeai-voice-env" log_info "Setting up Python environment at $VENV_DIR..." if [[ ! -d "$VENV_DIR" ]]; then python3 -m venv "$VENV_DIR" log_ok "Created virtual environment" fi source "$VENV_DIR/bin/activate" # ─── Install Python packages ─────────────────────────────────────────────────── log_info "Installing Python packages..." pip install --upgrade pip # Core Wyoming packages pip install wyoming>=1.8.0 pip install wyoming-faster-whisper # Wyoming satellite (note: has strict dependency on wyoming 1.4.1 but works with 1.8+) pip install wyoming-satellite # TTS pip install kokoro-onnx # Wake word pip install openwakeword pyaudio log_ok "Python packages installed" # ─── Create model directories ────────────────────────────────────────────────── log_info "Creating model directories..." mkdir -p "$HOME/models/whisper" mkdir -p "$HOME/models/kokoro" mkdir -p "$HOME/models/openwakeword" # ─── Download models ─────────────────────────────────────────────────────────── log_info "Downloading models..." # Kokoro TTS model if [[ ! -f "$HOME/models/kokoro/kokoro-v1.0.onnx" ]]; then log_info "Downloading Kokoro TTS model..." curl -L -o "$HOME/models/kokoro/kokoro-v1.0.onnx" \ "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v1.0.onnx" || \ log_warn "Failed to download Kokoro model" fi if [[ ! -f "$HOME/models/kokoro/voices-v1.0.bin" ]]; then curl -L -o "$HOME/models/kokoro/voices-v1.0.bin" \ "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices-v1.0.bin" || \ log_warn "Failed to download Kokoro voices" fi log_ok "Models downloaded (or already present)" # ─── Install launchd services ────────────────────────────────────────────────── log_info "Installing launchd services..." LAUNCHD_DIR="$SCRIPT_DIR/scripts/launchd" LAUNCH_AGENTS="$HOME/Library/LaunchAgents" mkdir -p "$LAUNCH_AGENTS" # Fix wyoming-satellite entry point if needed SATELLITE_BIN="$VENV_DIR/bin/wyoming-satellite" if [[ -f "$SATELLITE_BIN" ]] && grep -q "__main__.run()" "$SATELLITE_BIN"; then log_info "Fixing wyoming-satellite entry point..." # Cross-platform sed (macOS vs Linux) if [[ "$PLATFORM" == "macos" ]]; then sed -i '' 's/sys.exit(__main__.run())/import asyncio; asyncio.run(__main__.main())/' "$SATELLITE_BIN" else sed -i 's/sys.exit(__main__.run())/import asyncio; asyncio.run(__main__.main())/' "$SATELLITE_BIN" fi fi # Copy plists for plist in com.homeai.wyoming-stt.plist com.homeai.wyoming-tts.plist \ com.homeai.wakeword.plist com.homeai.wyoming-satellite.plist; do src="$LAUNCHD_DIR/$plist" dst="$LAUNCH_AGENTS/$plist" if [[ -f "$src" ]]; then cp "$src" "$dst" log_ok "Installed: $plist" else log_warn "Missing: $plist" fi done # ─── Summary ─────────────────────────────────────────────────────────────────── cat <