#!/usr/bin/env bash # ============================================================================ # Trixy Server — Raspberry Pi 4 Setup # # Konfiguriert: # - Hostname: trixyone # - eth0: DHCP (kabelgebundenes Netzwerk) # - wlan0: Access Point mit SSID "trixyone_srv" (eigenes Subnetz 10.10.10.0/24) # - DHCP-Server (dnsmasq) fuer WLAN-Clients # - Trixy Server als systemd-Service (auto-start, --auto-regist) # - Python venv mit allen Abhaengigkeiten # # Ausfuehrung: # chmod +x setup_raspberry.sh # sudo ./setup_raspberry.sh # # ============================================================================ set -euo pipefail # --- Konfiguration --- TRIXY_USER="pi" TRIXY_DIR="/home/${TRIXY_USER}/trixy" VENV_DIR="${TRIXY_DIR}/venv" HOSTNAME="trixyone" WIFI_SSID="trixyone_srv" WIFI_PASS="trixy2026!" WIFI_CHANNEL=6 AP_IP="10.10.10.1" AP_SUBNET="10.10.10.0/24" DHCP_RANGE_START="10.10.10.10" DHCP_RANGE_END="10.10.10.50" DHCP_LEASE="12h" COUNTRY_CODE="DE" # --- Checks --- if [[ $EUID -ne 0 ]]; then echo "FEHLER: Dieses Script muss als root ausgefuehrt werden (sudo)." exit 1 fi if [[ ! -d "${TRIXY_DIR}/source" ]]; then echo "FEHLER: Trixy-Quellcode nicht gefunden unter ${TRIXY_DIR}/source" echo "Bitte das Projekt nach ${TRIXY_DIR}/ kopieren." exit 1 fi echo "========================================" echo " Trixy Server — Raspberry Pi Setup" echo "========================================" echo "" echo " Hostname: ${HOSTNAME}" echo " WLAN AP SSID: ${WIFI_SSID}" echo " AP IP: ${AP_IP}" echo " Trixy Dir: ${TRIXY_DIR}" echo "" # ============================================================================ # 1. System aktualisieren und Pakete installieren # ============================================================================ echo "[1/8] System aktualisieren und Pakete installieren..." apt-get update -qq apt-get upgrade -y -qq # Grundpakete + Python apt-get install -y -qq \ python3-dev python3-venv python3-pip \ hostapd dnsmasq \ iptables \ git curl wget # Audio-System (PortAudio fuer sounddevice, ALSA fuer Wiedergabe) apt-get install -y -qq \ portaudio19-dev \ libasound2-dev \ alsa-utils # Multimedia: ffmpeg (MP3/OGG/FLAC/Opus Dekodierung, TTS-Konvertierung, yt-dlp Backend) apt-get install -y -qq \ ffmpeg # Numerik / Wissenschaftliche Bibliotheken (numpy, scipy, scikit-learn) apt-get install -y -qq \ libopenblas-dev \ libatlas-base-dev \ liblapack-dev \ gfortran # Kryptographie und Netzwerk (cryptography, aiohttp) apt-get install -y -qq \ libffi-dev \ libssl-dev # Optionale System-Deps fuer Plugins apt-get install -y -qq \ pydub 2>/dev/null || true # Manche Distros haben es als Paket apt-get install -y -qq \ espeak-ng 2>/dev/null || true # Fallback-TTS / Piper-Abhaengigkeit # hostapd und dnsmasq erst mal stoppen (werden spaeter konfiguriert) systemctl stop hostapd 2>/dev/null || true systemctl stop dnsmasq 2>/dev/null || true echo " -> System-Pakete installiert (inkl. ffmpeg, PortAudio, ALSA)" # ============================================================================ # 2. Hostname setzen # ============================================================================ echo "[2/8] Hostname setzen: ${HOSTNAME}..." hostnamectl set-hostname "${HOSTNAME}" # /etc/hosts aktualisieren if ! grep -q "${HOSTNAME}" /etc/hosts; then sed -i "s/127\.0\.1\.1.*/127.0.1.1\t${HOSTNAME}/" /etc/hosts fi echo " -> Hostname gesetzt" # ============================================================================ # 3. WLAN Access Point konfigurieren (hostapd) # ============================================================================ echo "[3/8] WLAN Access Point konfigurieren..." # WLAN-Laendereinstellung raspi-config nonint do_wifi_country "${COUNTRY_CODE}" 2>/dev/null || \ iw reg set "${COUNTRY_CODE}" 2>/dev/null || true # hostapd-Konfiguration cat > /etc/hostapd/hostapd.conf << HOSTAPD_EOF # Trixy Access Point — SSID unabhaengig vom Hostname interface=wlan0 driver=nl80211 ssid=${WIFI_SSID} hw_mode=g channel=${WIFI_CHANNEL} wmm_enabled=0 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 wpa=2 wpa_passphrase=${WIFI_PASS} wpa_key_mgmt=WPA-PSK wpa_pairwise=TKIP rsn_pairwise=CCMP country_code=${COUNTRY_CODE} ieee80211n=1 ieee80211d=1 HOSTAPD_EOF # hostapd Default-Config setzen sed -i 's|^#DAEMON_CONF=.*|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd 2>/dev/null || true echo 'DAEMON_CONF="/etc/hostapd/hostapd.conf"' > /etc/default/hostapd # hostapd unmask und aktivieren systemctl unmask hostapd systemctl enable hostapd echo " -> hostapd konfiguriert (SSID: ${WIFI_SSID})" # ============================================================================ # 4. DHCP-Server fuer WLAN (dnsmasq) # ============================================================================ echo "[4/8] DHCP-Server (dnsmasq) konfigurieren..." # Originale dnsmasq.conf sichern if [[ -f /etc/dnsmasq.conf ]] && [[ ! -f /etc/dnsmasq.conf.orig ]]; then cp /etc/dnsmasq.conf /etc/dnsmasq.conf.orig fi cat > /etc/dnsmasq.conf << DNSMASQ_EOF # Trixy DHCP-Server — nur fuer wlan0 (Access Point) interface=wlan0 bind-interfaces # DHCP-Range fuer Satellites dhcp-range=${DHCP_RANGE_START},${DHCP_RANGE_END},255.255.255.0,${DHCP_LEASE} # DNS weiterleiten (Satellites brauchen kein Internet, aber falls doch) server=8.8.8.8 server=8.8.4.4 # Hostname-Aufloesung: trixyone_srv → AP IP address=/${WIFI_SSID}/${AP_IP} DNSMASQ_EOF systemctl enable dnsmasq echo " -> dnsmasq konfiguriert (${DHCP_RANGE_START} - ${DHCP_RANGE_END})" # ============================================================================ # 5. Netzwerk konfigurieren (dhcpcd — statische IP fuer wlan0) # ============================================================================ echo "[5/8] Netzwerk konfigurieren..." # dhcpcd.conf: eth0 = DHCP, wlan0 = statische IP (kein wpa_supplicant) # Bestehende wlan0-Konfiguration entfernen if grep -q "interface wlan0" /etc/dhcpcd.conf 2>/dev/null; then # Existierenden wlan0-Block entfernen sed -i '/^# Trixy AP/,/^$/d' /etc/dhcpcd.conf fi cat >> /etc/dhcpcd.conf << DHCPCD_EOF # Trixy AP — wlan0 statische IP fuer Access Point interface wlan0 static ip_address=${AP_IP}/24 nohook wpa_supplicant DHCPCD_EOF # IP-Forwarding aktivieren (damit Satellites ggf. Internet ueber eth0 bekommen) sed -i 's/^#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf sysctl -w net.ipv4.ip_forward=1 >/dev/null # NAT/Masquerading: wlan0 → eth0 (Satellites bekommen Internet ueber Kabel) iptables -t nat -C POSTROUTING -o eth0 -j MASQUERADE 2>/dev/null || \ iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # iptables-Regeln persistent machen iptables-save > /etc/iptables.ipv4.nat # Beim Boot laden if ! grep -q "iptables-restore" /etc/rc.local 2>/dev/null; then sed -i '/^exit 0/i iptables-restore < /etc/iptables.ipv4.nat' /etc/rc.local 2>/dev/null || true fi echo " -> eth0: DHCP, wlan0: ${AP_IP}/24 (statisch)" echo " -> NAT: wlan0 → eth0 (Internet-Weiterleitung)" # ============================================================================ # 6. Python venv und Abhaengigkeiten # ============================================================================ echo "[6/8] Python venv erstellen und Abhaengigkeiten installieren..." # Python-Version pruefen (mindestens 3.10 noetig) PY_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')") PY_MAJOR=$(python3 -c "import sys; print(sys.version_info.major)") PY_MINOR=$(python3 -c "import sys; print(sys.version_info.minor)") echo " -> Python ${PY_VERSION} gefunden" if [[ "${PY_MAJOR}" -lt 3 ]] || [[ "${PY_MINOR}" -lt 10 ]]; then echo " WARNUNG: Python >= 3.10 wird benoetigt (gefunden: ${PY_VERSION})" echo " Installiere Python 3.11 aus Debian-Repos..." apt-get install -y -qq python3.11 python3.11-venv python3.11-dev 2>/dev/null || { echo " Python 3.11 nicht in Repos verfuegbar — baue aus Source..." apt-get install -y -qq build-essential zlib1g-dev libncurses5-dev \ libgdbm-dev libnss3-dev libreadline-dev libsqlite3-dev PY_BUILD_VERSION="3.11.9" cd /tmp wget -q "https://www.python.org/ftp/python/${PY_BUILD_VERSION}/Python-${PY_BUILD_VERSION}.tgz" tar xzf "Python-${PY_BUILD_VERSION}.tgz" cd "Python-${PY_BUILD_VERSION}" ./configure --enable-optimizations --prefix=/usr/local >/dev/null 2>&1 make -j$(nproc) >/dev/null 2>&1 make altinstall >/dev/null 2>&1 cd "${TRIXY_DIR}/source" rm -rf "/tmp/Python-${PY_BUILD_VERSION}" "/tmp/Python-${PY_BUILD_VERSION}.tgz" echo " -> Python ${PY_BUILD_VERSION} installiert nach /usr/local/" } # Bestes verfuegbares Python finden PYTHON_BIN=$(command -v python3.11 || command -v /usr/local/bin/python3.11 || echo "python3") else PYTHON_BIN="python3" fi echo " -> Verwende: ${PYTHON_BIN} ($(${PYTHON_BIN} --version 2>&1))" # venv erstellen (falls nicht vorhanden) if [[ ! -d "${VENV_DIR}" ]]; then sudo -u "${TRIXY_USER}" "${PYTHON_BIN}" -m venv "${VENV_DIR}" fi # pip aktualisieren sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel -q # Kern-Requirements installieren echo " -> Installiere Kern-Abhaengigkeiten..." sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install -r "${TRIXY_DIR}/source/requirements.txt" -q # Gemeinsame optionale Python-Pakete (von mehreren Plugins/Core genutzt) echo " -> Installiere gemeinsame optionale Pakete..." sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install -q \ pydub \ mutagen \ aiohttp \ edge-tts \ gtts \ PyYAML \ 2>/dev/null || echo " (einige optionale Pakete nicht verfuegbar — nicht kritisch)" # Plugin-Requirements installieren (nur nicht-auskommentierte Zeilen) echo " -> Installiere Plugin-Abhaengigkeiten..." PLUGIN_FAIL="" for req_file in "${TRIXY_DIR}"/source/plugins/*/requirements.txt; do plugin_name=$(basename "$(dirname "${req_file}")") # Nur Zeilen die nicht leer und nicht auskommentiert sind active_deps=$(grep -v '^\s*#' "${req_file}" | grep -v '^\s*$' || true) if [[ -z "${active_deps}" ]]; then continue fi echo " [${plugin_name}] $(echo "${active_deps}" | tr '\n' ', ')" if ! sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install -r "${req_file}" -q 2>/dev/null; then PLUGIN_FAIL="${PLUGIN_FAIL} ${plugin_name}" echo " WARNUNG: ${plugin_name} — einige Abhaengigkeiten fehlgeschlagen" fi done if [[ -n "${PLUGIN_FAIL}" ]]; then echo " -> Plugin-Warnungen (nicht kritisch):${PLUGIN_FAIL}" fi # ffmpeg Verfuegbarkeit verifizieren echo "" echo " Verifiziere System-Abhaengigkeiten:" for cmd in ffmpeg ffprobe python3 alsamixer; do if command -v "${cmd}" >/dev/null 2>&1; then echo " ✓ ${cmd}" else echo " ✗ ${cmd} — FEHLT" fi done echo " -> venv erstellt: ${VENV_DIR}" # ============================================================================ # 7. Verzeichnisse und Berechtigungen # ============================================================================ echo "[7/8] Verzeichnisse und Berechtigungen pruefen..." # Sicherstellen dass alle noetige Verzeichnisse existieren und dem User gehoeren for dir in data logs satellites certs sync_data models assets; do mkdir -p "${TRIXY_DIR}/source/${dir}" 2>/dev/null || true done chown -R "${TRIXY_USER}:${TRIXY_USER}" "${TRIXY_DIR}" echo " -> Verzeichnisse erstellt und Berechtigungen gesetzt" # ============================================================================ # 8. Trixy systemd-Service # ============================================================================ echo "[8/8] Trixy systemd-Service einrichten..." cat > /etc/systemd/system/trixy-server.service << SERVICE_EOF [Unit] Description=Trixy Voice Assistant Server After=network-online.target hostapd.service dnsmasq.service Wants=network-online.target [Service] Type=simple User=${TRIXY_USER} Group=${TRIXY_USER} WorkingDirectory=${TRIXY_DIR}/source ExecStart=${VENV_DIR}/bin/python3 main.py server --auto-regist --debug Restart=on-failure RestartSec=5 StartLimitIntervalSec=60 StartLimitBurst=5 # Umgebung Environment=PYTHONUNBUFFERED=1 Environment=HOME=/home/${TRIXY_USER} # Logging StandardOutput=journal StandardError=journal SyslogIdentifier=trixy [Install] WantedBy=multi-user.target SERVICE_EOF systemctl daemon-reload systemctl enable trixy-server.service echo " -> trixy-server.service aktiviert (auto-start beim Boot)" # ============================================================================ # Zusammenfassung # ============================================================================ echo "" echo "========================================" echo " Setup abgeschlossen!" echo "========================================" echo "" echo " Netzwerk:" echo " eth0 (Kabel): DHCP — erreichbar als '${HOSTNAME}'" echo " wlan0 (AP): SSID '${WIFI_SSID}', IP ${AP_IP}" echo " WLAN-Passwort: ${WIFI_PASS}" echo " Satellites verbinden sich auf '${WIFI_SSID}'" echo "" echo " Trixy Server:" echo " Service: trixy-server.service" echo " Start: sudo systemctl start trixy-server" echo " Status: sudo systemctl status trixy-server" echo " Logs: journalctl -u trixy-server -f" echo " Ports: 2101-2105 (auf allen Interfaces)" echo "" echo " Naechste Schritte:" echo " 1. sudo reboot" echo " 2. SSH: ssh pi@${HOSTNAME}" echo " 3. Logs: journalctl -u trixy-server -f" echo "" echo " WLAN-Passwort aendern:" echo " /etc/hostapd/hostapd.conf → wpa_passphrase" echo ""