Ports prior voice assistant research and prototypes from devl/Devops into the Minerva repo. Includes: - docs/: architecture, wake word guides, ESP32-S3 spec, hardware buying guide - scripts/: voice_server.py, voice_server_enhanced.py, setup scripts - hardware/maixduino/: edge device scripts with WiFi credentials scrubbed (replaced hardcoded password with secrets.py pattern) - config/.env.example: server config template - .gitignore: excludes .env, secrets.py, model blobs, ELF firmware - CLAUDE.md: Minerva product context and connection to cf-voice roadmap
456 lines
13 KiB
Bash
Executable file
456 lines
13 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Path: quick_start_hey_mycroft.sh
|
|
#
|
|
# Purpose and usage:
|
|
# Zero-training quick start using pre-trained "Hey Mycroft" model
|
|
# Gets you a working voice assistant in 5 minutes!
|
|
#
|
|
# Requirements:
|
|
# - Heimdall already setup (ran setup_voice_assistant.sh)
|
|
# - Mycroft Precise installed (ran setup_precise.sh)
|
|
#
|
|
# Usage:
|
|
# ./quick_start_hey_mycroft.sh [--test-only]
|
|
#
|
|
# Author: PRbL Library
|
|
|
|
# ----- PRbL Color and output functions -----
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
BLUE='\033[0;34m'
|
|
PURPLE='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
print_status() {
|
|
local level="$1"
|
|
shift
|
|
case "$level" in
|
|
"info") echo -e "${BLUE}[INFO]${NC} $*" >&2 ;;
|
|
"success") echo -e "${GREEN}[SUCCESS]${NC} $*" >&2 ;;
|
|
"warning") echo -e "${YELLOW}[WARNING]${NC} $*" >&2 ;;
|
|
"error") echo -e "${RED}[ERROR]${NC} $*" >&2 ;;
|
|
*) echo -e "$*" >&2 ;;
|
|
esac
|
|
}
|
|
|
|
# ----- Configuration -----
|
|
MODELS_DIR="$HOME/precise-models/pretrained"
|
|
MODEL_URL="https://github.com/MycroftAI/precise-data/raw/models-dev/hey-mycroft.tar.gz"
|
|
MODEL_NAME="hey-mycroft"
|
|
TEST_ONLY=false
|
|
|
|
# ----- Parse arguments -----
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--test-only)
|
|
TEST_ONLY=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
cat << EOF
|
|
Usage: $(basename "$0") [OPTIONS]
|
|
|
|
Quick start with pre-trained "Hey Mycroft" wake word model.
|
|
No training required!
|
|
|
|
Options:
|
|
--test-only Just test the model, don't start server
|
|
-h, --help Show this help
|
|
|
|
Examples:
|
|
$(basename "$0") # Download, test, and run server
|
|
$(basename "$0") --test-only # Just download and test
|
|
|
|
EOF
|
|
exit 0
|
|
;;
|
|
*)
|
|
print_status error "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ----- Functions -----
|
|
|
|
check_prerequisites() {
|
|
print_status info "Checking prerequisites..."
|
|
|
|
# Check conda
|
|
if ! command -v conda &> /dev/null; then
|
|
print_status error "conda not found"
|
|
return 1
|
|
fi
|
|
|
|
# Check precise environment
|
|
if ! conda env list | grep -q "^precise\s"; then
|
|
print_status error "Precise environment not found"
|
|
print_status info "Run: ./setup_precise.sh first"
|
|
return 1
|
|
fi
|
|
|
|
# Check voice-assistant directory
|
|
if [[ ! -d "$HOME/voice-assistant" ]]; then
|
|
print_status error "Voice assistant not setup"
|
|
print_status info "Run: ./setup_voice_assistant.sh first"
|
|
return 1
|
|
fi
|
|
|
|
print_status success "Prerequisites OK"
|
|
return 0
|
|
}
|
|
|
|
download_pretrained_model() {
|
|
print_status info "Downloading pre-trained 'Hey Mycroft' model..."
|
|
|
|
# Create directory
|
|
mkdir -p "$MODELS_DIR"
|
|
|
|
# Check if already downloaded
|
|
if [[ -f "$MODELS_DIR/${MODEL_NAME}.net" ]]; then
|
|
print_status info "Model already downloaded"
|
|
return 0
|
|
fi
|
|
|
|
# Download
|
|
cd "$MODELS_DIR" || return 1
|
|
|
|
print_status info "Fetching from GitHub..."
|
|
wget -q --show-progress "$MODEL_URL" || {
|
|
print_status error "Failed to download model"
|
|
return 1
|
|
}
|
|
|
|
# Extract
|
|
print_status info "Extracting model..."
|
|
tar xzf hey-mycroft.tar.gz || {
|
|
print_status error "Failed to extract model"
|
|
return 1
|
|
}
|
|
|
|
# Verify
|
|
if [[ ! -f "${MODEL_NAME}.net" ]]; then
|
|
print_status error "Model file not found after extraction"
|
|
return 1
|
|
fi
|
|
|
|
print_status success "Model downloaded: $MODELS_DIR/${MODEL_NAME}.net"
|
|
return 0
|
|
}
|
|
|
|
test_model() {
|
|
print_status info "Testing wake word model..."
|
|
|
|
cd "$MODELS_DIR" || return 1
|
|
|
|
# Activate conda
|
|
eval "$(conda shell.bash hook)"
|
|
conda activate precise || {
|
|
print_status error "Failed to activate precise environment"
|
|
return 1
|
|
}
|
|
|
|
cat << EOF
|
|
|
|
${CYAN}═══════════════════════════════════════════════════${NC}
|
|
${CYAN} Wake Word Test: "Hey Mycroft"${NC}
|
|
${CYAN}═══════════════════════════════════════════════════${NC}
|
|
|
|
${YELLOW}Instructions:${NC}
|
|
1. Speak "Hey Mycroft" into your microphone
|
|
2. You should see ${GREEN}"!"${NC} when detected
|
|
3. Try other phrases - should ${RED}not${NC} trigger
|
|
4. Press ${RED}Ctrl+C${NC} when done testing
|
|
|
|
${CYAN}Starting in 3 seconds...${NC}
|
|
|
|
EOF
|
|
|
|
sleep 3
|
|
|
|
# Test the model
|
|
precise-listen "${MODEL_NAME}.net" || {
|
|
print_status error "Model test failed"
|
|
return 1
|
|
}
|
|
|
|
print_status success "Model test complete!"
|
|
return 0
|
|
}
|
|
|
|
update_config() {
|
|
print_status info "Updating voice assistant configuration..."
|
|
|
|
local config_file="$HOME/voice-assistant/config/.env"
|
|
|
|
if [[ ! -f "$config_file" ]]; then
|
|
print_status error "Config file not found: $config_file"
|
|
return 1
|
|
fi
|
|
|
|
# Update PRECISE_MODEL if exists, otherwise add it
|
|
if grep -q "^PRECISE_MODEL=" "$config_file"; then
|
|
sed -i "s|^PRECISE_MODEL=.*|PRECISE_MODEL=$MODELS_DIR/${MODEL_NAME}.net|" "$config_file"
|
|
else
|
|
echo "PRECISE_MODEL=$MODELS_DIR/${MODEL_NAME}.net" >> "$config_file"
|
|
fi
|
|
|
|
# Update sensitivity if not set
|
|
if ! grep -q "^PRECISE_SENSITIVITY=" "$config_file"; then
|
|
echo "PRECISE_SENSITIVITY=0.5" >> "$config_file"
|
|
fi
|
|
|
|
print_status success "Configuration updated"
|
|
return 0
|
|
}
|
|
|
|
start_server() {
|
|
print_status info "Starting voice assistant server..."
|
|
|
|
cd "$HOME/voice-assistant" || return 1
|
|
|
|
# Activate conda
|
|
eval "$(conda shell.bash hook)"
|
|
conda activate precise || {
|
|
print_status error "Failed to activate environment"
|
|
return 1
|
|
}
|
|
|
|
cat << EOF
|
|
|
|
${CYAN}═══════════════════════════════════════════════════${NC}
|
|
${GREEN} Starting Voice Assistant Server${NC}
|
|
${CYAN}═══════════════════════════════════════════════════${NC}
|
|
|
|
${YELLOW}Configuration:${NC}
|
|
Wake word: ${GREEN}Hey Mycroft${NC}
|
|
Model: ${MODEL_NAME}.net
|
|
Server: http://0.0.0.0:5000
|
|
|
|
${YELLOW}What to do next:${NC}
|
|
1. Wait for "Precise listening started" message
|
|
2. Say ${GREEN}"Hey Mycroft"${NC} to test wake word
|
|
3. Say a command like ${GREEN}"turn on the lights"${NC}
|
|
4. Check server logs for activity
|
|
|
|
${YELLOW}Press Ctrl+C to stop the server${NC}
|
|
|
|
${CYAN}Starting server...${NC}
|
|
|
|
EOF
|
|
|
|
# Check if HA token is set
|
|
if ! grep -q "^HA_TOKEN=..*" config/.env; then
|
|
print_status warning "Home Assistant token not set!"
|
|
print_status warning "Commands won't execute without it."
|
|
print_status info "Edit config/.env and add your HA token"
|
|
echo
|
|
read -p "Continue anyway? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Start server
|
|
python voice_server.py \
|
|
--enable-precise \
|
|
--precise-model "$MODELS_DIR/${MODEL_NAME}.net" \
|
|
--precise-sensitivity 0.5
|
|
|
|
return $?
|
|
}
|
|
|
|
create_systemd_service() {
|
|
print_status info "Creating systemd service..."
|
|
|
|
local service_file="/etc/systemd/system/voice-assistant.service"
|
|
|
|
# Check if we should update existing service
|
|
if [[ -f "$service_file" ]]; then
|
|
print_status warning "Service file already exists"
|
|
read -p "Update with Hey Mycroft configuration? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Create service file
|
|
sudo tee "$service_file" > /dev/null << EOF
|
|
[Unit]
|
|
Description=Voice Assistant with Hey Mycroft Wake Word
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=$USER
|
|
WorkingDirectory=$HOME/voice-assistant
|
|
Environment="PATH=$HOME/miniconda3/envs/precise/bin:/usr/local/bin:/usr/bin:/bin"
|
|
EnvironmentFile=$HOME/voice-assistant/config/.env
|
|
ExecStart=$HOME/miniconda3/envs/precise/bin/python voice_server.py \\
|
|
--enable-precise \\
|
|
--precise-model $MODELS_DIR/${MODEL_NAME}.net \\
|
|
--precise-sensitivity 0.5
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
StandardOutput=append:$HOME/voice-assistant/logs/voice_assistant.log
|
|
StandardError=append:$HOME/voice-assistant/logs/voice_assistant_error.log
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# Reload systemd
|
|
sudo systemctl daemon-reload
|
|
|
|
print_status success "Systemd service created"
|
|
|
|
cat << EOF
|
|
|
|
${CYAN}To enable and start the service:${NC}
|
|
sudo systemctl enable voice-assistant
|
|
sudo systemctl start voice-assistant
|
|
sudo systemctl status voice-assistant
|
|
|
|
${CYAN}To view logs:${NC}
|
|
journalctl -u voice-assistant -f
|
|
|
|
EOF
|
|
|
|
read -p "Enable service now? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
sudo systemctl enable voice-assistant
|
|
sudo systemctl start voice-assistant
|
|
sleep 2
|
|
sudo systemctl status voice-assistant
|
|
fi
|
|
}
|
|
|
|
print_next_steps() {
|
|
cat << EOF
|
|
|
|
${GREEN}═══════════════════════════════════════════════════${NC}
|
|
${GREEN} Success! Your voice assistant is ready!${NC}
|
|
${GREEN}═══════════════════════════════════════════════════${NC}
|
|
|
|
${CYAN}What you have:${NC}
|
|
✓ Pre-trained "Hey Mycroft" wake word
|
|
✓ Voice assistant server configured
|
|
✓ Ready to control Home Assistant
|
|
|
|
${CYAN}Quick test:${NC}
|
|
1. Say: ${GREEN}"Hey Mycroft"${NC}
|
|
2. Say: ${GREEN}"Turn on the living room lights"${NC}
|
|
3. Check if command executed
|
|
|
|
${CYAN}Next steps:${NC}
|
|
1. ${YELLOW}Configure Home Assistant entities${NC}
|
|
Edit: ~/voice-assistant/config/.env
|
|
Add: HA_TOKEN=your_token_here
|
|
|
|
2. ${YELLOW}Add more entity mappings${NC}
|
|
Edit: voice_server.py
|
|
Update: IntentParser.ENTITY_MAP
|
|
|
|
3. ${YELLOW}Fine-tune for your voice (optional)${NC}
|
|
cd ~/precise-models/hey-mycroft-custom
|
|
./1-record-wake-word.sh
|
|
# Record 20-30 samples
|
|
precise-train -e 30 hey-mycroft-custom.net . \\
|
|
--from-checkpoint $MODELS_DIR/${MODEL_NAME}.net
|
|
|
|
4. ${YELLOW}Setup Maix Duino${NC}
|
|
See: QUICKSTART.md Phase 2
|
|
|
|
${CYAN}Useful commands:${NC}
|
|
# Test wake word only
|
|
cd $MODELS_DIR && conda activate precise
|
|
precise-listen ${MODEL_NAME}.net
|
|
|
|
# Check server health
|
|
curl http://localhost:5000/health
|
|
|
|
# Monitor logs
|
|
journalctl -u voice-assistant -f
|
|
|
|
${CYAN}Documentation:${NC}
|
|
README.md - Project overview
|
|
WAKE_WORD_ADVANCED.md - Multiple wake words guide
|
|
QUICKSTART.md - Complete setup guide
|
|
|
|
${GREEN}Happy voice assisting! 🎙️${NC}
|
|
|
|
EOF
|
|
}
|
|
|
|
# ----- Main -----
|
|
main() {
|
|
cat << EOF
|
|
${CYAN}═══════════════════════════════════════════════════${NC}
|
|
${CYAN} Quick Start: Hey Mycroft Wake Word${NC}
|
|
${CYAN}═══════════════════════════════════════════════════${NC}
|
|
|
|
${YELLOW}This script will:${NC}
|
|
1. Download pre-trained "Hey Mycroft" model
|
|
2. Test wake word detection
|
|
3. Configure voice assistant server
|
|
4. Start the server (optional)
|
|
|
|
${YELLOW}Total time: ~5 minutes (no training!)${NC}
|
|
|
|
EOF
|
|
|
|
# Parse arguments
|
|
parse_args "$@"
|
|
|
|
# Check prerequisites
|
|
check_prerequisites || exit 1
|
|
|
|
# Download model
|
|
download_pretrained_model || exit 1
|
|
|
|
# Test model
|
|
print_status info "Ready to test wake word"
|
|
read -p "Test now? (Y/n): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
test_model
|
|
fi
|
|
|
|
# If test-only mode, stop here
|
|
if [[ "$TEST_ONLY" == "true" ]]; then
|
|
print_status success "Test complete!"
|
|
print_status info "Model location: $MODELS_DIR/${MODEL_NAME}.net"
|
|
exit 0
|
|
fi
|
|
|
|
# Update configuration
|
|
update_config || exit 1
|
|
|
|
# Start server
|
|
read -p "Start voice assistant server now? (Y/n): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
start_server
|
|
else
|
|
# Offer to create systemd service
|
|
read -p "Create systemd service instead? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
create_systemd_service
|
|
fi
|
|
fi
|
|
|
|
# Print next steps
|
|
print_next_steps
|
|
}
|
|
|
|
# Run main
|
|
main "$@"
|