#!/bin/bash # setup-wizard.sh — First-boot configuration wizard for RPi Audio Mixer # # Runs on first boot (tty1) before getty starts. Guides the user through: # 1. Audio interface detection and selection # 2. WiFi configuration # 3. Hostname setting # 4. API key generation # 5. JACK device configuration # # After completion, the wizard disables itself, enables the mixer services, # and reboots into normal operation. set -euo pipefail # ─── Colours ───────────────────────────────────────────────────────────────── RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' BOLD='\033[1m' NC='\033[0m' # ─── Configuration paths ──────────────────────────────────────────────────── JACKD_SERVICE="/etc/systemd/system/jackd.service" MIXER_API_SERVICE="/etc/systemd/system/mixer-api.service" MIXER_UI_SERVICE="/etc/systemd/system/mixer-ui.service" CONFIG_TXT="/boot/config.txt" CMDLINE_TXT="/boot/cmdline.txt" WPA_CONF="/etc/wpa_supplicant/wpa_supplicant.conf" HOSTNAME_FILE="/etc/hostname" HOSTS_FILE="/etc/hosts" # ─── Helper functions ──────────────────────────────────────────────────────── header() { clear echo -e "${BLUE}${BOLD}" echo "╔══════════════════════════════════════════╗" echo "║ RPi Audio Mixer — First-Boot Setup ║" echo "║ $1" echo "╚══════════════════════════════════════════╝" echo -e "${NC}" echo "" } prompt() { local var_name="$1" local default="$2" local question="$3" local value echo -en "${GREEN}${question}${NC} [${default}]: " read -r value value="${value:-$default}" eval "$var_name=\"$value\"" } prompt_yn() { local question="$1" local default="${2:-y}" local answer while true; do echo -en "${GREEN}${question}${NC} (y/n) [${default}]: " read -r answer answer="${answer:-$default}" case "$answer" in [Yy]*) return 0 ;; [Nn]*) return 1 ;; *) echo "Please answer y or n" ;; esac done } press_enter() { echo "" echo -n "Press Enter to continue..." read -r } # ─── 1. Welcome ────────────────────────────────────────────────────────────── header "Welcome" echo "This wizard will configure your Raspberry Pi Audio Mixer." echo "You'll need:" echo " - USB audio interface connected" echo " - (Optional) WiFi network name and password" echo " - (Optional) Touchscreen connected via HDMI+USB" echo "" echo "This only runs once. Settings can be changed later via:" echo " /opt/rpi-mixer/config/ — audio/jack/carla configs" echo " raspi-config — system settings" echo "" press_enter # ─── 2. Audio Interface Detection ──────────────────────────────────────────── header "Step 1/5: Audio Interface" echo "Detecting USB audio interfaces..." echo "" # List sound cards ALSA_CARDS="" if command -v aplay >/dev/null 2>&1; then ALSA_CARDS=$(aplay -l 2>/dev/null | grep -E '^card [0-9]' || true) fi if [[ -z "$ALSA_CARDS" ]]; then echo -e "${YELLOW}No sound cards detected!${NC}" echo "" echo "Make sure your USB audio interface is connected." echo "You can continue anyway and configure later." echo "" if prompt_yn "Continue without audio interface?" "y"; then echo "Skipping audio interface config." else echo "Please connect your audio interface and reboot." echo "The setup wizard will run again on next boot." exit 0 fi else echo "Found sound cards:" echo "$ALSA_CARDS" echo "" # Detect USB audio card USB_CARD="" if aplay -l 2>/dev/null | grep -qi "USB"; then USB_CARD=$(aplay -l 2>/dev/null | grep -i "USB" | head -1 | awk '{print $2}' | tr -d ':') echo -e "${GREEN}USB audio interface detected: card $USB_CARD${NC}" else echo -e "${YELLOW}No USB audio card found. Using first available card.${NC}" USB_CARD=$(echo "$ALSA_CARDS" | head -1 | awk '{print $2}' | tr -d ':') fi echo "" prompt AUDIO_DEV "hw:USB" "ALSA device name for JACK?" # Update JACK service with detected device sed -i "s/-d hw:USB/-d $AUDIO_DEV/" "$JACKD_SERVICE" 2>/dev/null || true # Also update jackdrc mkdir -p /etc/jack sed -i "s/-d hw:USB/-d $AUDIO_DEV/" /etc/jackdrc 2>/dev/null || true echo "/usr/bin/jackd -P 70 -t 2000 -d alsa -d $AUDIO_DEV -r 48000 -p 128 -n 3 -M -X seq" > /etc/jackdrc fi press_enter # ─── 3. WiFi Configuration ─────────────────────────────────────────────────── header "Step 2/5: Network" # Check if Ethernet is connected ETH_CONNECTED=false if ip link show eth0 2>/dev/null | grep -q "state UP"; then ETH_CONNECTED=true echo -e "${GREEN}Ethernet connected.${NC}" echo "" fi if prompt_yn "Configure WiFi?" "n"; then prompt WIFI_SSID "" "WiFi network name (SSID)" echo -n "WiFi password: " read -s WIFI_PASS echo "" # Remove WiFi disable from config.txt sed -i 's/^dtoverlay=disable-wifi/#dtoverlay=disable-wifi/' "$CONFIG_TXT" 2>/dev/null || true # Write wpa_supplicant config cat > "$WPA_CONF" </dev/null || cat "$HOSTNAME_FILE" 2>/dev/null || echo "pi-mixer") prompt NEW_HOSTNAME "$CURRENT_HOSTNAME" "System hostname" if [[ "$NEW_HOSTNAME" != "$CURRENT_HOSTNAME" ]]; then echo "$NEW_HOSTNAME" > "$HOSTNAME_FILE" sed -i "s/$CURRENT_HOSTNAME/$NEW_HOSTNAME/g" "$HOSTS_FILE" 2>/dev/null || true hostname "$NEW_HOSTNAME" 2>/dev/null || true echo -e "${GREEN}Hostname set to: $NEW_HOSTNAME${NC}" echo " Local access: http://${NEW_HOSTNAME}.local:8080" fi press_enter # ─── 5. API Key ────────────────────────────────────────────────────────────── header "Step 4/5: Security" CURRENT_API_KEY=$(grep "MIXER_API_KEY" "$MIXER_API_SERVICE" 2>/dev/null | grep -oP 'MIXER_API_KEY=\K[^"]+' | head -1 || echo "") if [[ -z "$CURRENT_API_KEY" ]] || [[ "$CURRENT_API_KEY" == "MIXER_API_KEY_PLACEHOLDER" ]] || [[ "$CURRENT_API_KEY" == "mixer-local" ]]; then NEW_API_KEY=$(head -c 24 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) else NEW_API_KEY="$CURRENT_API_KEY" fi echo "API key for the mixer web UI and REST API:" echo -e " ${BOLD}${NEW_API_KEY}${NC}" echo "" echo "You'll need this to access the web interface from another device." echo "Keep it safe. It can be changed in:" echo " $MIXER_API_SERVICE" echo "" if prompt_yn "Use a custom API key?" "n"; then prompt NEW_API_KEY "$NEW_API_KEY" "API key (no spaces)" fi # Update API key in service files sed -i "s/--api-key [^ ]*/--api-key $NEW_API_KEY/" "$MIXER_API_SERVICE" 2>/dev/null || true sed -i "s/--api-key [^ ]*/--api-key $NEW_API_KEY/" "$MIXER_UI_SERVICE" 2>/dev/null || true sed -i "s/MIXER_API_KEY=[^ ]*/MIXER_API_KEY=$NEW_API_KEY/" "$MIXER_UI_SERVICE" 2>/dev/null || true # Also update the env variable export line sed -i "/^ExecStart.*mixer-api/,/^$/s/MIXER_API_KEY_PLACEHOLDER/$NEW_API_KEY/" "$MIXER_API_SERVICE" 2>/dev/null || true echo -e "${GREEN}API key updated.${NC}" press_enter # ─── 6. JACK Configuration ─────────────────────────────────────────────────── header "Step 5/5: Audio Settings" echo "JACK audio server settings:" echo "" prompt SAMPLE_RATE "48000" "Sample rate (44100/48000/96000)" prompt BUFFER_SIZE "128" "Buffer size (64/128/256/512 — lower = less latency)" prompt PERIODS "3" "Periods per buffer (2 or 3)" # Update JACK service sed -i "s/-r [0-9]*/-r $SAMPLE_RATE/" "$JACKD_SERVICE" 2>/dev/null || true sed -i "s/-p [0-9]*/-p $BUFFER_SIZE/" "$JACKD_SERVICE" 2>/dev/null || true sed -i "s/-n [0-9]*/-n $PERIODS/" "$JACKD_SERVICE" 2>/dev/null || true # Calculate approximate latency LATENCY_MS=$(echo "scale=1; ($BUFFER_SIZE * $PERIODS) / ($SAMPLE_RATE / 1000)" | bc 2>/dev/null || echo "?") echo "" echo -e "${GREEN}JACK configured:${NC}" echo " Sample rate: $SAMPLE_RATE Hz" echo " Buffer size: $BUFFER_SIZE frames" echo " Periods: $PERIODS" echo " Latency: ~${LATENCY_MS}ms round-trip" echo "" echo "Lower latency requires more CPU. If you hear xruns (clicks/pops)," echo "increase buffer size or periods in: $JACKD_SERVICE" press_enter # ─── 7. Touchscreen Check ──────────────────────────────────────────────────── header "Touchscreen (optional)" echo "If a touchscreen is connected via HDMI+USB, the Kivy touch UI" echo "will start automatically on boot." echo "" if prompt_yn "Enable touchscreen UI on boot?" "y"; then systemctl enable mixer-ui.service 2>/dev/null || true echo -e "${GREEN}Touchscreen UI enabled.${NC}" # Check HDMI connection if tvservice -s 2>/dev/null | grep -q "HDMI"; then echo " HDMI display detected." else echo -e " ${YELLOW}HDMI display not detected. UI may not be visible.${NC}" fi else systemctl disable mixer-ui.service 2>/dev/null || true echo "Touchscreen UI disabled." echo "Start manually: sudo systemctl start mixer-ui" fi press_enter # ─── 8. Finalize ───────────────────────────────────────────────────────────── header "Setup Complete" echo "Configuration summary:" echo " Hostname: $(cat $HOSTNAME_FILE 2>/dev/null || echo unknown)" echo " Audio device: ${AUDIO_DEV:-hw:USB}" echo " JACK: ${SAMPLE_RATE:-48000}Hz / ${BUFFER_SIZE:-128}frames / ${PERIODS:-3}periods" echo " API key: ${NEW_API_KEY:-mixer-local}" echo " Web UI: http://$(cat $HOSTNAME_FILE 2>/dev/null || echo pi-mixer).local:8080" echo " SSH: ssh pi@$(cat $HOSTNAME_FILE 2>/dev/null || echo pi-mixer).local" echo "" # Enable services log "Enabling mixer services..." systemctl enable jackd.service 2>/dev/null || true systemctl enable mixer-api.service 2>/dev/null || true systemctl daemon-reload 2>/dev/null || true # Disable this wizard log "Disabling first-boot wizard..." systemctl disable mixer-firstboot.service 2>/dev/null || true # Write setup-complete marker date -u +%Y-%m-%dT%H:%M:%SZ > /etc/mixer-setup-complete echo -e "${GREEN}${BOLD}Rebooting in 5 seconds...${NC}" echo "" echo "After reboot, the mixer will start automatically." echo " Web UI: http://$(cat $HOSTNAME_FILE 2>/dev/null || echo pi-mixer).local:8080" echo " API Key: ${NEW_API_KEY:-mixer-local}" echo "" sleep 5 reboot