#!/bin/bash # Enable Voice Mode - Start voice server and configure voice-mode set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" HOMELAB_ROOT="$(dirname "$SCRIPT_DIR")" VOICE_SERVER_DIR="$HOMELAB_ROOT/voice-server" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } check_prerequisites() { log_info "Checking prerequisites..." # Check if poetry is installed if ! command -v poetry &> /dev/null; then log_error "Poetry is not installed. Please install it first:" log_error "curl -sSL https://install.python-poetry.org | python3 -" exit 1 fi # Check if piper-tts is installed if ! command -v piper-tts &> /dev/null; then log_error "piper-tts is not installed. Please install it first:" log_error "yay -S piper-tts" exit 1 fi # Check if voice server directory exists if [[ ! -d "$VOICE_SERVER_DIR" ]]; then log_error "Voice server directory not found: $VOICE_SERVER_DIR" exit 1 fi # Check if voice models exist VOICES_DIR="$HOME/.local/share/piper-voices" RYAN_MODEL="$VOICES_DIR/en_US-ryan-medium.onnx" if [[ ! -f "$RYAN_MODEL" ]]; then log_warn "Ryan voice model not found: $RYAN_MODEL" log_info "Downloading voice models..." mkdir -p "$VOICES_DIR" cd "$VOICES_DIR" log_info "Downloading Ryan voice (male US English)..." wget -q --show-progress \ "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ryan/medium/en_US-ryan-medium.onnx" \ "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ryan/medium/en_US-ryan-medium.onnx.json" log_info "Voice models downloaded successfully" fi log_info "Prerequisites check completed" } setup_voice_server() { log_info "Setting up voice server..." cd "$VOICE_SERVER_DIR" # Install dependencies if needed if [[ ! -d ".venv" ]] || ! poetry check &> /dev/null; then log_info "Installing voice server dependencies..." poetry install --only=main fi log_info "Voice server setup completed" } start_voice_server() { log_info "Starting voice server..." cd "$VOICE_SERVER_DIR" # Check if server is already running if curl -s http://127.0.0.1:8880/health > /dev/null 2>&1; then log_warn "Voice server is already running on port 8880" return 0 fi # Start server in background log_info "Starting voice server on http://127.0.0.1:8880" poetry run voice-server & SERVER_PID=$! # Save PID for cleanup echo $SERVER_PID > /tmp/voice-server.pid # Wait for server to start log_info "Waiting for server to start..." for i in {1..30}; do if curl -s http://127.0.0.1:8880/health > /dev/null 2>&1; then log_info "Voice server started successfully (PID: $SERVER_PID)" return 0 fi sleep 1 done log_error "Failed to start voice server" exit 1 } test_voice_server() { log_info "Testing voice server..." # Test health endpoint if ! curl -s http://127.0.0.1:8880/health | grep -q "healthy"; then log_error "Voice server health check failed" exit 1 fi # Test TTS endpoint if ! curl -s -X POST "http://127.0.0.1:8880/v1/audio/speech" \ -H "Content-Type: application/json" \ -d '{"input": "Voice server test", "voice": "ryan"}' \ --output /tmp/voice-test.wav > /dev/null 2>&1; then log_error "Voice server TTS test failed" exit 1 fi # Check if audio file was created if [[ ! -f "/tmp/voice-test.wav" ]] || [[ ! -s "/tmp/voice-test.wav" ]]; then log_error "Generated audio file is invalid" exit 1 fi rm -f /tmp/voice-test.wav log_info "Voice server test completed successfully" } show_usage() { log_info "Voice mode is now enabled!" echo echo "Server Details:" echo " URL: http://127.0.0.1:8880" echo " Health: http://127.0.0.1:8880/health" echo " API Docs: http://127.0.0.1:8880/docs" echo echo "Available commands:" echo " # Test TTS" echo " curl -X POST 'http://127.0.0.1:8880/v1/audio/speech' \\" echo " -H 'Content-Type: application/json' \\" echo " -d '{\"input\": \"Hello world!\", \"voice\": \"ryan\"}' \\" echo " --output test.wav" echo echo " # Stop server" echo " $SCRIPT_DIR/disable-voice.sh" echo echo " # View server logs" echo " tail -f $VOICE_SERVER_DIR/voice-server.log" echo log_info "You can now use voice commands in Claude Code!" } create_disable_script() { cat > "$SCRIPT_DIR/disable-voice.sh" << 'EOF' #!/bin/bash # Disable Voice Mode - Stop voice server RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } if [[ -f "/tmp/voice-server.pid" ]]; then PID=$(cat /tmp/voice-server.pid) if kill -0 "$PID" 2>/dev/null; then log_info "Stopping voice server (PID: $PID)..." kill "$PID" rm -f /tmp/voice-server.pid log_info "Voice server stopped" else log_error "Voice server process not found" rm -f /tmp/voice-server.pid fi else log_error "Voice server PID file not found" fi # Also try to kill any remaining voice-server processes pkill -f "voice-server" && log_info "Cleaned up remaining voice-server processes" log_info "Voice mode disabled" EOF chmod +x "$SCRIPT_DIR/disable-voice.sh" } main() { log_info "Enabling Voice Mode for Claude Code..." check_prerequisites setup_voice_server start_voice_server test_voice_server create_disable_script show_usage } # Handle cleanup on exit cleanup() { if [[ -n "${SERVER_PID:-}" ]]; then log_info "Cleaning up..." kill "$SERVER_PID" 2>/dev/null || true fi } trap cleanup EXIT main "$@"