Initial implementation: Bordanlage boat onboard system
Complete multiroom audio + navigation dashboard: - Docker stack: SignalK, Snapcast (4 zones), librespot, shairport-sync, Mopidy, Jellyfin, Portainer - React 18 + Vite dashboard with nautical dark theme - Full mock system (SignalK NMEA simulation, Snapcast zones, Mopidy player) - Real API clients for all services with reconnect logic - SVG instruments: Compass, WindRose, Gauge, DepthSounder, SpeedLog - Pages: Overview, Navigation, Audio (zones/radio/library), Systems - Dev mode runs fully without hardware (make dev) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
482
plan.md
Normal file
482
plan.md
Normal file
@@ -0,0 +1,482 @@
|
||||
# Bordanlage – Vollständiges Multiroom Audio + Bootsdaten Dashboard
|
||||
|
||||
## Projektbeschreibung
|
||||
|
||||
Baue ein vollständiges, produktionsreifes System namens `bordanlage` für ein Boot.
|
||||
Es besteht aus drei Teilen:
|
||||
|
||||
1. **Docker-Stack** – alle Backend-Dienste (Audio, Navigation, Management)
|
||||
2. **React-Dashboard** – ein modernes, touch-optimiertes Web-UI für Touchscreen-Display
|
||||
3. **Dev-Modus** – vollständig ohne Hardware lauffähig (Mac & Windows), mit simulierten Daten
|
||||
|
||||
---
|
||||
|
||||
## Technologie-Stack
|
||||
|
||||
### Backend (Docker)
|
||||
- **Snapcast** (snapserver + snapclient) → synchrones Multiroom-Audio, 4 Zonen
|
||||
- **librespot** → Spotify Connect (echter Endpunkt, erscheint in Spotify App)
|
||||
- **shairport-sync** → AirPlay 2 Empfänger (erscheint auf iPhone/Mac)
|
||||
- **Mopidy** + Plugins → Web Radio (HTTP-Streams) + lokale Musikbibliothek
|
||||
- **Jellyfin** → Mediathek (Musik, Hörbücher, Videos von Festplatte/USB)
|
||||
- **SignalK** → NMEA 2000 / NMEA 0183 Gateway (Bootsdaten)
|
||||
- **Portainer** → Docker Management UI
|
||||
- **Nginx** → Reverse Proxy, served das Dashboard
|
||||
|
||||
### Frontend (React + Vite)
|
||||
- React 18 + Vite
|
||||
- Keine UI-Bibliothek – eigenes Design-System
|
||||
- WebSocket-Verbindung zu SignalK für Live-Bootsdaten
|
||||
- Snapcast JSON-RPC API für Zonen-Steuerung
|
||||
- Mopidy JSON-RPC API für Musik-Steuerung
|
||||
- Jellyfin REST API für Mediathek
|
||||
|
||||
---
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
Erstelle folgende Verzeichnisstruktur:
|
||||
|
||||
```
|
||||
bordanlage/
|
||||
├── docker-compose.yml # Produktion (Boot)
|
||||
├── docker-compose.dev.yml # Development Override (Mac/Windows)
|
||||
├── docker-compose.override.yml # Symlink → dev wenn DEV=true
|
||||
├── .env.example
|
||||
├── .env
|
||||
├── Makefile # make dev / make boot / make stop / make logs
|
||||
├── README.md
|
||||
│
|
||||
├── config/
|
||||
│ ├── snapserver.conf
|
||||
│ ├── mopidy.conf
|
||||
│ ├── shairport.conf
|
||||
│ └── nginx/
|
||||
│ └── default.conf
|
||||
│
|
||||
├── dashboard/ # React App
|
||||
│ ├── package.json
|
||||
│ ├── vite.config.js
|
||||
│ ├── index.html
|
||||
│ └── src/
|
||||
│ ├── main.jsx
|
||||
│ ├── App.jsx
|
||||
│ ├── api/
|
||||
│ │ ├── snapcast.js # Snapcast JSON-RPC Client
|
||||
│ │ ├── mopidy.js # Mopidy JSON-RPC Client
|
||||
│ │ ├── signalk.js # SignalK WebSocket Client
|
||||
│ │ └── jellyfin.js # Jellyfin REST Client
|
||||
│ ├── mock/
|
||||
│ │ ├── index.js # Mock-Router: echte API wenn prod, fake wenn dev
|
||||
│ │ ├── signalk.mock.js # Simulierte NMEA-Daten mit realistischen Werten
|
||||
│ │ ├── snapcast.mock.js # Simulierte Zonen, Lautstärke, Quellen
|
||||
│ │ └── mopidy.mock.js # Simulierte Tracks, Radio, Wiedergabe
|
||||
│ ├── hooks/
|
||||
│ │ ├── useNMEA.js # Hook: NMEA-Daten (echt oder mock)
|
||||
│ │ ├── useZones.js # Hook: Snapcast Zonen
|
||||
│ │ ├── usePlayer.js # Hook: Wiedergabe-Steuerung
|
||||
│ │ └── useDocker.js # Hook: Container-Status via Portainer API
|
||||
│ ├── components/
|
||||
│ │ ├── layout/
|
||||
│ │ │ ├── TopBar.jsx
|
||||
│ │ │ └── TabNav.jsx
|
||||
│ │ ├── instruments/
|
||||
│ │ │ ├── Gauge.jsx # Analoges Rundinstrument (SVG)
|
||||
│ │ │ ├── Compass.jsx # Kompassrose (SVG, animiert)
|
||||
│ │ │ ├── WindRose.jsx # Windrose (SVG, animiert)
|
||||
│ │ │ ├── DepthSounder.jsx # Tiefenmesser mit Warngrenze
|
||||
│ │ │ └── SpeedLog.jsx # Fahrtmesser
|
||||
│ │ ├── audio/
|
||||
│ │ │ ├── NowPlaying.jsx # Track-Info, Cover, Playback-Controls
|
||||
│ │ │ ├── ZoneCard.jsx # Eine Snapcast-Zone
|
||||
│ │ │ ├── ZoneGrid.jsx # Alle Zonen
|
||||
│ │ │ ├── SourcePicker.jsx # Quelle wählen (Spotify/AirPlay/Radio/Jellyfin)
|
||||
│ │ │ ├── RadioBrowser.jsx # Senderliste mit Suche
|
||||
│ │ │ └── LibraryBrowser.jsx # Jellyfin Mediathek
|
||||
│ │ ├── nav/
|
||||
│ │ │ ├── ChartPlaceholder.jsx # Seekarten-Iframe (OpenCPN/SignalK)
|
||||
│ │ │ └── InstrumentPanel.jsx
|
||||
│ │ └── systems/
|
||||
│ │ ├── BatteryStatus.jsx
|
||||
│ │ ├── EngineData.jsx
|
||||
│ │ └── ServiceHealth.jsx # Docker-Container Status
|
||||
│ └── pages/
|
||||
│ ├── Overview.jsx # Tab 1: Instrumente + Now Playing + Zonen
|
||||
│ ├── Navigation.jsx # Tab 2: Seekarte + alle Navigationsdaten
|
||||
│ ├── Audio.jsx # Tab 3: Vollständige Audio-Steuerung
|
||||
│ └── Systems.jsx # Tab 4: Batterien, Motor, Docker-Status
|
||||
│
|
||||
└── scripts/
|
||||
├── init-pipes.sh # Named Pipes anlegen
|
||||
├── setup-dev.sh # Erstkonfiguration Entwicklung
|
||||
└── setup-boot.sh # Erstkonfiguration echtes Boot
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Detaillierte Anforderungen
|
||||
|
||||
### 1. Docker Compose – Produktion (`docker-compose.yml`)
|
||||
|
||||
Definiere folgende Services:
|
||||
|
||||
**signalk:**
|
||||
- Image: `signalk/signalk-server:latest`
|
||||
- Port: 3000
|
||||
- Volume: signalk-data
|
||||
- Kommentar für NMEA-Adapter: `/dev/ttyUSB0` (auskommentiert)
|
||||
|
||||
**librespot (Spotify Connect):**
|
||||
- Image: `ghcr.io/librespot-org/librespot:latest`
|
||||
- Umgebungsvariablen aus `.env`: `SPOTIFY_NAME`, `SPOTIFY_BITRATE` (default: 320)
|
||||
- Backend: `pipe`, Output: `/tmp/audio/spotify.pcm`
|
||||
- Volume: pipes
|
||||
- WICHTIG: Der Service muss wirklich in der Spotify App als Gerät erscheinen.
|
||||
Stelle sicher, dass `network_mode: host` auf Linux gesetzt ist (Boot).
|
||||
Auf Mac/Windows alternative Port-Mappings verwenden.
|
||||
|
||||
**shairport-sync (AirPlay):**
|
||||
- Image: `mikebrady/shairport-sync:latest`
|
||||
- Config: `./config/shairport.conf` (Output auf `/tmp/audio/airplay.pcm`)
|
||||
- Avahi/mDNS: Auf Linux `network_mode: host`. Auf Mac/Windows eigenen
|
||||
avahi-daemon Container (`hauscontribs/avahi`) bereitstellen damit
|
||||
AirPlay discoverable ist.
|
||||
- Volume: pipes
|
||||
|
||||
**mopidy:**
|
||||
- Image: `ghcr.io/mopidy/mopidy:latest`
|
||||
- Port: 6680
|
||||
- Plugins installieren via Custom-Dockerfile in `./docker/mopidy/`:
|
||||
```
|
||||
mopidy-iris
|
||||
mopidy-local
|
||||
mopidy-stream
|
||||
mopidy-tunein (Web Radio Suche)
|
||||
mopidy-podcast
|
||||
```
|
||||
- Audio-Output via GStreamer-Pipeline auf `/tmp/audio/mopidy.pcm`
|
||||
- Volumes: pipes, mopidy-data, music
|
||||
|
||||
**jellyfin:**
|
||||
- Image: `jellyfin/jellyfin:latest`
|
||||
- Port: 8096
|
||||
- Volumes: jellyfin-config, jellyfin-cache, music (read-only)
|
||||
- Hardware-Decoding: `/dev/dri` auskommentiert für Boot
|
||||
|
||||
**snapserver:**
|
||||
- Image: `ghcr.io/badaix/snapcast:latest`
|
||||
- Ports: 1704 (Protokoll), 1705 (Control API), 1780 (Snapweb)
|
||||
- Config: `./config/snapserver.conf`
|
||||
- 3 Streams: Spotify, AirPlay, Mopidy
|
||||
- depends_on: librespot, mopidy
|
||||
|
||||
**zone-salon, zone-cockpit, zone-bug, zone-heck:**
|
||||
- Je ein `ghcr.io/badaix/snapcast:latest` Container als snapclient
|
||||
- `--host snapserver --hostID <zonename>`
|
||||
- Auf dem Boot: `--soundcard hw:N,0` und `/dev/snd` device
|
||||
- Im Dev-Modus: Ohne Soundkarte (Audio wird simuliert, kein Fehler)
|
||||
|
||||
**portainer:**
|
||||
- Image: `portainer/portainer-ce:latest`
|
||||
- Port: 9000
|
||||
- Volume: docker.sock + portainer-data
|
||||
|
||||
**dashboard:**
|
||||
- Custom Dockerfile in `./dashboard/`
|
||||
- Nginx serviert den gebauten React-Build
|
||||
- Port: 8080
|
||||
- Env-Variablen: alle API-URLs
|
||||
|
||||
### 2. Docker Compose Dev Override (`docker-compose.dev.yml`)
|
||||
|
||||
Überschreibt für lokale Entwicklung ohne Hardware:
|
||||
|
||||
- **signalk**: Zusätzliche Umgebungsvariable `SIGNALK_DEMO=true` →
|
||||
SignalK generiert dann selbst Demo-NMEA-Daten (eingebautes Feature!)
|
||||
|
||||
- **snapclients**: Alle 4 Zonen erhalten `command: snapclient --host snapserver --hostID <name> --player null`
|
||||
(Null-Player: kein Audio-Output, kein Fehler)
|
||||
|
||||
- **dashboard**: Statt gebautem Nginx → Vite Dev-Server mit Hot Reload
|
||||
(`node:20-alpine`, `npm run dev`, Port 8080)
|
||||
|
||||
- **librespot**: `LIBRESPOT_DISABLE_DISCOVERY=false` → erscheint trotzdem in Spotify
|
||||
(funktioniert über TCP auch ohne host network, solange Port 57621 gemappt)
|
||||
|
||||
- **shairport**: avahi-container für mDNS-Discovery auf Mac/Windows
|
||||
|
||||
### 3. `.env` Konfiguration
|
||||
|
||||
```env
|
||||
# Allgemein
|
||||
COMPOSE_PROJECT_NAME=bordanlage
|
||||
DEV=true
|
||||
|
||||
# Spotify Connect
|
||||
SPOTIFY_NAME=Bordanlage
|
||||
SPOTIFY_BITRATE=320
|
||||
SPOTIFY_CACHE_SIZE=1024
|
||||
|
||||
# Boot-Info
|
||||
BOAT_NAME=Meine Yacht
|
||||
BOAT_MMSI=123456789
|
||||
|
||||
# Pfade (Musik, Logs)
|
||||
MUSIC_PATH=./music
|
||||
```
|
||||
|
||||
### 4. Makefile
|
||||
|
||||
```makefile
|
||||
dev:
|
||||
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
|
||||
|
||||
boot:
|
||||
docker compose up -d
|
||||
|
||||
stop:
|
||||
docker compose down
|
||||
|
||||
logs:
|
||||
docker compose logs -f
|
||||
|
||||
rebuild:
|
||||
docker compose build --no-cache
|
||||
|
||||
status:
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
### 5. Dashboard – Mock-System
|
||||
|
||||
Das Mock-System ist KRITISCH. Es muss folgendes können:
|
||||
|
||||
**`src/mock/index.js`:**
|
||||
Exportiert einen `createApi()`-Factory, der prüft ob `import.meta.env.DEV === true`.
|
||||
- Im Dev-Modus: gibt Mock-Implementierungen zurück
|
||||
- Im Prod-Modus: gibt echte API-Clients zurück
|
||||
|
||||
**`src/mock/signalk.mock.js`:**
|
||||
Simuliert einen SignalK WebSocket mit realistischen Bootsdaten.
|
||||
Werte ändern sich kontinuierlich und realistisch:
|
||||
- SOG: 4–7 Knoten, sanfte Schwankungen
|
||||
- COG/Heading: 200–230°, leichte Drift
|
||||
- Tiefe: 8–20m, langsame Änderung
|
||||
- Windgeschwindigkeit: 10–18 Knoten, böig
|
||||
- Windwinkel: 30–60° (am Wind), variabel
|
||||
- Motorendrehzahl: 1600–2000 RPM
|
||||
- Batterien: Starter 12.4–12.8V, Bord 24.8–25.4V
|
||||
- Wassertemperatur: 17–19°C
|
||||
- GPS: bewegt sich entlang einer realistischen Route (Ostsee-Koordinaten)
|
||||
|
||||
Gibt ein EventEmitter-ähnliches Objekt zurück, das `on('delta', callback)` unterstützt.
|
||||
Format: echtes SignalK Delta-Format (`{"updates": [{"values": [...]}]}`)
|
||||
|
||||
**`src/mock/snapcast.mock.js`:**
|
||||
Simuliert die Snapcast JSON-RPC API:
|
||||
- 4 Zonen (Salon, Cockpit, Bug, Heck)
|
||||
- Salon + Cockpit initial aktiv
|
||||
- Implementiert: `Server.GetStatus`, `Client.SetVolume`, `Client.SetMuted`,
|
||||
`Group.SetStream`, `Server.GetRPCVersion`
|
||||
|
||||
**`src/mock/mopidy.mock.js`:**
|
||||
Simuliert die Mopidy WebSocket JSON-RPC API:
|
||||
- Tracks mit realistischen Metadaten (Titel, Künstler, Album, Dauer)
|
||||
- Wiedergabestatus: playing/paused/stopped
|
||||
- Position läuft mit
|
||||
- Implementiert: `playback.get_current_track`, `playback.get_state`,
|
||||
`playback.get_time_position`, `playback.play`, `playback.pause`,
|
||||
`playback.next`, `playback.previous`, `tracklist.get_tracks`,
|
||||
`library.browse`, `library.search`
|
||||
|
||||
### 6. Dashboard – API-Clients
|
||||
|
||||
**`src/api/snapcast.js`:**
|
||||
Echter Snapcast JSON-RPC Client über WebSocket (`ws://host:1705`).
|
||||
Implementiert alle Methoden des Mock-Clients.
|
||||
Reconnect-Logik mit exponential backoff.
|
||||
|
||||
**`src/api/signalk.js`:**
|
||||
Echter SignalK WebSocket Client (`ws://host:3000/signalk/v1/stream`).
|
||||
Abonniert alle relevanten Pfade:
|
||||
- `navigation.speedOverGround`
|
||||
- `navigation.courseOverGroundTrue`
|
||||
- `navigation.headingTrue`
|
||||
- `environment.depth.belowKeel`
|
||||
- `environment.wind.speedApparent`
|
||||
- `environment.wind.angleApparent`
|
||||
- `propulsion.*.revolutions`
|
||||
- `electrical.batteries.*.voltage`
|
||||
|
||||
**`src/api/mopidy.js`:**
|
||||
Echter Mopidy JSON-RPC Client über WebSocket (`ws://host:6680/mopidy/ws`).
|
||||
|
||||
**`src/api/jellyfin.js`:**
|
||||
Jellyfin REST API Client. Authentifizierung via API-Key aus `.env`.
|
||||
Implementiert: Musik browsen, Alben, Künstler, Suche, Stream-URL.
|
||||
|
||||
### 7. Dashboard – Hooks
|
||||
|
||||
**`useNMEA()`:**
|
||||
Verbindet sich mit SignalK (echt oder mock).
|
||||
Gibt strukturiertes Objekt zurück:
|
||||
```js
|
||||
{
|
||||
sog, cog, heading, depth, windSpeed, windAngle, windDirection,
|
||||
lat, lon, rpm, battery1, battery2, waterTemp, airTemp, rudder, fuel
|
||||
}
|
||||
```
|
||||
|
||||
**`useZones()`:**
|
||||
Verbindet sich mit Snapcast.
|
||||
Gibt zurück:
|
||||
```js
|
||||
{
|
||||
zones, // Array aller Zonen mit {id, name, active, volume, source, muted}
|
||||
setVolume, // (zoneId, volume) => void
|
||||
setMuted, // (zoneId, muted) => void
|
||||
setSource, // (zoneId, streamId) => void
|
||||
toggleZone, // (zoneId) => void
|
||||
}
|
||||
```
|
||||
|
||||
**`usePlayer()`:**
|
||||
Verbindet sich mit Mopidy und Spotify-Status-API.
|
||||
Gibt zurück:
|
||||
```js
|
||||
{
|
||||
currentTrack, // {title, artist, album, duration, coverUrl}
|
||||
state, // 'playing' | 'paused' | 'stopped'
|
||||
position, // Sekunden
|
||||
activeSource, // 'spotify' | 'airplay' | 'mopidy' | 'jellyfin'
|
||||
play, pause, next, previous, seek
|
||||
}
|
||||
```
|
||||
|
||||
### 8. Dashboard – Design
|
||||
|
||||
**Farbschema:** Nautisch-dunkel. Hauptfarben:
|
||||
- Background: `#07111f` (tiefes Marineblau)
|
||||
- Surface: `#0a1928`
|
||||
- Border: `#1e2a3a`
|
||||
- Text primary: `#e2eaf2`
|
||||
- Text muted: `#4a6080`
|
||||
- Accent: `#38bdf8` (Cyan/Himmelblau)
|
||||
- Success: `#34d399`
|
||||
- Warning: `#f59e0b`
|
||||
- Danger: `#ef4444`
|
||||
- Spotify: `#1DB954`
|
||||
- AirPlay: `#60a5fa`
|
||||
|
||||
**Schriften:** `DM Mono` für Instrumentenwerte, `DM Sans` für UI-Text.
|
||||
|
||||
**Instrumente (SVG-basiert, animiert):**
|
||||
- Rundinstrumente mit Zeiger (Gauge.jsx): SOG, Tiefe, RPM, Wassertemp
|
||||
- Kompassrose (animiert, dreht sich): Heading
|
||||
- Windrose: Windrichtung + Geschwindigkeit
|
||||
- Alle Instrumente reagieren flüssig auf Daten-Updates (CSS transitions)
|
||||
|
||||
**Touch-Optimierung:**
|
||||
- Alle Buttons mindestens 44×44px
|
||||
- Volume Slider touch-fähig (pointer events)
|
||||
- Keine Hover-only Interaktionen
|
||||
|
||||
**Tabs:**
|
||||
- Übersicht: Alle Instrumente + Now Playing + Zonen-Schnellübersicht
|
||||
- Navigation: Seekarte (SignalK iframe / Placeholder) + Detaildaten
|
||||
- Audio: Vollständige Zonen-Steuerung + Quelle + Radio + Bibliothek
|
||||
- Systeme: Batterien, Motor, Kraftstoff, Container-Status
|
||||
|
||||
**DEV-Indikator:**
|
||||
Wenn `import.meta.env.DEV === true`: kleines Badge "DEV · MOCK DATA" in der TopBar.
|
||||
|
||||
### 9. Snapserver Konfiguration
|
||||
|
||||
```ini
|
||||
[stream]
|
||||
source = pipe:///tmp/audio/spotify.pcm?name=Spotify&codec=pcm&sampleformat=44100:16:2&chunk_ms=20
|
||||
source = pipe:///tmp/audio/airplay.pcm?name=AirPlay&codec=pcm&sampleformat=44100:16:2&chunk_ms=20
|
||||
source = pipe:///tmp/audio/mopidy.pcm?name=Mopidy&codec=pcm&sampleformat=44100:16:2&chunk_ms=20
|
||||
|
||||
[server]
|
||||
threads = -1
|
||||
|
||||
[http]
|
||||
enabled = true
|
||||
bind_to_address = 0.0.0.0
|
||||
port = 1780
|
||||
|
||||
[logging]
|
||||
sink = system
|
||||
filter = *:info
|
||||
```
|
||||
|
||||
### 10. Shairport-sync Konfiguration
|
||||
|
||||
```conf
|
||||
general = {
|
||||
name = "Bordanlage AirPlay";
|
||||
port = 5000;
|
||||
interpolation = "auto";
|
||||
output_backend = "pipe";
|
||||
};
|
||||
pipe = {
|
||||
name = "/tmp/audio/airplay.pcm";
|
||||
};
|
||||
```
|
||||
|
||||
### 11. README.md
|
||||
|
||||
Erstelle eine vollständige README mit:
|
||||
- Voraussetzungen (Docker Desktop, make)
|
||||
- Schnellstart (`make dev` → http://localhost:8080)
|
||||
- Alle Service-URLs
|
||||
- Anleitung: Wie erscheint der Spotify Connect Endpunkt?
|
||||
- Anleitung: Wie erscheint der AirPlay Endpunkt?
|
||||
- Anleitung: Wie füge ich Musik hinzu? (./music Ordner)
|
||||
- Anleitung: Wie verbinde ich echte NMEA-Hardware?
|
||||
- Migration Boot: Was muss geändert werden? (network_mode, soundcards, dri)
|
||||
- Troubleshooting (häufige Fehler auf Mac/Windows)
|
||||
|
||||
---
|
||||
|
||||
## Implementierungsreihenfolge
|
||||
|
||||
1. Projektstruktur und alle Konfigurationsdateien anlegen
|
||||
2. `docker-compose.yml` + `docker-compose.dev.yml` + `.env` + `Makefile`
|
||||
3. Mopidy Custom-Dockerfile mit Plugins
|
||||
4. Dashboard Grundgerüst (Vite + React Setup)
|
||||
5. Mock-System vollständig implementieren
|
||||
6. Echte API-Clients implementieren
|
||||
7. Hooks implementieren (useNMEA, useZones, usePlayer)
|
||||
8. UI-Komponenten (Instrumente, Audio, Navigation)
|
||||
9. Pages (Overview, Navigation, Audio, Systems)
|
||||
10. Dashboard Dockerfile + Nginx-Config
|
||||
11. README.md
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Hinweise für die Implementierung
|
||||
|
||||
- **Spotify Connect** funktioniert über mDNS. Auf Mac/Windows muss Port 57621 (UDP)
|
||||
gemappt sein und `--zeroconf-port 57621` an librespot übergeben werden.
|
||||
Alternativ: Spotify-Nutzer kann den Endpunkt auch manuell über "Gerät verbinden"
|
||||
mit der IP des Computers finden.
|
||||
|
||||
- **AirPlay** braucht Bonjour/mDNS. Auf Mac läuft Bonjour nativ, daher kann
|
||||
shairport-sync mit `network_mode: host` auf Mac funktionieren wenn
|
||||
`--host` Netzwerk erlaubt ist. Auf Windows WSL2 muss avahi im Container laufen.
|
||||
|
||||
- **Named Pipes** müssen VOR dem Start existieren. Das `init-pipes.sh` Skript
|
||||
legt sie an. In Docker: via `entrypoint` sicherstellen dass die Pipes existieren
|
||||
bevor der eigentliche Prozess startet.
|
||||
|
||||
- **Multiroom** bedeutet: alle aktiven Snapclients spielen synchron denselben Stream.
|
||||
Jede Zone kann aber einen anderen Stream (Quelle) zugewiesen bekommen.
|
||||
|
||||
- **Fehlertoleranz**: Wenn eine API nicht erreichbar ist (z.B. SignalK offline),
|
||||
soll das Dashboard nicht abstürzen – stattdessen graceful degradation mit
|
||||
"Nicht verbunden" Anzeige.
|
||||
|
||||
Starte jetzt mit der Implementierung. Beginne mit der Verzeichnisstruktur und
|
||||
arbeite dich dann systematisch durch alle Komponenten.
|
||||
Reference in New Issue
Block a user