| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- #!/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 ""
|