122 lines
4.9 KiB
Bash
122 lines
4.9 KiB
Bash
#!/usr/bin/env bash
|
|
# One-shot Raspberry Pi installer for javis-slave (headless or with touch display).
|
|
#
|
|
# sudo ./setup-pi.sh # headless satellite (mic + speaker)
|
|
# sudo ./setup-pi.sh --gui # Pi with official touch display
|
|
# sudo ./setup-pi.sh --with-vad # also install silero-vad (+PyTorch, ~200 MB)
|
|
#
|
|
# Idempotent: re-running upgrades the venv and rewrites the service unit.
|
|
set -euo pipefail
|
|
|
|
INSTALL_DIR="${INSTALL_DIR:-/opt/javis-slave}"
|
|
SERVICE_USER="${SERVICE_USER:-${SUDO_USER:-pi}}"
|
|
WITH_GUI=0
|
|
WITH_VAD=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--gui) WITH_GUI=1 ;;
|
|
--with-vad) WITH_VAD=1 ;;
|
|
-h|--help) sed -n '2,9p' "$0"; exit 0 ;;
|
|
*) echo "unknown option: $arg" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ $EUID -ne 0 ]]; then
|
|
echo "Run with sudo (installs apt packages and a systemd unit)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
SRC_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
echo "==> apt packages"
|
|
apt-get update -qq
|
|
# portaudio: sounddevice; libsndfile: soundfile; alsa-utils: amixer/aplay/arecord;
|
|
# libatomic1 + libopenblas: onnxruntime/numpy wheels on aarch64.
|
|
apt-get install -y -qq python3 python3-venv python3-dev libportaudio2 libsndfile1 \
|
|
alsa-utils libatomic1 libopenblas0 rsync
|
|
if [[ $WITH_GUI -eq 1 ]]; then
|
|
# Qt platform plugin deps for PySide6 wheels on Raspberry Pi OS + colour emoji
|
|
# glyphs used by the orb/buttons (🎤 🔊 ⚙).
|
|
apt-get install -y -qq libxcb-cursor0 libxkbcommon-x11-0 libegl1 libgl1 libglib2.0-0 \
|
|
libfontconfig1 libdbus-1-3 fonts-noto-color-emoji
|
|
fi
|
|
|
|
echo "==> copying to ${INSTALL_DIR}"
|
|
mkdir -p "$INSTALL_DIR"
|
|
if [[ "$SRC_DIR" != "$INSTALL_DIR" ]]; then
|
|
rsync -a --delete --exclude .venv --exclude config --exclude '__pycache__' \
|
|
"$SRC_DIR"/ "$INSTALL_DIR"/
|
|
fi
|
|
mkdir -p "$INSTALL_DIR/config"
|
|
chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR"
|
|
|
|
echo "==> python venv"
|
|
extras=""
|
|
[[ $WITH_GUI -eq 1 ]] && extras="gui"
|
|
[[ $WITH_VAD -eq 1 ]] && extras="${extras:+$extras,}vad"
|
|
sudo -u "$SERVICE_USER" bash -c "
|
|
set -e
|
|
cd '$INSTALL_DIR'
|
|
[[ -x .venv/bin/python ]] || python3 -m venv .venv
|
|
.venv/bin/python -m pip install --upgrade pip wheel
|
|
if [[ -n '$extras' ]]; then .venv/bin/python -m pip install -e '.[$extras]'; else .venv/bin/python -m pip install -e .; fi
|
|
# Pre-download openWakeWord ONNX models so the service works offline later.
|
|
.venv/bin/python -c 'from openwakeword.utils import download_models; download_models()' || true
|
|
"
|
|
|
|
echo "==> permissions (audio, backlight)"
|
|
usermod -aG audio,video "$SERVICE_USER"
|
|
cat > /etc/udev/rules.d/90-javis-backlight.rules <<'EOF'
|
|
# Let members of 'video' set the display brightness (javis-slave /mcp/v1/brightness).
|
|
SUBSYSTEM=="backlight", ACTION=="add", RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness", RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness"
|
|
EOF
|
|
udevadm control --reload-rules || true
|
|
# Passwordless power control for the service user (only the exact commands used).
|
|
cat > /etc/sudoers.d/javis-slave <<EOF
|
|
$SERVICE_USER ALL=(root) NOPASSWD: /usr/bin/systemctl suspend, /usr/sbin/shutdown
|
|
EOF
|
|
chmod 0440 /etc/sudoers.d/javis-slave
|
|
|
|
echo "==> systemd unit"
|
|
sed -e "s|^User=.*|User=$SERVICE_USER|" \
|
|
-e "s|/opt/javis-slave|$INSTALL_DIR|g" \
|
|
"$INSTALL_DIR/javis-slave.service" > /etc/systemd/system/javis-slave.service
|
|
systemctl daemon-reload
|
|
if [[ $WITH_GUI -eq 1 ]]; then
|
|
# With a display the touch UI runs in the user's desktop session (kiosk),
|
|
# not as a system service — both would fight over the microphone.
|
|
systemctl disable javis-slave.service >/dev/null 2>&1 || true
|
|
AUTOSTART_DIR="$(getent passwd "$SERVICE_USER" | cut -d: -f6)/.config/autostart"
|
|
sudo -u "$SERVICE_USER" mkdir -p "$AUTOSTART_DIR"
|
|
cat > "$AUTOSTART_DIR/javis-slave.desktop" <<EOF
|
|
[Desktop Entry]
|
|
Type=Application
|
|
Name=Javis Slave
|
|
Exec=env JAVIS_SLAVE_CONFIG_DIR=$INSTALL_DIR/config $INSTALL_DIR/.venv/bin/python -m javis_slave --kiosk
|
|
X-GNOME-Autostart-enabled=true
|
|
EOF
|
|
chown "$SERVICE_USER":"$SERVICE_USER" "$AUTOSTART_DIR/javis-slave.desktop"
|
|
else
|
|
systemctl enable javis-slave.service
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
Done. Next steps:
|
|
|
|
1. Find your audio devices:
|
|
sudo -u $SERVICE_USER JAVIS_SLAVE_CONFIG_DIR=$INSTALL_DIR/config \\
|
|
$INSTALL_DIR/.venv/bin/python -m javis_slave --list-devices
|
|
2. Pair with the master (generate a code in its Devices tab), e.g.:
|
|
sudo -u $SERVICE_USER JAVIS_SLAVE_CONFIG_DIR=$INSTALL_DIR/config \\
|
|
$INSTALL_DIR/.venv/bin/python -m javis_slave --headless \\
|
|
--input-device 1 --output-device 0 --pair 192.168.1.10:8765 123456 --name Kueche
|
|
Press Ctrl+C once you hear the wake chime working, then:
|
|
EOF
|
|
if [[ $WITH_GUI -eq 1 ]]; then
|
|
echo " 3. Reboot (or log out/in): the touch UI autostarts in kiosk mode."
|
|
echo " Manual start: $INSTALL_DIR/.venv/bin/python -m javis_slave --kiosk"
|
|
else
|
|
echo " 3. sudo systemctl start javis-slave"
|
|
echo " journalctl -u javis-slave -f"
|
|
fi |