setup_raspberry.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. #!/usr/bin/env bash
  2. # ============================================================================
  3. # Trixy Server — Raspberry Pi 4 Setup
  4. #
  5. # Konfiguriert:
  6. # - Hostname: trixyone
  7. # - eth0: DHCP (kabelgebundenes Netzwerk)
  8. # - wlan0: Access Point mit SSID "trixyone_srv" (eigenes Subnetz 10.10.10.0/24)
  9. # - DHCP-Server (dnsmasq) fuer WLAN-Clients
  10. # - Trixy Server als systemd-Service (auto-start, --auto-regist)
  11. # - Python venv mit allen Abhaengigkeiten
  12. #
  13. # Ausfuehrung:
  14. # chmod +x setup_raspberry.sh
  15. # sudo ./setup_raspberry.sh
  16. #
  17. # ============================================================================
  18. set -euo pipefail
  19. # --- Konfiguration ---
  20. TRIXY_USER="pi"
  21. TRIXY_DIR="/home/${TRIXY_USER}/trixy"
  22. VENV_DIR="${TRIXY_DIR}/venv"
  23. HOSTNAME="trixyone"
  24. WIFI_SSID="trixyone_srv"
  25. WIFI_PASS="trixy2026!"
  26. WIFI_CHANNEL=6
  27. AP_IP="10.10.10.1"
  28. AP_SUBNET="10.10.10.0/24"
  29. DHCP_RANGE_START="10.10.10.10"
  30. DHCP_RANGE_END="10.10.10.50"
  31. DHCP_LEASE="12h"
  32. COUNTRY_CODE="DE"
  33. # --- Checks ---
  34. if [[ $EUID -ne 0 ]]; then
  35. echo "FEHLER: Dieses Script muss als root ausgefuehrt werden (sudo)."
  36. exit 1
  37. fi
  38. if [[ ! -d "${TRIXY_DIR}/source" ]]; then
  39. echo "FEHLER: Trixy-Quellcode nicht gefunden unter ${TRIXY_DIR}/source"
  40. echo "Bitte das Projekt nach ${TRIXY_DIR}/ kopieren."
  41. exit 1
  42. fi
  43. echo "========================================"
  44. echo " Trixy Server — Raspberry Pi Setup"
  45. echo "========================================"
  46. echo ""
  47. echo " Hostname: ${HOSTNAME}"
  48. echo " WLAN AP SSID: ${WIFI_SSID}"
  49. echo " AP IP: ${AP_IP}"
  50. echo " Trixy Dir: ${TRIXY_DIR}"
  51. echo ""
  52. # ============================================================================
  53. # 1. System aktualisieren und Pakete installieren
  54. # ============================================================================
  55. echo "[1/8] System aktualisieren und Pakete installieren..."
  56. apt-get update -qq
  57. apt-get upgrade -y -qq
  58. # Grundpakete + Python
  59. apt-get install -y -qq \
  60. python3-dev python3-venv python3-pip \
  61. hostapd dnsmasq \
  62. iptables \
  63. git curl wget
  64. # Audio-System (PortAudio fuer sounddevice, ALSA fuer Wiedergabe)
  65. apt-get install -y -qq \
  66. portaudio19-dev \
  67. libasound2-dev \
  68. alsa-utils
  69. # Multimedia: ffmpeg (MP3/OGG/FLAC/Opus Dekodierung, TTS-Konvertierung, yt-dlp Backend)
  70. apt-get install -y -qq \
  71. ffmpeg
  72. # Numerik / Wissenschaftliche Bibliotheken (numpy, scipy, scikit-learn)
  73. apt-get install -y -qq \
  74. libopenblas-dev \
  75. libatlas-base-dev \
  76. liblapack-dev \
  77. gfortran
  78. # Kryptographie und Netzwerk (cryptography, aiohttp)
  79. apt-get install -y -qq \
  80. libffi-dev \
  81. libssl-dev
  82. # Optionale System-Deps fuer Plugins
  83. apt-get install -y -qq \
  84. pydub 2>/dev/null || true # Manche Distros haben es als Paket
  85. apt-get install -y -qq \
  86. espeak-ng 2>/dev/null || true # Fallback-TTS / Piper-Abhaengigkeit
  87. # hostapd und dnsmasq erst mal stoppen (werden spaeter konfiguriert)
  88. systemctl stop hostapd 2>/dev/null || true
  89. systemctl stop dnsmasq 2>/dev/null || true
  90. echo " -> System-Pakete installiert (inkl. ffmpeg, PortAudio, ALSA)"
  91. # ============================================================================
  92. # 2. Hostname setzen
  93. # ============================================================================
  94. echo "[2/8] Hostname setzen: ${HOSTNAME}..."
  95. hostnamectl set-hostname "${HOSTNAME}"
  96. # /etc/hosts aktualisieren
  97. if ! grep -q "${HOSTNAME}" /etc/hosts; then
  98. sed -i "s/127\.0\.1\.1.*/127.0.1.1\t${HOSTNAME}/" /etc/hosts
  99. fi
  100. echo " -> Hostname gesetzt"
  101. # ============================================================================
  102. # 3. WLAN Access Point konfigurieren (hostapd)
  103. # ============================================================================
  104. echo "[3/8] WLAN Access Point konfigurieren..."
  105. # WLAN-Laendereinstellung
  106. raspi-config nonint do_wifi_country "${COUNTRY_CODE}" 2>/dev/null || \
  107. iw reg set "${COUNTRY_CODE}" 2>/dev/null || true
  108. # hostapd-Konfiguration
  109. cat > /etc/hostapd/hostapd.conf << HOSTAPD_EOF
  110. # Trixy Access Point — SSID unabhaengig vom Hostname
  111. interface=wlan0
  112. driver=nl80211
  113. ssid=${WIFI_SSID}
  114. hw_mode=g
  115. channel=${WIFI_CHANNEL}
  116. wmm_enabled=0
  117. macaddr_acl=0
  118. auth_algs=1
  119. ignore_broadcast_ssid=0
  120. wpa=2
  121. wpa_passphrase=${WIFI_PASS}
  122. wpa_key_mgmt=WPA-PSK
  123. wpa_pairwise=TKIP
  124. rsn_pairwise=CCMP
  125. country_code=${COUNTRY_CODE}
  126. ieee80211n=1
  127. ieee80211d=1
  128. HOSTAPD_EOF
  129. # hostapd Default-Config setzen
  130. sed -i 's|^#DAEMON_CONF=.*|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd 2>/dev/null || true
  131. echo 'DAEMON_CONF="/etc/hostapd/hostapd.conf"' > /etc/default/hostapd
  132. # hostapd unmask und aktivieren
  133. systemctl unmask hostapd
  134. systemctl enable hostapd
  135. echo " -> hostapd konfiguriert (SSID: ${WIFI_SSID})"
  136. # ============================================================================
  137. # 4. DHCP-Server fuer WLAN (dnsmasq)
  138. # ============================================================================
  139. echo "[4/8] DHCP-Server (dnsmasq) konfigurieren..."
  140. # Originale dnsmasq.conf sichern
  141. if [[ -f /etc/dnsmasq.conf ]] && [[ ! -f /etc/dnsmasq.conf.orig ]]; then
  142. cp /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
  143. fi
  144. cat > /etc/dnsmasq.conf << DNSMASQ_EOF
  145. # Trixy DHCP-Server — nur fuer wlan0 (Access Point)
  146. interface=wlan0
  147. bind-interfaces
  148. # DHCP-Range fuer Satellites
  149. dhcp-range=${DHCP_RANGE_START},${DHCP_RANGE_END},255.255.255.0,${DHCP_LEASE}
  150. # DNS weiterleiten (Satellites brauchen kein Internet, aber falls doch)
  151. server=8.8.8.8
  152. server=8.8.4.4
  153. # Hostname-Aufloesung: trixyone_srv → AP IP
  154. address=/${WIFI_SSID}/${AP_IP}
  155. DNSMASQ_EOF
  156. systemctl enable dnsmasq
  157. echo " -> dnsmasq konfiguriert (${DHCP_RANGE_START} - ${DHCP_RANGE_END})"
  158. # ============================================================================
  159. # 5. Netzwerk konfigurieren (dhcpcd — statische IP fuer wlan0)
  160. # ============================================================================
  161. echo "[5/8] Netzwerk konfigurieren..."
  162. # dhcpcd.conf: eth0 = DHCP, wlan0 = statische IP (kein wpa_supplicant)
  163. # Bestehende wlan0-Konfiguration entfernen
  164. if grep -q "interface wlan0" /etc/dhcpcd.conf 2>/dev/null; then
  165. # Existierenden wlan0-Block entfernen
  166. sed -i '/^# Trixy AP/,/^$/d' /etc/dhcpcd.conf
  167. fi
  168. cat >> /etc/dhcpcd.conf << DHCPCD_EOF
  169. # Trixy AP — wlan0 statische IP fuer Access Point
  170. interface wlan0
  171. static ip_address=${AP_IP}/24
  172. nohook wpa_supplicant
  173. DHCPCD_EOF
  174. # IP-Forwarding aktivieren (damit Satellites ggf. Internet ueber eth0 bekommen)
  175. sed -i 's/^#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
  176. sysctl -w net.ipv4.ip_forward=1 >/dev/null
  177. # NAT/Masquerading: wlan0 → eth0 (Satellites bekommen Internet ueber Kabel)
  178. iptables -t nat -C POSTROUTING -o eth0 -j MASQUERADE 2>/dev/null || \
  179. iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
  180. # iptables-Regeln persistent machen
  181. iptables-save > /etc/iptables.ipv4.nat
  182. # Beim Boot laden
  183. if ! grep -q "iptables-restore" /etc/rc.local 2>/dev/null; then
  184. sed -i '/^exit 0/i iptables-restore < /etc/iptables.ipv4.nat' /etc/rc.local 2>/dev/null || true
  185. fi
  186. echo " -> eth0: DHCP, wlan0: ${AP_IP}/24 (statisch)"
  187. echo " -> NAT: wlan0 → eth0 (Internet-Weiterleitung)"
  188. # ============================================================================
  189. # 6. Python venv und Abhaengigkeiten
  190. # ============================================================================
  191. echo "[6/8] Python venv erstellen und Abhaengigkeiten installieren..."
  192. # Python-Version pruefen (mindestens 3.10 noetig)
  193. PY_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
  194. PY_MAJOR=$(python3 -c "import sys; print(sys.version_info.major)")
  195. PY_MINOR=$(python3 -c "import sys; print(sys.version_info.minor)")
  196. echo " -> Python ${PY_VERSION} gefunden"
  197. if [[ "${PY_MAJOR}" -lt 3 ]] || [[ "${PY_MINOR}" -lt 10 ]]; then
  198. echo " WARNUNG: Python >= 3.10 wird benoetigt (gefunden: ${PY_VERSION})"
  199. echo " Installiere Python 3.11 aus Debian-Repos..."
  200. apt-get install -y -qq python3.11 python3.11-venv python3.11-dev 2>/dev/null || {
  201. echo " Python 3.11 nicht in Repos verfuegbar — baue aus Source..."
  202. apt-get install -y -qq build-essential zlib1g-dev libncurses5-dev \
  203. libgdbm-dev libnss3-dev libreadline-dev libsqlite3-dev
  204. PY_BUILD_VERSION="3.11.9"
  205. cd /tmp
  206. wget -q "https://www.python.org/ftp/python/${PY_BUILD_VERSION}/Python-${PY_BUILD_VERSION}.tgz"
  207. tar xzf "Python-${PY_BUILD_VERSION}.tgz"
  208. cd "Python-${PY_BUILD_VERSION}"
  209. ./configure --enable-optimizations --prefix=/usr/local >/dev/null 2>&1
  210. make -j$(nproc) >/dev/null 2>&1
  211. make altinstall >/dev/null 2>&1
  212. cd "${TRIXY_DIR}/source"
  213. rm -rf "/tmp/Python-${PY_BUILD_VERSION}" "/tmp/Python-${PY_BUILD_VERSION}.tgz"
  214. echo " -> Python ${PY_BUILD_VERSION} installiert nach /usr/local/"
  215. }
  216. # Bestes verfuegbares Python finden
  217. PYTHON_BIN=$(command -v python3.11 || command -v /usr/local/bin/python3.11 || echo "python3")
  218. else
  219. PYTHON_BIN="python3"
  220. fi
  221. echo " -> Verwende: ${PYTHON_BIN} ($(${PYTHON_BIN} --version 2>&1))"
  222. # venv erstellen (falls nicht vorhanden)
  223. if [[ ! -d "${VENV_DIR}" ]]; then
  224. sudo -u "${TRIXY_USER}" "${PYTHON_BIN}" -m venv "${VENV_DIR}"
  225. fi
  226. # pip aktualisieren
  227. sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install --upgrade pip setuptools wheel -q
  228. # Kern-Requirements installieren
  229. echo " -> Installiere Kern-Abhaengigkeiten..."
  230. sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install -r "${TRIXY_DIR}/source/requirements.txt" -q
  231. # Gemeinsame optionale Python-Pakete (von mehreren Plugins/Core genutzt)
  232. echo " -> Installiere gemeinsame optionale Pakete..."
  233. sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install -q \
  234. pydub \
  235. mutagen \
  236. aiohttp \
  237. edge-tts \
  238. gtts \
  239. PyYAML \
  240. 2>/dev/null || echo " (einige optionale Pakete nicht verfuegbar — nicht kritisch)"
  241. # Plugin-Requirements installieren (nur nicht-auskommentierte Zeilen)
  242. echo " -> Installiere Plugin-Abhaengigkeiten..."
  243. PLUGIN_FAIL=""
  244. for req_file in "${TRIXY_DIR}"/source/plugins/*/requirements.txt; do
  245. plugin_name=$(basename "$(dirname "${req_file}")")
  246. # Nur Zeilen die nicht leer und nicht auskommentiert sind
  247. active_deps=$(grep -v '^\s*#' "${req_file}" | grep -v '^\s*$' || true)
  248. if [[ -z "${active_deps}" ]]; then
  249. continue
  250. fi
  251. echo " [${plugin_name}] $(echo "${active_deps}" | tr '\n' ', ')"
  252. if ! sudo -u "${TRIXY_USER}" "${VENV_DIR}/bin/pip" install -r "${req_file}" -q 2>/dev/null; then
  253. PLUGIN_FAIL="${PLUGIN_FAIL} ${plugin_name}"
  254. echo " WARNUNG: ${plugin_name} — einige Abhaengigkeiten fehlgeschlagen"
  255. fi
  256. done
  257. if [[ -n "${PLUGIN_FAIL}" ]]; then
  258. echo " -> Plugin-Warnungen (nicht kritisch):${PLUGIN_FAIL}"
  259. fi
  260. # ffmpeg Verfuegbarkeit verifizieren
  261. echo ""
  262. echo " Verifiziere System-Abhaengigkeiten:"
  263. for cmd in ffmpeg ffprobe python3 alsamixer; do
  264. if command -v "${cmd}" >/dev/null 2>&1; then
  265. echo " ✓ ${cmd}"
  266. else
  267. echo " ✗ ${cmd} — FEHLT"
  268. fi
  269. done
  270. echo " -> venv erstellt: ${VENV_DIR}"
  271. # ============================================================================
  272. # 7. Verzeichnisse und Berechtigungen
  273. # ============================================================================
  274. echo "[7/8] Verzeichnisse und Berechtigungen pruefen..."
  275. # Sicherstellen dass alle noetige Verzeichnisse existieren und dem User gehoeren
  276. for dir in data logs satellites certs sync_data models assets; do
  277. mkdir -p "${TRIXY_DIR}/source/${dir}" 2>/dev/null || true
  278. done
  279. chown -R "${TRIXY_USER}:${TRIXY_USER}" "${TRIXY_DIR}"
  280. echo " -> Verzeichnisse erstellt und Berechtigungen gesetzt"
  281. # ============================================================================
  282. # 8. Trixy systemd-Service
  283. # ============================================================================
  284. echo "[8/8] Trixy systemd-Service einrichten..."
  285. cat > /etc/systemd/system/trixy-server.service << SERVICE_EOF
  286. [Unit]
  287. Description=Trixy Voice Assistant Server
  288. After=network-online.target hostapd.service dnsmasq.service
  289. Wants=network-online.target
  290. [Service]
  291. Type=simple
  292. User=${TRIXY_USER}
  293. Group=${TRIXY_USER}
  294. WorkingDirectory=${TRIXY_DIR}/source
  295. ExecStart=${VENV_DIR}/bin/python3 main.py server --auto-regist --debug
  296. Restart=on-failure
  297. RestartSec=5
  298. StartLimitIntervalSec=60
  299. StartLimitBurst=5
  300. # Umgebung
  301. Environment=PYTHONUNBUFFERED=1
  302. Environment=HOME=/home/${TRIXY_USER}
  303. # Logging
  304. StandardOutput=journal
  305. StandardError=journal
  306. SyslogIdentifier=trixy
  307. [Install]
  308. WantedBy=multi-user.target
  309. SERVICE_EOF
  310. systemctl daemon-reload
  311. systemctl enable trixy-server.service
  312. echo " -> trixy-server.service aktiviert (auto-start beim Boot)"
  313. # ============================================================================
  314. # Zusammenfassung
  315. # ============================================================================
  316. echo ""
  317. echo "========================================"
  318. echo " Setup abgeschlossen!"
  319. echo "========================================"
  320. echo ""
  321. echo " Netzwerk:"
  322. echo " eth0 (Kabel): DHCP — erreichbar als '${HOSTNAME}'"
  323. echo " wlan0 (AP): SSID '${WIFI_SSID}', IP ${AP_IP}"
  324. echo " WLAN-Passwort: ${WIFI_PASS}"
  325. echo " Satellites verbinden sich auf '${WIFI_SSID}'"
  326. echo ""
  327. echo " Trixy Server:"
  328. echo " Service: trixy-server.service"
  329. echo " Start: sudo systemctl start trixy-server"
  330. echo " Status: sudo systemctl status trixy-server"
  331. echo " Logs: journalctl -u trixy-server -f"
  332. echo " Ports: 2101-2105 (auf allen Interfaces)"
  333. echo ""
  334. echo " Naechste Schritte:"
  335. echo " 1. sudo reboot"
  336. echo " 2. SSH: ssh pi@${HOSTNAME}"
  337. echo " 3. Logs: journalctl -u trixy-server -f"
  338. echo ""
  339. echo " WLAN-Passwort aendern:"
  340. echo " /etc/hostapd/hostapd.conf → wpa_passphrase"
  341. echo ""