From cf46181e489c1cc367376c7322b9a1c0b6c52b59 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Mon, 15 Dec 2025 04:02:51 +0000 Subject: [PATCH] Initial commit - Jetson Home Hub Setup (Face/Finger Auth, RustDesk, Authentik, Whisper) --- AI-SETUP-INSTRUCTIONS.md | 215 +++++++++++++++ CHAT-SUMMARY-COMPLETE.md | 370 ++++++++++++++++++++++++++ CHAT-SUMMARY.md | 131 +++++++++ README.md | 269 +++++++++++++++++++ adguard-dns-rewrites.txt | 48 ++++ caddy/Caddyfile | 72 +++++ docker-compose.yml | 292 ++++++++++++++++++++ first-boot.sh | 21 ++ homepage/bookmarks.yaml | 19 ++ homepage/docker.yaml | 6 + homepage/services.yaml | 105 ++++++++ homepage/settings.yaml | 40 +++ homepage/widgets.yaml | 29 ++ infomaniak/.env.example | 18 ++ infomaniak/docker-compose.yml | 128 +++++++++ infomaniak/nginx/conf.d/hofmanns.conf | 82 ++++++ infomaniak/setup-infomaniak.sh | 80 ++++++ setup.sh | 172 ++++++++++++ whisper-webui-jetson/Dockerfile | 16 ++ whisper-webui-jetson/app.py | 177 ++++++++++++ whisper-webui/Dockerfile | 24 ++ whisper-webui/app.py | 177 ++++++++++++ whisper-webui/requirements.txt | 6 + 23 files changed, 2497 insertions(+) create mode 100644 AI-SETUP-INSTRUCTIONS.md create mode 100644 CHAT-SUMMARY-COMPLETE.md create mode 100644 CHAT-SUMMARY.md create mode 100644 README.md create mode 100644 adguard-dns-rewrites.txt create mode 100644 caddy/Caddyfile create mode 100644 docker-compose.yml create mode 100644 first-boot.sh create mode 100644 homepage/bookmarks.yaml create mode 100644 homepage/docker.yaml create mode 100644 homepage/services.yaml create mode 100644 homepage/settings.yaml create mode 100644 homepage/widgets.yaml create mode 100644 infomaniak/.env.example create mode 100644 infomaniak/docker-compose.yml create mode 100644 infomaniak/nginx/conf.d/hofmanns.conf create mode 100644 infomaniak/setup-infomaniak.sh create mode 100644 setup.sh create mode 100644 whisper-webui-jetson/Dockerfile create mode 100644 whisper-webui-jetson/app.py create mode 100644 whisper-webui/Dockerfile create mode 100644 whisper-webui/app.py create mode 100644 whisper-webui/requirements.txt diff --git a/AI-SETUP-INSTRUCTIONS.md b/AI-SETUP-INSTRUCTIONS.md new file mode 100644 index 0000000..4b85cfe --- /dev/null +++ b/AI-SETUP-INSTRUCTIONS.md @@ -0,0 +1,215 @@ +# HOFMANNS.AI - SETUP INSTRUCTIONS FOR AI AGENTS + +**Ziel:** Jetson Orin Nano als zentraler Hub mit SSO (Passkeys) + RustDesk + +--- + +## WICHTIG: Architektur + +``` +JETSON (192.168.1.50) = MASTER +├── Authentik (SSO mit Face ID / Fingerprint) +├── RustDesk Server (hbbs/hbbr) +├── Alle Services (HA, Whisper, Jellyfin, etc.) +├── Private Daten (Kunden, Source, Docs) +└── Cloudflare Tunnel → *.hofmanns.ai + +INFOMANIAK (185.143.102.153) = DEV/PILOT +├── Git Repos +├── Dev Environments +├── AI Agents arbeiten hier +└── Kann jederzeit resettet werden +``` + +--- + +## FILES IN THIS PACKAGE + +``` +jetson-setup/ +│ +├── FOR JETSON (192.168.1.50): +│ ├── setup.sh → Run first after JetPack flash +│ ├── docker-compose.yml → All services including Authentik +│ ├── homepage/ → Dashboard config +│ └── whisper-webui-jetson/ → GPU-accelerated Whisper +│ +├── FOR INFOMANIAK (185.143.102.153): +│ └── infomaniak/ +│ ├── setup-infomaniak.sh → Optional dev setup +│ └── docker-compose.yml → Dev services only +│ +└── DOCUMENTATION: + ├── CHAT-SUMMARY-COMPLETE.md → Full context + ├── README.md → User guide + └── AI-SETUP-INSTRUCTIONS.md → This file +``` + +--- + +## JETSON SETUP STEPS + +### 1. Flash JetPack (on Ubuntu PC with SDK Manager) + +```bash +# Jetson in Recovery Mode +# SDK Manager → Jetson Orin Nano 8GB → JetPack 6.x → NVMe +``` + +### 2. First Boot Config + +```bash +# After first boot, copy files to Jetson +scp -r jetson-setup/ d@192.168.1.50:~/ + +# SSH to Jetson +ssh d@192.168.1.50 + +# Run setup +cd ~/jetson-setup +chmod +x setup.sh +sudo ./setup.sh +sudo reboot +``` + +### 3. Start Services + +```bash +cd ~/docker +docker-compose up -d +``` + +### 4. Configure Authentik + +1. Open: `http://192.168.1.50:9000/if/flow/initial-setup/` +2. Create admin account +3. Enable WebAuthn/Passkeys: + - Admin → Flows → default-authentication-flow + - Add Stage: "authenticator_validate" with WebAuthn +4. Create Application for each service +5. Set up Forward Auth proxy + +### 5. Configure Cloudflare Tunnel + +```bash +# Install cloudflared +curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64 -o cloudflared +chmod +x cloudflared +sudo mv cloudflared /usr/local/bin/ + +# Login and create tunnel +cloudflared tunnel login +cloudflared tunnel create jetson-home + +# Configure tunnel +cat > ~/.cloudflared/config.yml << EOF +tunnel: +credentials-file: /home/d/.cloudflared/.json + +ingress: + - hostname: auth.hofmanns.ai + service: http://localhost:9000 + - hostname: portal.hofmanns.ai + service: http://localhost:3001 + - hostname: home.hofmanns.ai + service: http://localhost:8123 + - hostname: whisper.hofmanns.ai + service: http://localhost:7860 + - hostname: files.hofmanns.ai + service: http://localhost:8081 + - hostname: "*.hofmanns.ai" + service: http://localhost:80 + - service: http_status:404 +EOF + +# Run as service +sudo cloudflared service install +sudo systemctl enable cloudflared +sudo systemctl start cloudflared +``` + +### 6. DNS Records (Cloudflare) + +| Type | Name | Content | +|------|------|---------| +| CNAME | auth | .cfargotunnel.com | +| CNAME | portal | .cfargotunnel.com | +| CNAME | home | .cfargotunnel.com | +| CNAME | whisper | .cfargotunnel.com | +| CNAME | files | .cfargotunnel.com | + +### 7. Register Passkeys + +1. Open auth.hofmanns.ai on each device +2. Login with initial password +3. Settings → MFA → Add WebAuthn Device +4. Use Face ID / Fingerprint to register +5. Done - no more passwords needed + +--- + +## RUSTDESK SETUP + +### Server runs on Jetson: + +```bash +# After docker-compose up, get the key: +cat ~/docker/rustdesk/id_ed25519.pub +``` + +### Client Config (all devices): + +| Setting | Value | +|---------|-------| +| ID Server | jetson.hofmanns.ai (or via tunnel) | +| Relay Server | jetson.hofmanns.ai | +| Key | (from id_ed25519.pub) | + +--- + +## PORTS OVERVIEW + +### Jetson (192.168.1.50): + +| Port | Service | +|------|---------| +| 80 | Nginx Proxy Manager | +| 443 | Nginx Proxy Manager (SSL) | +| 81 | NPM Admin UI | +| 3001 | Homepage Dashboard | +| 8123 | Home Assistant | +| 9000 | Authentik | +| 9090 | Cockpit | +| 7860 | Whisper | +| 8096 | Jellyfin | +| 8081 | FileBrowser | +| 8384 | Syncthing | +| 11434 | Ollama | +| 21115-21119 | RustDesk | + +--- + +## USER REQUIREMENTS + +- **NO passwords** - only Face ID / Fingerprint +- **NO email codes** - Passkeys only +- **NO 2FA apps** - device IS the auth +- **ONE login** - session stays active +- **ALL devices** - Quest 3, Android, Laptop, TV +- **Browser-based** - no apps needed (except RustDesk client) + +--- + +## RESULT + +After setup, user experience: + +1. Open browser → portal.hofmanns.ai +2. Touch finger OR show face +3. **Done. Everything accessible. All day.** + +--- + +**Package created:** December 2025 +**For:** Dee / hofmanns.ai / DOUANA® +**By:** Claude (Anthropic) diff --git a/CHAT-SUMMARY-COMPLETE.md b/CHAT-SUMMARY-COMPLETE.md new file mode 100644 index 0000000..5d609c4 --- /dev/null +++ b/CHAT-SUMMARY-COMPLETE.md @@ -0,0 +1,370 @@ +# Hofmanns.AI - Complete Infrastructure Setup + +**Chat Summary - Dezember 2025** +**Erstellt für:** Dee / DOUANA® / ZFEB GmbH + +--- + +## 🎯 Das Problem + +- 48 Jahre alt, Enterprise-Kunden (Banken, Konzerne) +- Jeden Tag 1-2 Stunden verloren mit: Logins, Passwörter, Email-Codes, Verify-Me, 2FA-Apps +- Jedes Device, jede App, jeder Service will eigenen Login +- Kein Bock mehr auf Console-Gefrickel +- Meta Quest 3 als primäres Arbeitsgerät → braucht Browser-UI + +**Das Ziel:** Ein Finger/Gesicht → alles offen. Fertig. + +--- + +## 🏗️ Die Architektur + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ AUTHENTIK SSO │ +│ auth.hofmanns.ai (Passkeys/WebAuthn) │ +│ Face ID / Fingerprint = einmal einloggen = fertig │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌─────────────────────┼─────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌───────────────┐ ┌───────────────┐ ┌───────────────┐ +│ INFOMANIAK │ │ JETSON │ │ DEVICES │ +│ SERVER │ │ ORIN NANO │ │ │ +│ (öffentlich) │◄──►│ (privat) │◄──►│ Quest 3 │ +│ │ │ │ │ Android │ +│ - Git Repos │ │ - Kundendaten │ │ Laptop │ +│ - Dev/Pilot │ │ - Source Code │ │ TV │ +│ - Auth Server │ │ - Dokumente │ │ │ +│ - RustDesk ID │ │ - Home Hub │ │ │ +└───────────────┘ └───────────────┘ └───────────────┘ +``` + +--- + +## 📍 Server Overview + +### Infomaniak Server (öffentlich) + +| Info | Wert | +|------|------| +| IP | 185.143.102.153 | +| OS | Debian 12 (bookworm) | +| User | debian | +| SSH | Port 22 mit RSA Key | +| Projekt | /home/debian/hofmanns.ai | +| Command | `hof` → navigiert zum Projekt | + +**Domains (alle auf diesem Server):** +- hofmanns.ai +- hofmanns.tech +- hofmanns.app +- hofmanns.shop +- hofmanns.ltd +- hofmann-s.com + +**Was läuft dort:** +- Git Repository (git.hofmanns.ai) +- Dev/Pilot Umgebungen +- AI Agents arbeiten hier (Claude, Manus, Perplexity) +- Docker installiert + +**Was NOCH drauf kommt:** +- Authentik (SSO mit Passkeys) +- RustDesk ID Server (hbbs/hbbr) +- Cloudflare Tunnel oder direkt + +### Jetson Orin Nano (privat, zu Hause) + +| Info | Wert | +|------|------| +| IP | 192.168.1.50 | +| OS | JetPack 6.x (Ubuntu-based) | +| User | d | +| Storage | 1TB NVMe | +| GPU | NVIDIA (für AI) | +| Standort | Am TV, zentral in Wohnung | + +**Was läuft dort:** +- Kundendaten +- Source Code (produktiv) +- Wichtige Dokumente +- Home Assistant +- AdGuard DNS +- Whisper (Sprache → Text) +- Jellyfin (Media) +- Private Cloud (FileBrowser) +- Syncthing +- Ollama (LLMs) + +--- + +## 🔐 SSO mit Passkeys (WebAuthn) + +**Keine Passwörter. Keine Email-Codes. Kein 2FA-App Stress.** + +### So funktioniert es: + +1. **Einmalig einrichten:** Passkey auf jedem Gerät registrieren +2. **Danach:** Finger auf Sensor oder Gesicht zeigen → eingeloggt +3. **Session:** Bleibt aktiv, kein ständiges neu einloggen + +### Unterstützte Geräte: + +| Gerät | Auth-Methode | +|-------|--------------| +| Android Handy | Fingerprint / Face | +| Meta Quest 3 | Handy als Authenticator (zeigt in VR) | +| Laptop | Fingerprint / Windows Hello | +| iPad/iPhone | Face ID / Touch ID | + +### Authentik Setup: + +``` +auth.hofmanns.ai + │ + ├── Passkey Registration + ├── Session Management + ├── Application Proxy + │ + └── Geschützte Apps: + ├── portal.hofmanns.ai (Dashboard) + ├── git.hofmanns.ai + ├── home.hofmanns.ai (Home Assistant) + ├── files.hofmanns.ai (FileBrowser) + ├── whisper.hofmanns.ai + └── ... alle anderen Services +``` + +--- + +## 🖥️ Das Dashboard + +**Ein Tab. Alles drin. Vollbild. Kein OS-Scheiss.** + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ portal.hofmanns.ai │ +│ (nach Passkey-Auth = sofort da) │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Home │ │ Whisper │ │ Jellyfin │ │ +│ │ Assistant │ │ Speech→Text│ │ Media │ │ +│ │ │ │ (GPU) │ │ │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Portainer │ │ Files │ │ Syncthing │ │ +│ │ Docker │ │ Cloud │ │ Sync │ │ +│ │ │ │ │ │ │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Git │ │ RustDesk │ │ Ollama │ │ +│ │ Repos │ │ Remote │ │ LLM │ │ +│ │ │ │ Desktop │ │ │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🔌 RustDesk (Self-Hosted TeamViewer) + +**Kein WireGuard Console-Gefrickel. GUI everywhere.** + +### Server (auf Infomaniak): + +```yaml +rustdesk-hbbs: # ID/Rendezvous Server + ports: 21115, 21116, 21118 + +rustdesk-hbbr: # Relay Server + ports: 21117, 21119 +``` + +### Client Setup: + +1. RustDesk App installieren (alle Plattformen) +2. Settings → Network → ID/Relay Server +3. ID Server: `185.143.102.153` (oder rustdesk.hofmanns.ai) +4. Relay Server: `185.143.102.153` +5. Key: (wird beim ersten Start generiert) + +### Zugriff: + +- Von Quest 3 Browser → RustDesk Web Client +- Von Handy → RustDesk App +- Von Laptop → RustDesk App +- Auf alle Rechner: Jetson, Gaming PC, Server + +--- + +## 📱 Jetson Orin Nano - Services + +| Port | Domain | Service | +|------|--------|---------| +| 3001 | portal.hofmanns.ai | Homepage Dashboard | +| 81 | - | Nginx Proxy Manager | +| 9443 | docker.hofmanns.ai | Portainer | +| 9090 | system.hofmanns.ai | Cockpit | +| 8123 | home.hofmanns.ai | Home Assistant | +| 3000 | dns.hofmanns.ai | AdGuard Home | +| 7860 | whisper.hofmanns.ai | Whisper WebUI | +| 8096 | media.hofmanns.ai | Jellyfin | +| 8081 | files.hofmanns.ai | FileBrowser | +| 8384 | sync.hofmanns.ai | Syncthing | +| 11434 | llm.hofmanns.ai | Ollama | + +--- + +## 🌐 Netzwerk Setup + +### Zu Hause: + +``` +5G Antenne (500 CHF) + │ + ▼ +Netgear Nighthawk M5 +(DHCP 192.168.1.x, DNS=192.168.1.50) + │ + ▼ +Netgear 8-Port Switch + │ + ├── Jetson Orin Nano (192.168.1.50) + │ └── USB WiFi → AP "Home.8" + │ + └── Gaming PC (RTX 4080) +``` + +### Tunnel nach draussen: + +``` +Jetson ──► Cloudflare Tunnel ──► *.hofmanns.ai + │ + └── RustDesk ──► Infomaniak Server ──► Clients überall +``` + +--- + +## 📋 Hardware Übersicht + +| Gerät | Zweck | Status | +|-------|-------|--------| +| Jetson Orin Nano 8GB | Home Hub, AI, Private Cloud | Setup pending | +| Gaming PC (Ryzen 9, RTX 4080, 64GB) | Workstation, Gaming | Aktiv | +| Netgear Nighthawk M5 | 5G Router, 800 Mbit | Aktiv | +| TP-Link USB WiFi Antenne | AP für Jetson | Vorhanden | +| Meta Quest 3 | VR, primäres Arbeitsgerät | Aktiv | +| 5x Raspberry Pi 5 | Übrig, verkaufen oder Projekte | Unused | +| Infomaniak Root Server | Public Services, Auth, Git | Aktiv | + +--- + +## 🚀 Installation - Reihenfolge + +### Phase 1: Jetson Setup (zu Hause) + +1. JetPack auf NVMe flashen (SDK Manager) +2. `setup.sh` ausführen +3. Docker Services starten +4. WiFi AP "Home.8" aktivieren +5. AdGuard DNS konfigurieren + +### Phase 2: Infomaniak Server + +1. SSH verbinden: `ssh hofmanns.ai` +2. Authentik installieren (Docker) +3. RustDesk Server installieren +4. Cloudflare Tunnel oder Nginx konfigurieren +5. DNS Records für alle Subdomains + +### Phase 3: SSO Integration + +1. Authentik Passkeys einrichten +2. Alle Services als Applications registrieren +3. Proxy-Provider für jeden Service +4. Passkey auf allen Geräten registrieren + +### Phase 4: Dashboard + +1. Homepage auf Jetson konfigurieren +2. Alle Services einbinden +3. Authentik Forward Auth +4. Testen von allen Devices + +--- + +## 📁 Dateien in diesem Setup + +``` +jetson-setup/ +├── setup.sh # Haupt-Setup für Jetson +├── docker-compose.yml # Alle lokalen Services +├── CHAT-SUMMARY.md # Diese Datei +├── adguard-dns-rewrites.txt # DNS Einträge +├── homepage/ +│ ├── services.yaml +│ ├── settings.yaml +│ ├── widgets.yaml +│ ├── docker.yaml +│ └── bookmarks.yaml +└── whisper-webui-jetson/ + ├── Dockerfile + └── app.py +``` + +--- + +## 🔑 Wichtige Credentials (ÄNDERN!) + +| Was | Default | Ändern auf | +|-----|---------|------------| +| WiFi "Home.8" | 123 | Sicheres Passwort | +| Nginx Proxy Manager | admin@example.com / changeme | Eigene | +| FileBrowser | admin / admin | Eigene | +| Portainer | (bei Setup) | Eigene | +| Authentik | (bei Setup) | Eigene | + +--- + +## 🎯 Das Endergebnis + +**Morgens aufstehen:** + +1. Quest 3 aufsetzen +2. Browser öffnen → portal.hofmanns.ai +3. Handy kurz ans Gesicht halten (Face ID) +4. **Fertig. Alles offen. Den ganzen Tag.** + +**Unterwegs:** + +1. Laptop/Handy öffnen +2. portal.hofmanns.ai +3. Finger auf Sensor +4. **Fertig. Voller Zugriff auf alles.** + +**Keine Passwörter. Keine Codes. Keine Apps. Keine Scheisse.** + +--- + +## 📞 Nächste Schritte + +1. [ ] Jetson JetPack flashen +2. [ ] `setup.sh` ausführen auf Jetson +3. [ ] Authentik auf Infomaniak installieren +4. [ ] RustDesk Server auf Infomaniak +5. [ ] DNS Records für Subdomains +6. [ ] Cloudflare Tunnel einrichten +7. [ ] Passkeys registrieren +8. [ ] Testen mit Quest 3 + +--- + +**Erstellt:** Dezember 2025 +**Chat mit:** Claude (Anthropic) +**Für:** Dee / hofmanns.ai / DOUANA® diff --git a/CHAT-SUMMARY.md b/CHAT-SUMMARY.md new file mode 100644 index 0000000..8c04e4f --- /dev/null +++ b/CHAT-SUMMARY.md @@ -0,0 +1,131 @@ +# Jetson Orin Nano - Home Hub Setup + +## Ausgangsproblem + +Raspberry Pi 5 als Access Point für "Home.8" WiFi hatte falsche Konfiguration: +- **Problem:** NetworkManager Hotspot Mode erstellt eigenes Subnetz (10.42.0.x) mit NAT +- **Gewollt:** Einfache Bridge die IPs vom Nighthawk Router (192.168.1.x) durchreicht + +## Lösung: Jetson Orin Nano als zentraler Hub + +Statt den RP5 weiter zu debuggen → komplettes Setup auf Jetson Orin Nano am TV. + +### Hardware Setup + +``` +5G Antenne (500 CHF) ──► Netgear Nighthawk M5 (800 Mbit) + │ + Netgear 8-Port Switch + │ + ┌─────────┴─────────┐ + │ │ + Jetson Orin Nano Gaming PC + (@ TV, NVMe 1TB) (RTX 4080) + │ + ┌──────────┼──────────┐ + │ │ │ + USB WiFi HDMI 4K Docker Services + (TP-Link) TV (alles lokal) +``` + +### Services (alle mit Web UI!) + +| Port | Service | Beschreibung | +|------|---------|--------------| +| 3001 | Homepage | Zentrales Dashboard | +| 81 | Nginx Proxy Manager | Reverse Proxy mit GUI | +| 9443 | Portainer | Docker Management | +| 9090 | Cockpit | System Management | +| 8123 | Home Assistant | Smart Home | +| 3000 | AdGuard Home | DNS + Werbeblocker | +| 7860 | Whisper WebUI | Sprache → Text (GPU) | +| 8096 | Jellyfin | Media Server | +| 8081 | FileBrowser | Private Cloud | +| 8384 | Syncthing | File Sync | +| 11434 | Ollama | Lokale LLMs | +| 21115-21119 | RustDesk | Remote Desktop Server | + +### Warum diese Entscheidungen + +1. **RustDesk statt WireGuard** - Remote Desktop mit GUI, kein Console-Gefrickel +2. **Nginx Proxy Manager statt Caddy** - Proxy-Konfiguration per Web UI +3. **Homepage Dashboard** - Zentrales UI für alles, perfekt für Meta Quest 3 Browser +4. **Cockpit** - System-Management im Browser statt SSH + +### Dateien im Setup + +``` +jetson-setup/ +├── setup.sh # Hauptscript +├── docker-compose.yml # Alle Services +├── adguard-dns-rewrites.txt # DNS Einträge +├── homepage/ +│ ├── services.yaml # Dashboard Services +│ ├── settings.yaml # Dashboard Settings +│ ├── widgets.yaml # System Widgets +│ ├── docker.yaml # Docker Integration +│ └── bookmarks.yaml # Links +└── whisper-webui-jetson/ + ├── Dockerfile + └── app.py # Gradio Web UI +``` + +### Installation + +1. **JetPack auf NVMe flashen** (SDK Manager am Ubuntu PC) +2. **Setup-Dateien kopieren:** + ```bash + scp -r jetson-setup/ d@192.168.1.50:~/ + ``` +3. **Setup ausführen:** + ```bash + cd ~/jetson-setup + chmod +x setup.sh + sudo ./setup.sh + sudo reboot + ``` +4. **Services starten:** + ```bash + cd ~/docker + docker-compose up -d + ``` + +### Nach dem Setup + +1. **Homepage öffnen:** `http://192.168.1.50:3001` +2. **Nginx Proxy Manager:** `http://192.168.1.50:81` (admin@example.com / changeme) +3. **AdGuard DNS Rewrites** für .lan Domains einrichten +4. **RustDesk Key holen:** `cat ~/docker/rustdesk/id_ed25519.pub` + +### RustDesk Client Setup + +1. RustDesk App installieren (Windows/Mac/Linux/Android/iOS) +2. Settings → Network → ID/Relay Server +3. ID Server: `192.168.1.50` +4. Relay Server: `192.168.1.50` +5. Key: (aus `id_ed25519.pub`) + +### Vorteile + +- **100% lokal** - keine Cloud-Abhängigkeit +- **Alles Web UI** - nix Console im Alltag +- **GPU beschleunigt** - Whisper, Ollama, Jellyfin Transcoding +- **Meta Quest 3 kompatibel** - Dashboard im VR Browser +- **Remote Access** - RustDesk für Desktop, alle Services über Proxy + +### Hardware die rumliegt + +- 5x Raspberry Pi 5 → verkaufen oder andere Projekte +- Jetson Orin Nano → jetzt der zentrale Hub +- TP-Link USB WiFi Antenne → AP "Home.8" am Jetson +- Netgear Nighthawk M5 → 5G Internet + DHCP +- Gaming PC (Ryzen 9, RTX 4080, 64GB) → bleibt Workstation + +### Noch zu tun + +- [ ] WiFi Passwort ändern (aktuell: 123) +- [ ] Nginx Proxy Manager Domains einrichten +- [ ] AdGuard DNS Rewrites hinzufügen +- [ ] RustDesk auf allen Geräten installieren +- [ ] Home Assistant Geräte einbinden +- [ ] Whisper/Piper für HA Voice Assistant diff --git a/README.md b/README.md new file mode 100644 index 0000000..bb4ae01 --- /dev/null +++ b/README.md @@ -0,0 +1,269 @@ +# Jetson Orin Nano 8GB - Complete Home Hub + +## Übersicht + +Alles lokal, keine Cloud, volle Kontrolle. + +| Domain | Service | Beschreibung | +|--------|---------|--------------| +| `home.lan` | Home Assistant | Smart Home Zentrale | +| `adguard.lan` | AdGuard Home | DNS + Werbeblocker | +| `whisper.lan` | Whisper WebUI | Sprache → Text (GPU) | +| `jellyfin.lan` | Jellyfin | Media Server | +| `portainer.lan` | Portainer | Docker Management | +| `vpn.lan` | WireGuard Easy | VPN für unterwegs | +| `files.lan` | FileBrowser | Private Cloud | +| `sync.lan` | Syncthing | Dateisync | +| `ollama.lan` | Ollama | Lokale LLMs | + +## Hardware Setup + +``` +5G Antenne ──► Nighthawk M5 (DHCP, DNS=192.168.1.50) + │ + Netgear Switch + │ + └── Jetson Orin Nano @ TV + ├── eth0: 192.168.1.50 + ├── USB: TP-Link WiFi → AP "Home.8" + └── HDMI: 4K TV +``` + +## Installation + +### 1. JetPack flashen (am PC mit Ubuntu) + +```bash +# SDK Manager: https://developer.nvidia.com/sdk-manager +# Jetson in Recovery Mode (Power aus, Recovery halten, Power an) +sdkmanager +# → Jetson Orin Nano 8GB +# → JetPack 6.x +# → NVMe als Ziel +``` + +### 2. Nach erstem Boot + +```bash +# Dateien auf Jetson kopieren +scp -r jetson-setup/ d@192.168.1.50:~/ + +# SSH zum Jetson +ssh d@192.168.1.50 + +# Setup ausführen +cd ~/jetson-setup +chmod +x setup.sh +sudo ./setup.sh +sudo reboot +``` + +### 3. Nach Reboot + +```bash +cd ~/docker +docker-compose up -d + +# Logs +docker-compose logs -f +``` + +### 4. AdGuard DNS Rewrites einrichten + +1. Öffne `http://192.168.1.50:3000` +2. Setup durchführen +3. Settings → DNS rewrites +4. Alle Einträge aus `adguard-dns-rewrites.txt` hinzufügen + +### 5. Nighthawk DNS ändern + +Im Nighthawk Router: DNS auf `192.168.1.50` setzen + +Jetzt funktionieren alle `.lan` Domains! + +## Zugriff + +### Im LAN + +Einfach im Browser: +- `http://home.lan` +- `http://adguard.lan` +- `http://whisper.lan` +- `http://portainer.lan` +- etc. + +### Unterwegs (VPN) + +1. Öffne `http://vpn.lan` (Passwort: 123 - ÄNDERN!) +2. Neuen Client erstellen +3. QR-Code mit WireGuard App scannen +4. Verbinden - fertig, alle .lan Domains erreichbar + +**Wichtig:** `WG_HOST` in docker-compose.yml auf deine externe IP/DynDNS setzen! + +## Services im Detail + +### Home Assistant (`home.lan`) + +Voice Assistant einrichten: +1. Settings → Devices & Services → Add Integration +2. "Wyoming Protocol" hinzufügen +3. Whisper: `localhost:10300` +4. Piper TTS: `localhost:10200` + +### Whisper WebUI (`whisper.lan`) + +- Sprache aufnehmen im Browser +- Dateien hochladen +- Läuft 100% lokal auf GPU +- Deutsch, Englisch, etc. + +### FileBrowser (`files.lan`) + +Private Cloud für alle Dateien: +- Default Login: `admin` / `admin` +- Zugriff auf `/home/d/` + +### Syncthing (`sync.lan`) + +Dateien zwischen Geräten synchronisieren: +- Handy ↔ Jetson ↔ PC +- Verschlüsselt, kein Server in der Mitte + +### Portainer (`portainer.lan`) + +Docker Management mit GUI: +- Container starten/stoppen +- Logs anschauen +- Compose verwalten + +## Dateistruktur + +``` +~/docker/ +├── docker-compose.yml +├── caddy/ +│ └── Caddyfile +├── homeassistant/config/ +├── adguard/conf/ +├── whisper/models/ +├── jellyfin/config/ +├── portainer/ +├── wireguard/ +├── filebrowser/ +├── syncthing/ +└── ollama/ +``` + +## Passwörter ändern! + +```bash +# WiFi AP +sudo nano /etc/hostapd/hostapd.conf +# wpa_passphrase=NEUES_PASSWORT +sudo systemctl restart hostapd + +# WireGuard +nano ~/docker/docker-compose.yml +# PASSWORD=NEUES_PASSWORT +docker-compose up -d wg-easy +``` + +## Port Forwarding (für VPN von aussen) + +Im Nighthawk Router: +- UDP 51820 → 192.168.1.50:51820 + +## Whisper Modelle + +| Modell | VRAM | Speed | Qualität | +|--------|------|-------|----------| +| tiny | ~1GB | ⚡⚡⚡⚡ | ★★☆☆☆ | +| base | ~1GB | ⚡⚡⚡ | ★★★☆☆ | +| small | ~2GB | ⚡⚡ | ★★★★☆ | +| medium | ~5GB | ⚡ | ★★★★★ | + +Ändern in `docker-compose.yml`: +```yaml +environment: + - WHISPER_MODEL=small +``` + +## Troubleshooting + +### Domains funktionieren nicht + +```bash +# DNS Test +nslookup home.lan 192.168.1.50 + +# AdGuard läuft? +docker logs adguard +``` + +### WiFi AP startet nicht + +```bash +sudo systemctl status hostapd +sudo journalctl -u hostapd -f +iw dev +``` + +### GPU nicht erkannt + +```bash +# Test +docker run --rm --runtime=nvidia nvidia/cuda:11.4-base nvidia-smi + +# Jetson Stats +sudo pip3 install jetson-stats +jtop +``` + +### Container startet nicht + +```bash +docker-compose logs +docker-compose up # ohne -d für live output +``` + +## Updates + +```bash +cd ~/docker +docker-compose pull +docker-compose up -d +``` + +## Backup + +```bash +# Alles +tar -czvf jetson-backup.tar.gz ~/docker/ + +# Nur Configs +tar -czvf ha-backup.tar.gz ~/docker/homeassistant/config +tar -czvf adguard-backup.tar.gz ~/docker/adguard/conf +``` + +## Nützliche Befehle + +```bash +# Alle Container Status +docker ps -a + +# Ressourcen +docker stats + +# Jetson GPU/CPU Monitor +jtop + +# Logs live +docker-compose logs -f + +# Neustart einzelner Service +docker-compose restart homeassistant + +# Shell in Container +docker exec -it homeassistant bash +``` diff --git a/adguard-dns-rewrites.txt b/adguard-dns-rewrites.txt new file mode 100644 index 0000000..2d0a7c6 --- /dev/null +++ b/adguard-dns-rewrites.txt @@ -0,0 +1,48 @@ +# ============================================================================= +# ADGUARD DNS REWRITES +# Nach AdGuard Setup: Settings → DNS rewrites → Bulk Add +# Kopiere alles unten und füge es ein +# ============================================================================= + +# Home Assistant +home.lan 192.168.1.50 +ha.lan 192.168.1.50 + +# AdGuard +adguard.lan 192.168.1.50 +dns.lan 192.168.1.50 + +# Whisper +whisper.lan 192.168.1.50 +stt.lan 192.168.1.50 + +# Jellyfin +jellyfin.lan 192.168.1.50 +media.lan 192.168.1.50 +tv.lan 192.168.1.50 + +# Portainer +portainer.lan 192.168.1.50 +docker.lan 192.168.1.50 + +# VPN +vpn.lan 192.168.1.50 +wg.lan 192.168.1.50 + +# Ollama +ollama.lan 192.168.1.50 +llm.lan 192.168.1.50 + +# Cockpit +cockpit.lan 192.168.1.50 +system.lan 192.168.1.50 + +# FileBrowser +files.lan 192.168.1.50 +cloud.lan 192.168.1.50 + +# Syncthing +sync.lan 192.168.1.50 + +# Jetson direkt +jetson.lan 192.168.1.50 diff --git a/caddy/Caddyfile b/caddy/Caddyfile new file mode 100644 index 0000000..38010fd --- /dev/null +++ b/caddy/Caddyfile @@ -0,0 +1,72 @@ +# ============================================================================= +# CADDY REVERSE PROXY +# Lokale Domains - kein HTTPS nötig im LAN +# ============================================================================= + +# Home Assistant +home.lan, ha.lan { + reverse_proxy localhost:8123 +} + +# AdGuard Home +adguard.lan, dns.lan { + reverse_proxy localhost:80 +} + +# Whisper Web UI +whisper.lan, stt.lan { + reverse_proxy localhost:7860 +} + +# Jellyfin +jellyfin.lan, media.lan, tv.lan { + reverse_proxy localhost:8096 +} + +# Portainer (Docker Management) +portainer.lan, docker.lan { + reverse_proxy localhost:9443 { + transport http { + tls_insecure_skip_verify + } + } +} + +# Ollama API +ollama.lan, llm.lan { + reverse_proxy localhost:11434 +} + +# Whisper API +whisper-api.lan { + reverse_proxy localhost:9000 +} + +# WireGuard Easy (VPN Management) +vpn.lan, wg.lan { + reverse_proxy localhost:51821 +} + +# Cockpit (System Management) - falls installiert +cockpit.lan, system.lan { + reverse_proxy localhost:9090 { + transport http { + tls_insecure_skip_verify + } + } +} + +# FileBrowser (Private Cloud) +files.lan, cloud.lan { + reverse_proxy localhost:8081 +} + +# Syncthing +sync.lan { + reverse_proxy localhost:8384 +} + +# Fallback - zeigt Status +:80 { + respond "Jetson Home Hub - Use: home.lan, adguard.lan, whisper.lan, jellyfin.lan, portainer.lan, vpn.lan, files.lan, sync.lan" +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..8aa0ec8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,292 @@ +version: '3.8' + +# ============================================================================= +# JETSON ORIN NANO - COMPLETE HOME HUB +# Alles mit Web UI - keine Console! +# ============================================================================= + +services: + # =========================================== + # NGINX PROXY MANAGER (Reverse Proxy mit GUI!) + # =========================================== + npm: + container_name: nginx-proxy-manager + image: jc21/nginx-proxy-manager:latest + restart: unless-stopped + ports: + - "80:80" + - "443:443" + - "81:81" + volumes: + - ./npm/data:/data + - ./npm/letsencrypt:/etc/letsencrypt + + # =========================================== + # HOMEPAGE - Zentrales Dashboard + # =========================================== + homepage: + container_name: homepage + image: ghcr.io/gethomepage/homepage:latest + restart: unless-stopped + ports: + - "3001:3000" + volumes: + - ./homepage:/app/config + - /var/run/docker.sock:/var/run/docker.sock:ro + + # =========================================== + # PORTAINER - Docker Management UI + # =========================================== + portainer: + container_name: portainer + image: portainer/portainer-ce:latest + restart: unless-stopped + ports: + - "9443:9443" + - "8000:8000" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./portainer:/data + + # =========================================== + # RUSTDESK SERVER (Self-hosted Remote Desktop) + # =========================================== + rustdesk-hbbs: + container_name: rustdesk-hbbs + image: rustdesk/rustdesk-server:latest + restart: unless-stopped + command: hbbs + ports: + - "21115:21115" + - "21116:21116" + - "21116:21116/udp" + - "21118:21118" + volumes: + - ./rustdesk:/root + depends_on: + - rustdesk-hbbr + + rustdesk-hbbr: + container_name: rustdesk-hbbr + image: rustdesk/rustdesk-server:latest + restart: unless-stopped + command: hbbr + ports: + - "21117:21117" + - "21119:21119" + volumes: + - ./rustdesk:/root + + # =========================================== + # HOME ASSISTANT + # =========================================== + homeassistant: + container_name: homeassistant + image: ghcr.io/home-assistant/home-assistant:stable + restart: unless-stopped + privileged: true + network_mode: host + depends_on: + - adguard + volumes: + - ./homeassistant/config:/config + - /etc/localtime:/etc/localtime:ro + - /run/dbus:/run/dbus:ro + environment: + - TZ=Europe/Zurich + + # =========================================== + # ADGUARD HOME (DNS + Ad Blocking + Local DNS) + # =========================================== + adguard: + container_name: adguard + image: adguard/adguardhome:latest + restart: unless-stopped + ports: + - "53:53/tcp" + - "53:53/udp" + - "3000:3000/tcp" + - "8080:80/tcp" + volumes: + - ./adguard/work:/opt/adguardhome/work + - ./adguard/conf:/opt/adguardhome/conf + + # =========================================== + # WHISPER GRADIO WEB UI + # =========================================== + whisper-webui: + container_name: whisper-webui + build: + context: ./whisper-webui-jetson + dockerfile: Dockerfile + restart: unless-stopped + ports: + - "7860:7860" + volumes: + - ./whisper/models:/root/.cache/whisper + runtime: nvidia + environment: + - NVIDIA_VISIBLE_DEVICES=all + - WHISPER_MODEL=base + devices: + - /dev/snd:/dev/snd + + # =========================================== + # WYOMING WHISPER (Home Assistant Voice) + # =========================================== + wyoming-whisper: + container_name: wyoming-whisper + image: rhasspy/wyoming-whisper:latest + restart: unless-stopped + ports: + - "10300:10300" + command: --model base --language de + volumes: + - ./whisper/wyoming:/data + runtime: nvidia + + # =========================================== + # PIPER TTS (Text-to-Speech) + # =========================================== + piper: + container_name: piper + image: rhasspy/wyoming-piper:latest + restart: unless-stopped + ports: + - "10200:10200" + command: --voice de_DE-thorsten-high + volumes: + - ./piper:/data + + # =========================================== + # JELLYFIN (Media Server) + # =========================================== + jellyfin: + container_name: jellyfin + image: jellyfin/jellyfin:latest + restart: unless-stopped + ports: + - "8096:8096" + volumes: + - ./jellyfin/config:/config + - ./jellyfin/cache:/cache + - /media:/media:ro + environment: + - TZ=Europe/Zurich + - NVIDIA_VISIBLE_DEVICES=all + runtime: nvidia + devices: + - /dev/dri:/dev/dri + + # =========================================== + # OLLAMA (Local LLM) + # =========================================== + ollama: + container_name: ollama + image: ollama/ollama:latest + restart: unless-stopped + ports: + - "11434:11434" + volumes: + - ./ollama:/root/.ollama + runtime: nvidia + environment: + - NVIDIA_VISIBLE_DEVICES=all + + # =========================================== + # FILEBROWSER (Private Cloud) + # =========================================== + filebrowser: + container_name: filebrowser + image: filebrowser/filebrowser:latest + restart: unless-stopped + ports: + - "8081:80" + volumes: + - /home/d:/srv + - ./filebrowser/database.db:/database.db + environment: + - PUID=1000 + - PGID=1000 + + # =========================================== + # AUTHENTIK - SSO mit Passkeys + # =========================================== + authentik-postgres: + container_name: authentik-postgres + image: postgres:16-alpine + restart: unless-stopped + volumes: + - ./authentik/database:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: ${PG_PASS:-supersecret} + POSTGRES_USER: authentik + POSTGRES_DB: authentik + healthcheck: + test: ["CMD-SHELL", "pg_isready -U authentik"] + interval: 30s + timeout: 5s + retries: 5 + + authentik-redis: + container_name: authentik-redis + image: redis:alpine + command: --save 60 1 --loglevel warning + restart: unless-stopped + volumes: + - ./authentik/redis:/data + + authentik: + container_name: authentik + image: ghcr.io/goauthentik/server:2024.2 + restart: unless-stopped + command: server + environment: + AUTHENTIK_REDIS__HOST: authentik-redis + AUTHENTIK_POSTGRESQL__HOST: authentik-postgres + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:-supersecret} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-changeme-run-openssl-rand-60-base64} + ports: + - "9000:9000" + depends_on: + - authentik-postgres + - authentik-redis + + authentik-worker: + container_name: authentik-worker + image: ghcr.io/goauthentik/server:2024.2 + restart: unless-stopped + command: worker + environment: + AUTHENTIK_REDIS__HOST: authentik-redis + AUTHENTIK_POSTGRESQL__HOST: authentik-postgres + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS:-supersecret} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-changeme-run-openssl-rand-60-base64} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + depends_on: + - authentik-postgres + - authentik-redis + + # =========================================== + # SYNCTHING (File Sync) + # =========================================== + syncthing: + container_name: syncthing + image: syncthing/syncthing:latest + restart: unless-stopped + ports: + - "8384:8384" + - "22000:22000/tcp" + - "22000:22000/udp" + - "21027:21027/udp" + volumes: + - ./syncthing:/var/syncthing + - /home/d/sync:/data + environment: + - PUID=1000 + - PGID=1000 diff --git a/first-boot.sh b/first-boot.sh new file mode 100644 index 0000000..2d6a509 --- /dev/null +++ b/first-boot.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# ============================================================================= +# FIRST BOOT - Run this from USB stick after JetPack flash +# ============================================================================= + +echo "Copying setup files to home directory..." +cp -r /media/$USER/*/jetson-setup ~/jetson-setup 2>/dev/null || \ +cp -r /media/*/jetson-setup ~/jetson-setup 2>/dev/null || \ +echo "Please copy jetson-setup folder manually to ~/" + +if [ -d ~/jetson-setup ]; then + echo "Files copied. Now run:" + echo "" + echo " cd ~/jetson-setup" + echo " chmod +x setup.sh" + echo " sudo ./setup.sh" + echo "" +else + echo "ERROR: Could not find jetson-setup on USB" + echo "Copy manually and run setup.sh" +fi diff --git a/homepage/bookmarks.yaml b/homepage/bookmarks.yaml new file mode 100644 index 0000000..259cd00 --- /dev/null +++ b/homepage/bookmarks.yaml @@ -0,0 +1,19 @@ +# ============================================================================= +# HOMEPAGE DASHBOARD - Bookmarks +# ============================================================================= + +- Management: + - Nighthawk Router: + - icon: router + href: http://192.168.1.1 + - Proxy Manager: + - icon: nginx-proxy-manager.svg + href: http://192.168.1.50:81 + +- Dokumentation: + - Home Assistant: + - icon: home-assistant.svg + href: https://www.home-assistant.io/docs/ + - RustDesk: + - icon: rustdesk.svg + href: https://rustdesk.com/docs/ diff --git a/homepage/docker.yaml b/homepage/docker.yaml new file mode 100644 index 0000000..f5f71f7 --- /dev/null +++ b/homepage/docker.yaml @@ -0,0 +1,6 @@ +# ============================================================================= +# HOMEPAGE DASHBOARD - Docker Configuration +# ============================================================================= + +my-docker: + socket: /var/run/docker.sock diff --git a/homepage/services.yaml b/homepage/services.yaml new file mode 100644 index 0000000..23e485a --- /dev/null +++ b/homepage/services.yaml @@ -0,0 +1,105 @@ +# ============================================================================= +# HOMEPAGE DASHBOARD - Services Configuration +# ============================================================================= + +- Smart Home: + - Home Assistant: + icon: home-assistant.svg + href: http://home.lan + description: Smart Home Zentrale + server: my-docker + container: homeassistant + widget: + type: homeassistant + url: http://192.168.1.50:8123 + key: YOUR_HA_TOKEN + +- Netzwerk: + - AdGuard Home: + icon: adguard-home.svg + href: http://adguard.lan + description: DNS & Werbeblocker + server: my-docker + container: adguard + widget: + type: adguard + url: http://192.168.1.50:8080 + username: admin + password: YOUR_PASSWORD + + - Nginx Proxy Manager: + icon: nginx-proxy-manager.svg + href: http://192.168.1.50:81 + description: Reverse Proxy GUI + server: my-docker + container: nginx-proxy-manager + +- Remote Access: + - RustDesk: + icon: rustdesk.svg + href: http://rustdesk.lan + description: Remote Desktop Server + server: my-docker + container: rustdesk-hbbs + + - Portainer: + icon: portainer.svg + href: http://portainer.lan + description: Docker Management + server: my-docker + container: portainer + widget: + type: portainer + url: https://192.168.1.50:9443 + env: 1 + key: YOUR_PORTAINER_KEY + +- AI & Voice: + - Whisper: + icon: openai.svg + href: http://whisper.lan + description: Sprache zu Text (GPU) + server: my-docker + container: whisper-webui + + - Ollama: + icon: ollama.svg + href: http://ollama.lan + description: Lokale LLMs + server: my-docker + container: ollama + + - Piper TTS: + icon: mdi-text-to-speech + href: "#" + description: Text zu Sprache + server: my-docker + container: piper + +- Media: + - Jellyfin: + icon: jellyfin.svg + href: http://jellyfin.lan + description: Media Server + server: my-docker + container: jellyfin + widget: + type: jellyfin + url: http://192.168.1.50:8096 + key: YOUR_JELLYFIN_KEY + enableBlocks: true + +- Cloud & Sync: + - FileBrowser: + icon: filebrowser.svg + href: http://files.lan + description: Private Cloud + server: my-docker + container: filebrowser + + - Syncthing: + icon: syncthing.svg + href: http://sync.lan + description: File Sync + server: my-docker + container: syncthing diff --git a/homepage/settings.yaml b/homepage/settings.yaml new file mode 100644 index 0000000..d97b094 --- /dev/null +++ b/homepage/settings.yaml @@ -0,0 +1,40 @@ +# ============================================================================= +# HOMEPAGE DASHBOARD - Settings +# ============================================================================= + +title: Jetson Home Hub +description: Dein lokales Smart Home & AI Center + +theme: dark +color: slate + +background: + image: https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=1920 + blur: sm + opacity: 30 + +cardBlur: md + +favicon: https://raw.githubusercontent.com/home-assistant/assets/master/favicon/favicon.ico + +layout: + Smart Home: + style: row + columns: 1 + Netzwerk: + style: row + columns: 2 + Remote Access: + style: row + columns: 2 + AI & Voice: + style: row + columns: 3 + Media: + style: row + columns: 1 + Cloud & Sync: + style: row + columns: 2 + +headerStyle: clean diff --git a/homepage/widgets.yaml b/homepage/widgets.yaml new file mode 100644 index 0000000..fa681c0 --- /dev/null +++ b/homepage/widgets.yaml @@ -0,0 +1,29 @@ +# ============================================================================= +# HOMEPAGE DASHBOARD - Widgets (System Info) +# ============================================================================= + +- logo: + icon: https://developer.nvidia.com/sites/default/files/akamai/embedded/images/jetson-orin-nano/jetson-orin-nano-front.png + +- greeting: + text_size: xl + text: Jetson Home Hub + +- datetime: + text_size: l + format: + dateStyle: long + timeStyle: short + hour12: false + +- resources: + cpu: true + memory: true + disk: / + cputemp: true + uptime: true + label: System + +- search: + provider: google + target: _blank diff --git a/infomaniak/.env.example b/infomaniak/.env.example new file mode 100644 index 0000000..f30ea3b --- /dev/null +++ b/infomaniak/.env.example @@ -0,0 +1,18 @@ +# ============================================================================= +# AUTHENTIK ENVIRONMENT +# Für Infomaniak Server (185.143.102.153) +# ============================================================================= + +# PostgreSQL +PG_PASS=CHANGE_ME_SECURE_PASSWORD_HERE + +# Authentik Secret Key (generieren mit: openssl rand 60 | base64 -w 0) +AUTHENTIK_SECRET_KEY=CHANGE_ME_RUN_openssl_rand_60_base64 + +# Optional: Email +# AUTHENTIK_EMAIL__HOST=smtp.example.com +# AUTHENTIK_EMAIL__PORT=587 +# AUTHENTIK_EMAIL__USERNAME= +# AUTHENTIK_EMAIL__PASSWORD= +# AUTHENTIK_EMAIL__USE_TLS=true +# AUTHENTIK_EMAIL__FROM=authentik@hofmanns.ai diff --git a/infomaniak/docker-compose.yml b/infomaniak/docker-compose.yml new file mode 100644 index 0000000..dd357fb --- /dev/null +++ b/infomaniak/docker-compose.yml @@ -0,0 +1,128 @@ +version: "3.8" + +# ============================================================================= +# INFOMANIAK SERVER - AUTH + RUSTDESK +# Für: 185.143.102.153 / hofmanns.ai +# ============================================================================= + +services: + # =========================================== + # AUTHENTIK - SSO mit Passkeys/WebAuthn + # =========================================== + postgresql: + container_name: authentik-postgres + image: docker.io/library/postgres:16-alpine + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + volumes: + - ./authentik/database:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: ${PG_PASS:?database password required} + POSTGRES_USER: authentik + POSTGRES_DB: authentik + + redis: + container_name: authentik-redis + image: docker.io/library/redis:alpine + command: --save 60 1 --loglevel warning + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "redis-cli ping | grep PONG"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 3s + volumes: + - ./authentik/redis:/data + + authentik-server: + container_name: authentik-server + image: ghcr.io/goauthentik/server:2024.2 + restart: unless-stopped + command: server + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + volumes: + - ./authentik/media:/media + - ./authentik/custom-templates:/templates + ports: + - "9000:9000" + - "9443:9443" + depends_on: + - postgresql + - redis + + authentik-worker: + container_name: authentik-worker + image: ghcr.io/goauthentik/server:2024.2 + restart: unless-stopped + command: worker + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: authentik + AUTHENTIK_POSTGRESQL__NAME: authentik + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./authentik/media:/media + - ./authentik/certs:/certs + - ./authentik/custom-templates:/templates + depends_on: + - postgresql + - redis + + # =========================================== + # RUSTDESK SERVER - Remote Desktop + # =========================================== + rustdesk-hbbs: + container_name: rustdesk-hbbs + image: rustdesk/rustdesk-server:latest + restart: unless-stopped + command: hbbs + ports: + - "21115:21115" + - "21116:21116" + - "21116:21116/udp" + - "21118:21118" + volumes: + - ./rustdesk:/root + + rustdesk-hbbr: + container_name: rustdesk-hbbr + image: rustdesk/rustdesk-server:latest + restart: unless-stopped + command: hbbr + ports: + - "21117:21117" + - "21119:21119" + volumes: + - ./rustdesk:/root + + # =========================================== + # NGINX - Reverse Proxy mit SSL + # =========================================== + nginx: + container_name: nginx + image: nginx:alpine + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/conf.d:/etc/nginx/conf.d:ro + - ./nginx/ssl:/etc/nginx/ssl:ro + - ./nginx/html:/usr/share/nginx/html:ro + depends_on: + - authentik-server diff --git a/infomaniak/nginx/conf.d/hofmanns.conf b/infomaniak/nginx/conf.d/hofmanns.conf new file mode 100644 index 0000000..88f7980 --- /dev/null +++ b/infomaniak/nginx/conf.d/hofmanns.conf @@ -0,0 +1,82 @@ +# ============================================================================= +# NGINX CONFIG - hofmanns.ai +# ============================================================================= + +# Authentik SSO +server { + listen 80; + server_name auth.hofmanns.ai; + + location / { + return 301 https://$server_name$request_uri; + } +} + +server { + listen 443 ssl http2; + server_name auth.hofmanns.ai; + + ssl_certificate /etc/nginx/ssl/hofmanns.ai.crt; + ssl_certificate_key /etc/nginx/ssl/hofmanns.ai.key; + + location / { + proxy_pass http://authentik-server:9000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} + +# RustDesk Web (optional) +server { + listen 80; + server_name rustdesk.hofmanns.ai; + + location / { + proxy_pass http://rustdesk-hbbs:21118; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} + +# Forward Auth für alle geschützten Services +# Beispiel für einen Service hinter Authentik +server { + listen 443 ssl http2; + server_name portal.hofmanns.ai; + + ssl_certificate /etc/nginx/ssl/hofmanns.ai.crt; + ssl_certificate_key /etc/nginx/ssl/hofmanns.ai.key; + + # Authentik Forward Auth + location /outpost.goauthentik.io { + proxy_pass http://authentik-server:9000/outpost.goauthentik.io; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + auth_request /outpost.goauthentik.io/auth/nginx; + error_page 401 = @goauthentik_proxy_signin; + + # Nach erfolgreicher Auth → weiterleiten zum Jetson + proxy_pass http://JETSON_TUNNEL_OR_IP:3001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + + location @goauthentik_proxy_signin { + internal; + add_header Set-Cookie $auth_cookie; + return 302 /outpost.goauthentik.io/start?rd=$request_uri; + } +} diff --git a/infomaniak/setup-infomaniak.sh b/infomaniak/setup-infomaniak.sh new file mode 100644 index 0000000..73cf504 --- /dev/null +++ b/infomaniak/setup-infomaniak.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# ============================================================================= +# INFOMANIAK SERVER SETUP +# Server: 185.143.102.153 (hofmanns.ai) +# Run as: debian user +# ============================================================================= + +set -e + +echo "==========================================" +echo " HOFMANNS.AI - INFOMANIAK SERVER SETUP" +echo "==========================================" + +# 1. Update System +echo "[1/6] System Update..." +sudo apt update && sudo apt upgrade -y + +# 2. Install Docker (falls noch nicht) +echo "[2/6] Docker Check..." +if ! command -v docker &> /dev/null; then + curl -fsSL https://get.docker.com | sudo sh + sudo usermod -aG docker debian + echo "Docker installed. Please logout and login again, then re-run this script." + exit 0 +fi + +# 3. Install Docker Compose +echo "[3/6] Docker Compose Check..." +if ! command -v docker-compose &> /dev/null; then + sudo apt install -y docker-compose-plugin +fi + +# 4. Create directories +echo "[4/6] Creating directories..." +mkdir -p ~/hofmanns-auth/{authentik,rustdesk,nginx/conf.d,nginx/ssl,nginx/html} + +# 5. Copy config files +echo "[5/6] Copying config files..." +cp docker-compose.yml ~/hofmanns-auth/ +cp .env.example ~/hofmanns-auth/.env +cp -r nginx/* ~/hofmanns-auth/nginx/ + +# 6. Generate secrets +echo "[6/6] Generating secrets..." +cd ~/hofmanns-auth + +# Generate Authentik secret +AUTHENTIK_SECRET=$(openssl rand 60 | base64 -w 0) +sed -i "s/CHANGE_ME_RUN_openssl_rand_60_base64/$AUTHENTIK_SECRET/" .env + +# Generate PostgreSQL password +PG_PASSWORD=$(openssl rand -base64 32) +sed -i "s/CHANGE_ME_SECURE_PASSWORD_HERE/$PG_PASSWORD/" .env + +echo "" +echo "==========================================" +echo " SETUP COMPLETE!" +echo "==========================================" +echo "" +echo " NEXT STEPS:" +echo "" +echo " 1. SSL Zertifikate erstellen (Let's Encrypt):" +echo " sudo certbot certonly --standalone -d auth.hofmanns.ai" +echo " sudo cp /etc/letsencrypt/live/auth.hofmanns.ai/* ~/hofmanns-auth/nginx/ssl/" +echo "" +echo " 2. Services starten:" +echo " cd ~/hofmanns-auth" +echo " docker compose up -d" +echo "" +echo " 3. Authentik Initial Setup:" +echo " https://auth.hofmanns.ai/if/flow/initial-setup/" +echo "" +echo " 4. RustDesk Key anzeigen:" +echo " cat ~/hofmanns-auth/rustdesk/id_ed25519.pub" +echo "" +echo " PORTS:" +echo " - 80/443 → Nginx (Authentik Proxy)" +echo " - 9000 → Authentik (intern)" +echo " - 21115-21119 → RustDesk" +echo "" diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..804205c --- /dev/null +++ b/setup.sh @@ -0,0 +1,172 @@ +#!/bin/bash +# ============================================================================= +# JETSON ORIN NANO 8GB - COMPLETE SETUP SCRIPT +# Run this ONCE after JetPack flash +# ============================================================================= + +set -e + +USER="d" +WIFI_SSID="Home.8" +WIFI_PASS="123" +STATIC_IP="192.168.1.50" +GATEWAY="192.168.1.1" + +echo "==========================================" +echo " JETSON ORIN NANO - HOME HUB SETUP" +echo "==========================================" + +# 1. System Update +echo "[1/10] System Update..." +sudo apt update && sudo apt upgrade -y + +# 2. Install required packages +echo "[2/10] Installing packages..." +sudo apt install -y \ + docker.io docker-compose \ + hostapd bridge-utils \ + git curl wget htop \ + python3-pip \ + alsa-utils pulseaudio \ + ffmpeg \ + nvtop + +# 3. Add user to docker group +echo "[3/10] Docker permissions..." +sudo usermod -aG docker $USER + +# 4. Create swap (8GB for Whisper models) +echo "[4/10] Creating 8GB swap..." +sudo fallocate -l 8G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab + +# 5. Set static IP for eth0 +echo "[5/10] Network configuration..." +sudo tee /etc/netplan/01-netcfg.yaml << EOF +network: + version: 2 + renderer: NetworkManager + ethernets: + eth0: + addresses: + - ${STATIC_IP}/24 + routes: + - to: default + via: ${GATEWAY} + nameservers: + addresses: + - 127.0.0.1 + - 1.1.1.1 +EOF + +# 6. Disable NetworkManager for wlan (we use hostapd) +echo "[6/10] Disable NetworkManager for wlan..." +sudo tee /etc/NetworkManager/conf.d/99-unmanaged-wlan.conf << EOF +[keyfile] +unmanaged-devices=interface-name:wlan* +EOF + +# 7. Setup Bridge for AP +echo "[7/10] Bridge configuration..." +sudo tee /etc/network/interfaces.d/br0 << EOF +auto br0 +iface br0 inet dhcp + bridge_ports eth0 + bridge_stp off + bridge_fd 0 +EOF + +# 8. hostapd configuration (Bridge Mode - NO NAT!) +echo "[8/10] hostapd configuration..." +sudo tee /etc/hostapd/hostapd.conf << EOF +interface=wlan0 +bridge=br0 +driver=nl80211 +ssid=${WIFI_SSID} +hw_mode=a +channel=36 +wmm_enabled=1 +macaddr_acl=0 +auth_algs=1 +ignore_broadcast_ssid=0 +wpa=2 +wpa_passphrase=${WIFI_PASS} +wpa_key_mgmt=WPA-PSK +rsn_pairwise=CCMP +country_code=CH + +# 802.11n/ac settings +ieee80211n=1 +ieee80211ac=1 +ht_capab=[HT40+][SHORT-GI-20][SHORT-GI-40] +vht_capab=[SHORT-GI-80][MAX-MPDU-11454] +vht_oper_chwidth=1 +vht_oper_centr_freq_seg0_idx=42 +EOF + +sudo sed -i 's|#DAEMON_CONF=""|DAEMON_CONF="/etc/hostapd/hostapd.conf"|' /etc/default/hostapd + +# 9. Enable services +echo "[9/10] Enable services..." +sudo systemctl unmask hostapd +sudo systemctl enable hostapd +sudo systemctl enable docker + +# 10. Create docker directory structure +echo "[10/10] Creating directory structure..." +mkdir -p /home/$USER/docker/{homeassistant,adguard,whisper,jellyfin,portainer,filebrowser,syncthing,ollama,piper,rustdesk,homepage,npm} +mkdir -p /home/$USER/docker/homeassistant/config +mkdir -p /home/$USER/docker/adguard/{work,conf} +mkdir -p /home/$USER/docker/whisper/{models,wyoming} +mkdir -p /home/$USER/docker/jellyfin/{config,cache} +mkdir -p /home/$USER/docker/npm/{data,letsencrypt} +mkdir -p /home/$USER/docker/syncthing +mkdir -p /home/$USER/sync + +# Copy all config files +cp /home/$USER/jetson-setup/docker-compose.yml /home/$USER/docker/ +cp -r /home/$USER/jetson-setup/homepage/* /home/$USER/docker/homepage/ +cp -r /home/$USER/jetson-setup/whisper-webui-jetson /home/$USER/docker/ + +# Create filebrowser database +touch /home/$USER/docker/filebrowser/database.db + +# Install Cockpit for system management (Web UI) +sudo apt install -y cockpit + +# Set permissions +chown -R $USER:$USER /home/$USER/docker +chown -R $USER:$USER /home/$USER/sync + +echo "" +echo "==========================================" +echo " SETUP COMPLETE!" +echo "==========================================" +echo "" +echo " NEXT STEPS:" +echo " 1. REBOOT: sudo reboot" +echo " 2. After reboot, start services:" +echo " cd ~/docker && docker-compose up -d" +echo "" +echo " WEB UIs (alle im Browser!):" +echo " ─────────────────────────────────────────" +echo " http://192.168.1.50:3001 → Homepage Dashboard" +echo " http://192.168.1.50:81 → Nginx Proxy Manager" +echo " http://192.168.1.50:9443 → Portainer (Docker)" +echo " http://192.168.1.50:9090 → Cockpit (System)" +echo " http://192.168.1.50:8123 → Home Assistant" +echo " http://192.168.1.50:3000 → AdGuard Setup" +echo " http://192.168.1.50:7860 → Whisper (Speech→Text)" +echo " http://192.168.1.50:8096 → Jellyfin (Media)" +echo " http://192.168.1.50:8081 → FileBrowser (Cloud)" +echo " http://192.168.1.50:8384 → Syncthing" +echo "" +echo " RUSTDESK Server: 192.168.1.50" +echo " → Ports: 21115-21119" +echo " → Key: cat ~/docker/rustdesk/id_ed25519.pub" +echo "" +echo " WiFi AP: ${WIFI_SSID} (CHANGE PASSWORD!)" +echo "" diff --git a/whisper-webui-jetson/Dockerfile b/whisper-webui-jetson/Dockerfile new file mode 100644 index 0000000..a2b9dd7 --- /dev/null +++ b/whisper-webui-jetson/Dockerfile @@ -0,0 +1,16 @@ +# Jetson Orin Nano optimized Whisper WebUI +FROM dustynv/whisper:r36.2.0 + +WORKDIR /app + +# Install Gradio +RUN pip3 install --no-cache-dir gradio scipy + +# Copy app +COPY app.py /app/app.py + +EXPOSE 7860 + +ENV WHISPER_MODEL=base + +CMD ["python3", "/app/app.py"] diff --git a/whisper-webui-jetson/app.py b/whisper-webui-jetson/app.py new file mode 100644 index 0000000..7eec8d1 --- /dev/null +++ b/whisper-webui-jetson/app.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Whisper Web UI - Browser-based Speech-to-Text +Runs locally on Jetson Orin Nano with GPU acceleration +""" + +import gradio as gr +import whisper +import torch +import os +import tempfile + +# Load model (use GPU if available) +device = "cuda" if torch.cuda.is_available() else "cpu" +print(f"Using device: {device}") + +MODEL_SIZE = os.getenv("WHISPER_MODEL", "base") +print(f"Loading Whisper model: {MODEL_SIZE}") +model = whisper.load_model(MODEL_SIZE, device=device) +print("Model loaded!") + + +def transcribe_audio(audio_path, language="auto", task="transcribe"): + """Transcribe audio file using Whisper""" + if audio_path is None: + return "No audio provided" + + try: + # Transcribe + options = { + "task": task, + "fp16": device == "cuda" + } + + if language != "auto": + options["language"] = language + + result = model.transcribe(audio_path, **options) + + # Format output + text = result["text"] + detected_lang = result.get("language", "unknown") + + output = f"**Detected Language:** {detected_lang}\n\n" + output += f"**Transcription:**\n{text}\n\n" + + # Add segments with timestamps + output += "**Segments:**\n" + for segment in result["segments"]: + start = segment["start"] + end = segment["end"] + segment_text = segment["text"] + output += f"[{start:.2f}s - {end:.2f}s] {segment_text}\n" + + return output + + except Exception as e: + return f"Error: {str(e)}" + + +def transcribe_microphone(audio, language="auto", task="transcribe"): + """Transcribe from microphone input""" + if audio is None: + return "No audio recorded" + + # audio is a tuple (sample_rate, numpy_array) from gradio + sr, audio_data = audio + + # Save to temp file + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f: + import scipy.io.wavfile as wav + wav.write(f.name, sr, audio_data) + return transcribe_audio(f.name, language, task) + + +# Language options +LANGUAGES = [ + ("Auto Detect", "auto"), + ("German", "de"), + ("English", "en"), + ("French", "fr"), + ("Italian", "it"), + ("Spanish", "es"), + ("Portuguese", "pt"), + ("Dutch", "nl"), + ("Russian", "ru"), + ("Chinese", "zh"), + ("Japanese", "ja"), + ("Korean", "ko"), +] + +# Create Gradio interface +with gr.Blocks(title="Whisper Local - Speech to Text", theme=gr.themes.Soft()) as demo: + gr.Markdown(""" + # 🎤 Whisper Local - Speech to Text + ### Runs 100% locally on Jetson Orin Nano with GPU acceleration + No cloud, no internet required, your audio stays private. + """) + + with gr.Tabs(): + # Tab 1: Microphone + with gr.TabItem("🎙️ Microphone"): + gr.Markdown("Record audio directly from your microphone") + + with gr.Row(): + mic_input = gr.Audio( + sources=["microphone"], + type="numpy", + label="Record Audio" + ) + + with gr.Row(): + mic_language = gr.Dropdown( + choices=LANGUAGES, + value="auto", + label="Language" + ) + mic_task = gr.Radio( + choices=["transcribe", "translate"], + value="transcribe", + label="Task (translate = to English)" + ) + + mic_button = gr.Button("🚀 Transcribe", variant="primary") + mic_output = gr.Markdown(label="Result") + + mic_button.click( + fn=transcribe_microphone, + inputs=[mic_input, mic_language, mic_task], + outputs=mic_output + ) + + # Tab 2: File Upload + with gr.TabItem("📁 File Upload"): + gr.Markdown("Upload an audio or video file") + + with gr.Row(): + file_input = gr.Audio( + sources=["upload"], + type="filepath", + label="Upload Audio/Video" + ) + + with gr.Row(): + file_language = gr.Dropdown( + choices=LANGUAGES, + value="auto", + label="Language" + ) + file_task = gr.Radio( + choices=["transcribe", "translate"], + value="transcribe", + label="Task (translate = to English)" + ) + + file_button = gr.Button("🚀 Transcribe", variant="primary") + file_output = gr.Markdown(label="Result") + + file_button.click( + fn=transcribe_audio, + inputs=[file_input, file_language, file_task], + outputs=file_output + ) + + gr.Markdown(f""" + --- + **Model:** {MODEL_SIZE} | **Device:** {device} | **GPU:** {'✅ CUDA' if device == 'cuda' else '❌ CPU'} + """) + + +if __name__ == "__main__": + demo.launch( + server_name="0.0.0.0", + server_port=7860, + share=False, + ssl_verify=False + ) diff --git a/whisper-webui/Dockerfile b/whisper-webui/Dockerfile new file mode 100644 index 0000000..9db18a4 --- /dev/null +++ b/whisper-webui/Dockerfile @@ -0,0 +1,24 @@ +FROM nvcr.io/nvidia/l4t-pytorch:r35.2.1-pth2.0-py3 + +WORKDIR /app + +# Install dependencies +RUN apt-get update && apt-get install -y \ + ffmpeg \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Install Python packages +RUN pip3 install --no-cache-dir \ + openai-whisper \ + gradio \ + numpy \ + torch \ + torchaudio + +# Create the web interface +COPY app.py /app/app.py + +EXPOSE 7860 + +CMD ["python3", "app.py"] diff --git a/whisper-webui/app.py b/whisper-webui/app.py new file mode 100644 index 0000000..7eec8d1 --- /dev/null +++ b/whisper-webui/app.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Whisper Web UI - Browser-based Speech-to-Text +Runs locally on Jetson Orin Nano with GPU acceleration +""" + +import gradio as gr +import whisper +import torch +import os +import tempfile + +# Load model (use GPU if available) +device = "cuda" if torch.cuda.is_available() else "cpu" +print(f"Using device: {device}") + +MODEL_SIZE = os.getenv("WHISPER_MODEL", "base") +print(f"Loading Whisper model: {MODEL_SIZE}") +model = whisper.load_model(MODEL_SIZE, device=device) +print("Model loaded!") + + +def transcribe_audio(audio_path, language="auto", task="transcribe"): + """Transcribe audio file using Whisper""" + if audio_path is None: + return "No audio provided" + + try: + # Transcribe + options = { + "task": task, + "fp16": device == "cuda" + } + + if language != "auto": + options["language"] = language + + result = model.transcribe(audio_path, **options) + + # Format output + text = result["text"] + detected_lang = result.get("language", "unknown") + + output = f"**Detected Language:** {detected_lang}\n\n" + output += f"**Transcription:**\n{text}\n\n" + + # Add segments with timestamps + output += "**Segments:**\n" + for segment in result["segments"]: + start = segment["start"] + end = segment["end"] + segment_text = segment["text"] + output += f"[{start:.2f}s - {end:.2f}s] {segment_text}\n" + + return output + + except Exception as e: + return f"Error: {str(e)}" + + +def transcribe_microphone(audio, language="auto", task="transcribe"): + """Transcribe from microphone input""" + if audio is None: + return "No audio recorded" + + # audio is a tuple (sample_rate, numpy_array) from gradio + sr, audio_data = audio + + # Save to temp file + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f: + import scipy.io.wavfile as wav + wav.write(f.name, sr, audio_data) + return transcribe_audio(f.name, language, task) + + +# Language options +LANGUAGES = [ + ("Auto Detect", "auto"), + ("German", "de"), + ("English", "en"), + ("French", "fr"), + ("Italian", "it"), + ("Spanish", "es"), + ("Portuguese", "pt"), + ("Dutch", "nl"), + ("Russian", "ru"), + ("Chinese", "zh"), + ("Japanese", "ja"), + ("Korean", "ko"), +] + +# Create Gradio interface +with gr.Blocks(title="Whisper Local - Speech to Text", theme=gr.themes.Soft()) as demo: + gr.Markdown(""" + # 🎤 Whisper Local - Speech to Text + ### Runs 100% locally on Jetson Orin Nano with GPU acceleration + No cloud, no internet required, your audio stays private. + """) + + with gr.Tabs(): + # Tab 1: Microphone + with gr.TabItem("🎙️ Microphone"): + gr.Markdown("Record audio directly from your microphone") + + with gr.Row(): + mic_input = gr.Audio( + sources=["microphone"], + type="numpy", + label="Record Audio" + ) + + with gr.Row(): + mic_language = gr.Dropdown( + choices=LANGUAGES, + value="auto", + label="Language" + ) + mic_task = gr.Radio( + choices=["transcribe", "translate"], + value="transcribe", + label="Task (translate = to English)" + ) + + mic_button = gr.Button("🚀 Transcribe", variant="primary") + mic_output = gr.Markdown(label="Result") + + mic_button.click( + fn=transcribe_microphone, + inputs=[mic_input, mic_language, mic_task], + outputs=mic_output + ) + + # Tab 2: File Upload + with gr.TabItem("📁 File Upload"): + gr.Markdown("Upload an audio or video file") + + with gr.Row(): + file_input = gr.Audio( + sources=["upload"], + type="filepath", + label="Upload Audio/Video" + ) + + with gr.Row(): + file_language = gr.Dropdown( + choices=LANGUAGES, + value="auto", + label="Language" + ) + file_task = gr.Radio( + choices=["transcribe", "translate"], + value="transcribe", + label="Task (translate = to English)" + ) + + file_button = gr.Button("🚀 Transcribe", variant="primary") + file_output = gr.Markdown(label="Result") + + file_button.click( + fn=transcribe_audio, + inputs=[file_input, file_language, file_task], + outputs=file_output + ) + + gr.Markdown(f""" + --- + **Model:** {MODEL_SIZE} | **Device:** {device} | **GPU:** {'✅ CUDA' if device == 'cuda' else '❌ CPU'} + """) + + +if __name__ == "__main__": + demo.launch( + server_name="0.0.0.0", + server_port=7860, + share=False, + ssl_verify=False + ) diff --git a/whisper-webui/requirements.txt b/whisper-webui/requirements.txt new file mode 100644 index 0000000..6eb3cf6 --- /dev/null +++ b/whisper-webui/requirements.txt @@ -0,0 +1,6 @@ +openai-whisper +gradio>=4.0.0 +torch +torchaudio +numpy +scipy