This commit is contained in:
2026-01-12 19:03:50 +00:00
parent 0da76927ce
commit d4b8dcd455
48 changed files with 1352 additions and 723 deletions
+34 -34
View File
@@ -1,31 +1,31 @@
# Klangkiste Dev Playbook
# Klangkiste Dev-Playbook
## Purpose
This playbook documents the "virtual box" development environment. It is a complete, hardware-free simulation that runs entirely inside the repo and lets you test setup, playback, and API flows end-to-end.
## Zweck
Dieses Playbook dokumentiert die "virtuelle Box"-Entwicklungsumgebung. Es ist eine vollstaendige, hardwarefreie Simulation, die komplett im Repo laeuft und Setup-, Playback- und API-Flows end-to-end testbar macht.
Docs live in the repo-level `docs/` to keep all dev guidance in one predictable place for both Box and GUI contributors.
Die Doku liegt bewusst im Repo-weiten `docs/`-Ordner, damit Box- und GUI-Entwickler einen gemeinsamen Einstiegspunkt haben.
## Prerequisites
## Voraussetzungen
- Python 3
- Node.js + npm
- From repo root
- Aus dem Repo-Root ausfuehren
## Step-by-step
Quickstart (3-5 minutes):
1) Start everything:
## Schritt-fuer-Schritt
Quickstart (3-5 Minuten):
1) Alles starten:
```
./dev.sh
```
2) Open the WiFi setup UI:
2) Setup-WebGUI oeffnen:
```
http://127.0.0.1:9000/setup
```
3) Add a WiFi profile and submit.
4) Check status:
3) WLAN-Profil eintragen und speichern.
4) Status pruefen:
```
curl http://127.0.0.1:8000/status
```
5) Start playback:
5) Playback starten:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
@@ -33,26 +33,26 @@ curl -X POST http://127.0.0.1:8000/command \
```
## Troubleshooting
- Box not starting:
- Ensure dependencies are installed and ports 8000/9000 are free.
- GUI cannot reach API:
- Check `VITE_BOX_API_URL` and Box logs.
- Box startet nicht:
- Pruefe Abhaengigkeiten und ob Ports 8000/9000 frei sind.
- GUI erreicht API nicht:
- Pruefe `VITE_BOX_API_URL` und Box-Logs.
## What Is Mock vs Real
- Mock: WiFi, IP, Spotify, audio, hardware. These are logical simulations only.
- Real: Box process, playback state machine, API, CLI, resume logic, media traversal.
## Was ist Mock vs. real
- Mock: WLAN, IP, Spotify, Audio, Hardware. Diese Backends sind logische Simulationen.
- Real: Box-Prozess, Playback-State-Machine, API, CLI, Resume-Logik, Media-Traversal.
## Link Index
- Dev workflow: `docs/dev-workflow.md`
- Box lifecycle: `docs/box-lifecycle.md`
- Setup WebGUI: `docs/setup-webgui.md`
- HTTP API contract: `docs/api.md`
- GUI control panel: `docs/gui-control-panel.md`
- Playback testing: `docs/playback-testing.md`
- Offline scenarios: `docs/offline-scenarios.md`
- Security & storage: `docs/security-storage.md`
- CLI reference: `docs/cli.md`
- Acceptance tests: `docs/acceptance-tests.md`
- Debugging guide: `docs/debugging.md`
- Virtual box notes: `docs/virtual-box.md`
- Legacy storage notes: `docs/storage.md`
## Link-Index
- Dev-Workflow: `docs/dev-workflow.md`
- Box-Lifecycle: `docs/box-lifecycle.md`
- Setup-WebGUI: `docs/setup-webgui.md`
- HTTP-API Vertrag: `docs/api.md`
- GUI Control Panel: `docs/gui-control-panel.md`
- Playback-Tests: `docs/playback-testing.md`
- Offline-Szenarien: `docs/offline-scenarios.md`
- Security & Storage: `docs/security-storage.md`
- CLI-Referenz: `docs/cli.md`
- Acceptance-Tests: `docs/acceptance-tests.md`
- Debugging-Guide: `docs/debugging.md`
- Virtual-Box-Notizen: `docs/virtual-box.md`
- Legacy-Storage-Notizen: `docs/storage.md`
+346 -334
View File
@@ -1,560 +1,572 @@
# Acceptance Tests
# Acceptance-Tests
## Purpose
Comprehensive test catalog derived from the current codebase. Each test is runnable in Simulation and described for Real Box hardware. Behaviors not implemented are explicitly marked.
## Zweck
Konsistenter Testkatalog basierend auf dem aktuellen Code. Alle Tests trennen Simulation und Hardware. Nicht implementierte Punkte sind explizit markiert.
## Prerequisites
- Repo root
## Voraussetzungen
- Repo-Root
- Python 3
- Box API running for Simulation: `python3 box/main.py run`
- Box API fuer Simulation: `python3 box/main.py run`
- GUI optional: `cd gui && npm run dev`
## Step-by-step
Use the tests below in order or individually.
## Schritt-fuer-Schritt
Die Tests koennen einzeln oder der Reihe nach ausgefuehrt werden.
---
## Test A: Box-Start & Identitaet (box_id)
# Lokale Box-Tests (L1-L10)
1) Goal
- Verify box_id is created once and persists.
## Test L1: Box-Start & box_id
2) Preconditions
- Simulation: `box/data/box.json` may be missing.
- Real Box: storage is empty or fresh install.
1) Ziel
- box_id wird einmalig erzeugt und bleibt stabil.
3) Procedure Simulation / Emulation
- Start Box:
2) Voraussetzungen
- Simulation: `box/data/box.json` kann fehlen.
- Hardware: frischer Start.
3) Durchfuehrung Simulation / Emulation
- Box starten:
```
python3 box/main.py run
```
- Check box_id in API:
- Status lesen:
```
curl http://127.0.0.1:8000/status
```
- Inspect file:
- Datei pruefen:
```
cat box/data/box.json
```
4) Procedure Real Box (Hardware)
- Power on device.
- Open status UI (if available) or API endpoint.
4) Durchfuehrung Reale Box (Hardware)
- Box einschalten.
- Statusseite/API pruefen (sofern erreichbar).
5) Expected Result
- `/status` contains `box_id` with format `klangkiste-<10 chars a-z0-9>`.
- `box/data/box.json` contains `box_id` and is stable across restarts.
5) Erwartetes Ergebnis
- `box_id` im Format `klangkiste-<10 chars a-z0-9>`.
- `box/data/box.json` bleibt stabil ueber Neustarts.
6) Deviations / Troubleshooting
- `box_id` missing: check write permissions for `box/data/`.
- `box_id` changes: indicates manual deletion of `box/data/box.json`.
6) Abweichungen / Fehlersuche
- `box_id` fehlt: Schreibrechte in `box/data/` pruefen.
---
## Test B: WLAN-Ersteinrichtung (Setup WebGUI)
## Test L2: WLAN-Ersteinrichtung (Setup WebGUI)
1) Goal
- Configure WiFi profile via setup UI and reach WIFI_ONLINE.
1) Ziel
- WLAN-Profil per Setup UI anlegen.
2) Preconditions
- No WiFi profiles stored.
2) Voraussetzungen
- Keine WLAN-Profile gespeichert.
3) Procedure Simulation / Emulation
- Reset WiFi profiles:
3) Durchfuehrung Simulation / Emulation
- WLAN reset:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_reset"}'
```
- Open Setup UI:
- Setup UI oeffnen:
```
http://127.0.0.1:9000/setup
```
- Choose SSID and enter password, click "Verbinden".
- Check status:
- SSID waehlen, Passwort eingeben, speichern.
- Status pruefen:
```
curl http://127.0.0.1:8000/status
```
4) Procedure Real Box (Hardware)
- Power on device.
- Connect to device AP SSID (not implemented in code).
- Open setup page and submit WiFi credentials.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code: kein echter AP/WLAN-Stack.
5) Expected Result
5) Erwartetes Ergebnis
- `wifi_state = WIFI_ONLINE`
- `connected_ssid` matches selected SSID
- `wifi_profiles_count` increments
- `connected_ssid` gesetzt
6) Deviations / Troubleshooting
- Setup UI unreachable: port 9000 occupied or Box not running.
- Not testable with current code: AP SSID and real WiFi connect.
6) Abweichungen / Fehlersuche
- Setup UI nicht erreichbar: Port 9000 frei, Box laeuft.
---
## Test C: Persistenz & Neustart
## Test L3: Persistenz & Neustart
1) Goal
- Ensure resume state and WiFi profiles persist across restarts.
1) Ziel
- Resume und WLAN-Profile bleiben ueber Neustart erhalten.
2) Preconditions
- At least one WiFi profile stored.
- Playback started once.
2) Voraussetzungen
- WLAN-Profil vorhanden.
- Playback einmal gestartet.
3) Procedure Simulation / Emulation
- Add WiFi profile (if needed):
3) Durchfuehrung Simulation / Emulation
- WLAN-Profil setzen:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_add_profile","payload":{"ssid":"HomeWiFi","password":"secret","priority":10}}'
```
- Start playback:
- Playback starten:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"nfc_on","payload":{"uid":"UID_1"}}'
```
- Stop playback:
- Playback stoppen:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"nfc_off","payload":{"uid":"UID_1"}}'
```
- Restart Box process.
- Check status:
- Box-Prozess neu starten.
- Status pruefen:
```
curl http://127.0.0.1:8000/status
```
4) Procedure Real Box (Hardware)
- Start playback via NFC.
- Power cycle device.
- Verify WiFi profile and resume state persist.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code: kein echter WLAN-Stack.
5) Expected Result
- `box/data/state.json` contains `resume`.
- `box/data/secrets.enc` exists and contains profiles (encrypted).
- After restart, `wifi_state` is not UNCONFIGURED.
5) Erwartetes Ergebnis
- `box/data/state.json` enthaelt Resume.
- `box/data/secrets.enc` existiert.
6) Deviations / Troubleshooting
- Resume missing: check `box/data/state.json`.
- WiFi profiles missing: check `box/data/secrets.enc` creation.
6) Abweichungen / Fehlersuche
- Resume fehlt: `box/data/state.json` pruefen.
---
## Test D: HTTP API Status & Commands
## Test L4: HTTP API Status & Commands
1) Goal
- Verify /status and /command are functional.
1) Ziel
- /status und /command liefern Antworten und aendern Status.
2) Preconditions
- Box running.
2) Voraussetzungen
- Box laeuft.
3) Procedure Simulation / Emulation
3) Durchfuehrung Simulation / Emulation
- Status:
```
curl http://127.0.0.1:8000/status
```
- Command:
- Command (vor Pairing -> 403 erwartet):
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"play_pause"}'
```
4) Procedure Real Box (Hardware)
- Same as above once API is reachable in real network.
4) Durchfuehrung Reale Box (Hardware)
- Gleich wie Simulation, sofern API erreichbar.
5) Expected Result
- HTTP 200 responses.
- `/status` fields updated after commands.
5) Erwartetes Ergebnis
- Vor Pairing: HTTP 403 fuer gesperrte Commands.
- Nach Pairing (mit Token): HTTP 200 und Status aktualisiert.
6) Deviations / Troubleshooting
- CORS errors: ensure API is running.
- 400 errors: check payload requirements.
6) Abweichungen / Fehlersuche
- 400: Payload pruefen.
---
## Test E: GUI-Steuerung
## Test L5: GUI-Steuerung
1) Goal
- Verify GUI can control playback and display status.
1) Ziel
- GUI steuert Box und zeigt Live-Status.
2) Preconditions
- Box running on 127.0.0.1:8000.
- GUI running on 127.0.0.1:5174.
2) Voraussetzungen
- GUI laeuft auf 127.0.0.1:5174.
3) Procedure Simulation / Emulation
- Open GUI:
```
http://127.0.0.1:5174
```
- Click Play / Pause, Next, Prev, Vol +, Vol -.
- Click NFC UID_1 ON/OFF.
- Watch status refresh (polling every 1s).
3) Durchfuehrung Simulation / Emulation
- GUI oeffnen, Buttons klicken.
- Status aendert sich innerhalb von 1s.
4) Procedure Real Box (Hardware)
- Use GUI from another device pointing to Box IP.
4) Durchfuehrung Reale Box (Hardware)
- GUI auf Box-IP ausrichten (spaeter).
5) Expected Result
- `/status` changes after each button.
- GUI shows updated status and last_error if present.
5) Erwartetes Ergebnis
- `/status` aendert sich nach Commands.
6) Deviations / Troubleshooting
- GUI shows "Failed to fetch": verify VITE_BOX_API_URL.
6) Abweichungen / Fehlersuche
- GUI "Failed to fetch": `VITE_BOX_API_URL` pruefen.
---
## Test F: Playback & NFC
## Test L6: Playback & NFC
1) Goal
- Verify NFC starts playback from tags.
1) Ziel
- NFC startet Playback.
2) Preconditions
- Tags exist in `box/data/box.json`.
- Media exists under `box/data/media`.
2) Voraussetzungen
- Tags in `box/data/box.json`.
- Medien in `box/data/media`.
3) Procedure Simulation / Emulation
- Start playback:
3) Durchfuehrung Simulation / Emulation
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"nfc_on","payload":{"uid":"UID_1"}}'
```
- Observe `/status`:
```
curl http://127.0.0.1:8000/status
```
4) Procedure Real Box (Hardware)
- Place NFC tag mapped to UID_1.
4) Durchfuehrung Reale Box (Hardware)
- NFC-Tag auflegen (spaeter).
5) Expected Result
5) Erwartetes Ergebnis
- `playback_state.state = PLAYING`
- `active_uid = UID_1`
6) Deviations / Troubleshooting
- No playback: check tags in `box/data/box.json` and files in `box/data/media`.
6) Abweichungen / Fehlersuche
- Tag fehlt: `box/data/box.json` pruefen.
---
## Test G: Resume-Logik
## Test L7: Resume-Logik
1) Goal
- Resume picks up last file index and position per UID.
1) Ziel
- Resume pro UID funktioniert.
2) Preconditions
- Playback started at least once for UID_1.
2) Voraussetzungen
- Playback einmal gestartet.
3) Procedure Simulation / Emulation
- Start playback, wait ~5s.
- Stop with `nfc_off`.
- Start again with `nfc_on`.
3) Durchfuehrung Simulation / Emulation
- `nfc_on UID_1`, warten, `nfc_off UID_1`, dann `nfc_on UID_1`.
4) Procedure Real Box (Hardware)
- Place NFC tag, remove, place again.
4) Durchfuehrung Reale Box (Hardware)
- NFC auflegen/abnehmen (spaeter).
5) Expected Result
- Resume uses stored `file_index` and `position`.
- `box/data/state.json` contains resume for UID_1.
5) Erwartetes Ergebnis
- Resume nutzt gespeicherte Position.
6) Deviations / Troubleshooting
- Resume resets to 0: check `state.json` and path changes.
6) Abweichungen / Fehlersuche
- Resume = 0: `box/data/state.json` pruefen.
---
## Test H: Ordner- & Rekursionslogik
## Test L8: Rekursion & Ordnerstruktur
1) Goal
- Verify recursive ordering (DFS, folders first, lexicographic).
1) Ziel
- Rekursive Reihenfolge (DFS) ist korrekt.
2) Preconditions
- Nested media structure in `box/data/media`.
2) Voraussetzungen
- Verschachtelte Ordner in `box/data/media`.
3) Procedure Simulation / Emulation
- Create nested structure in `box/data/media/book_6/CD1/...`.
- Point a tag to `media/book_6` in `box/data/box.json`.
- Start playback with `nfc_on`.
3) Durchfuehrung Simulation / Emulation
- Tag auf `media/book_6` setzen, `nfc_on`.
4) Procedure Real Box (Hardware)
- Copy nested folders to the device media path.
- Start playback via NFC.
4) Durchfuehrung Reale Box (Hardware)
- Medienstruktur auf Box kopieren (spaeter).
5) Expected Result
- Files play in DFS order: subfolders sorted, then files.
5) Erwartetes Ergebnis
- DFS-Reihenfolge, lexikografisch.
6) Deviations / Troubleshooting
- Ordering unexpected: check folder names and lexicographic sort.
6) Abweichungen / Fehlersuche
- Reihenfolge falsch: Ordnernamen pruefen.
---
## Test I: Offline-Szenarien (Box intern)
## Test L9: Offline-Szenarien (Box intern)
1) Goal
- Verify WiFi and Spotify states in offline/online flows.
1) Ziel
- WiFi/Spotify-Status reagieren auf Commands.
2) Preconditions
- Box running.
2) Voraussetzungen
- Box laeuft.
3) Procedure Simulation / Emulation
- WiFi reset:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_reset"}'
```
- Spotify clear:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"spotify_clear"}'
```
3) Durchfuehrung Simulation / Emulation
- `wifi_reset` und `spotify_clear` ausfuehren.
4) Procedure Real Box (Hardware)
- Not testable with current code: real network loss and Spotify connectivity.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
5) Expected Result
5) Erwartetes Ergebnis
- `wifi_state = WIFI_UNCONFIGURED`
- `spotify_status = NOT_CONFIGURED`
6) Deviations / Troubleshooting
- Status unchanged: ensure you read `/status` after command.
6) Abweichungen / Fehlersuche
- Status aendert sich nicht: `/status` pruefen.
---
## Test J: Fehlerfaelle (Box intern)
## Test L10: Fehlerfaelle (Box intern)
1) Goal
- Verify error sound and error reporting.
1) Ziel
- Fehler setzen `last_error`.
2) Preconditions
- Box running.
2) Voraussetzungen
- Box laeuft.
3) Procedure Simulation / Emulation
- Trigger error:
3) Durchfuehrung Simulation / Emulation
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"trigger_error","payload":{"type":"uid_unmapped"}}'
```
- Check `/status` for `last_error`.
4) Procedure Real Box (Hardware)
- Not testable with current code: actual error sound output.
5) Expected Result
- `playback_state.last_error` equals error type.
- Box returns to `IDLE` after error.
6) Deviations / Troubleshooting
- No error shown: verify `/status` and that errors are not cleared automatically.
---
## Test K: Announce -> neue Box erscheint im Server
1) Ziel
- Box meldet sich beim Server und erscheint als UNPAIRED / neue Box.
2) Voraussetzungen
- Simulation: Server-Komponente vorhanden und laeuft.
- Real Box: Server erreichbar im Netzwerk.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code: Box sendet keine Announce-Requests.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code: Announce-Logik ist nicht implementiert.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
6) Abweichungen / Fehlersuche
- Wenn implementiert: Pruefe Server-Logs, API-Endpunkte und Box-Statusfelder.
---
## Test L: Mehrere Boxen gleichzeitig
1) Ziel
- Mehrere Boxen werden als separate, unbekannte Boxen erkannt.
2) Voraussetzungen
- Simulation: Mehrere Box-Prozesse mit unterschiedlichen `box_id`.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code: Announce/Server-Erkennung fehlt.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
- `playback_state.last_error` gesetzt.
6) Abweichungen / Fehlersuche
- Wenn implementiert: Pruefe Duplikate anhand `box_id` und `fingerprint`.
- Kein Fehler sichtbar: `/status` pruefen.
---
## Test M: Pairing-Freigabe ueber Server-GUI
# Server/Pairing-Tests (A-J)
## Test A: Box-Erststart & Identitaet
1) Ziel
- Box wird nach Freigabe gepairt und erhaelt API-Token.
- box_id wird einmalig erzeugt.
2) Voraussetzungen
- Simulation: Server-GUI und Pairing-Endpunkte implementiert.
- Box gestartet.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code: Pairing-Flow fehlt.
- Siehe Test L1.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
- Siehe Test L1.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
- box_id stabil.
6) Abweichungen / Fehlersuche
- Wenn implementiert: Pruefe Token-Speicherung in `box/data/secrets.enc`.
- Siehe Test L1.
---
## Test N: Verhalten vor Pairing (keine Steuerung erlaubt)
## Test B: Announce -> neue Box erscheint im Server
1) Ziel
- Vor Pairing sind Steuerbefehle blockiert.
- Box meldet sich beim Server und erscheint als UNPAIRED.
2) Voraussetzungen
- Server-Integration und Auth-Checks vorhanden.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code: kein Auth/Pairing implementiert.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
6) Abweichungen / Fehlersuche
- Wenn implementiert: Pruefe API-Responses (401/403) und Server-Logs.
---
## Test O: Verhalten nach Pairing
1) Ziel
- Nach Pairing sind Steuerbefehle erlaubt und Box reagiert.
2) Voraussetzungen
- Pairing-Flow implementiert.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
6) Abweichungen / Fehlersuche
- Wenn implementiert: Pruefe Token in `secrets.enc` und Server-GUI Status.
---
## Test P: Neustart vor/nach Pairing
1) Ziel
- Box behaelt Pairing-Status und Token ueber Neustarts.
2) Voraussetzungen
- Pairing implementiert.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
6) Abweichungen / Fehlersuche
- Wenn implementiert: Pruefe `secrets.enc` vor/nach Neustart.
---
## Test Q: Factory Reset -> neue Identitaet
1) Ziel
- Factory Reset loescht box_id und erzeugt neue Identitaet beim naechsten Start.
2) Voraussetzungen
- Box laeuft einmalig und `box/data/` existiert.
3) Durchfuehrung Simulation / Emulation
- Stoppe Box.
- Loesche folgende Dateien:
- Server laeuft:
```
rm -f box/data/box.json box/data/state.json box/data/secrets.enc
python3 server/main.py
```
- Starte Box neu:
3) Durchfuehrung Simulation / Emulation
- Box starten:
```
python3 box/main.py run
```
- Pruefe `/status`.
- 30-60s warten (Announce-Intervall).
- Server-GUI oeffnen:
```
http://127.0.0.1:7000/ui
```
- Optional: Server-Storage pruefen:
```
cat server/data/boxes.json
```
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code: kein Factory-Reset-Mechanismus implementiert.
- Box einschalten.
- Server-GUI oeffnen, Box sollte erscheinen.
5) Erwartetes Ergebnis
- Neue `box_id` erscheint in `/status`.
- Box erscheint als UNPAIRED in der Server-GUI.
- `server/data/boxes.json` enthaelt `box_id` und `fingerprint`.
6) Abweichungen / Fehlersuche
- `box_id` bleibt gleich: Dateien wurden nicht geloescht.
- Keine Box sichtbar: Server-URL in `box/data/box.json` pruefen.
- Server nicht erreichbar: Server-Prozess/Port 7000 pruefen.
---
## Test R: Offline / Server nicht erreichbar
## Test C: Mehrere Boxen gleichzeitig
1) Ziel
- Box laeuft weiter, wenn Server nicht erreichbar ist.
- Mehrere Boxen werden separat erkannt.
2) Voraussetzungen
- Server-Integration implementiert.
- Mehrere Boxen mit unterschiedlichen `box_id`.
3) Durchfuehrung Simulation / Emulation
- Nicht testbar mit aktuellem Code: keine Server-Kommunikation.
- Nicht testbar mit aktuellem Code: Box nutzt festen `box/data/`-Pfad und kann nicht mehrfach parallel laufen.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code.
- Zwei physische Boxen einschalten.
- Server-GUI zeigt beide UNPAIRED an.
5) Erwartetes Ergebnis
- Nicht testbar mit aktuellem Code.
- Zwei Eintraege mit unterschiedlichen `box_id`.
6) Abweichungen / Fehlersuche
- Wenn implementiert: pruefe lokale Funktionen (Playback, NFC) laufen weiter.
- Duplikate: `box_id` doppelt? Factory-Reset pruefen.
---
## Test S: Simulation vs. Hardware Unterschiede
## Test D: Pairing-Freigabe ueber Server-GUI
1) Ziel
- Dokumentiere, was real vs. mock ist.
- Pairing liefert API-Token und Box wird PAIRED.
2) Voraussetzungen
- Box erscheint als UNPAIRED in `/ui`.
3) Durchfuehrung Simulation / Emulation
- Server-GUI oeffnen:
```
http://127.0.0.1:7000/ui
```
- Button "Box hinzufuegen" klicken.
- API-Token via Server-API abrufen:
```
curl -X POST http://127.0.0.1:7000/api/boxes/pair -H "Content-Type: application/json" -d '{"box_id":"<box_id>"}'
```
4) Durchfuehrung Reale Box (Hardware)
- Gleiches Vorgehen ueber Server-GUI.
5) Erwartetes Ergebnis
- Box erscheint in der gepaarten Liste.
- `api_token` wird an die Box ausgeliefert.
6) Abweichungen / Fehlersuche
- Pairing bleibt aus: `server/data/boxes.json` pruefen.
---
## Test E: Verhalten vor Pairing (keine Steuerung erlaubt)
1) Ziel
- Vor Pairing werden Steuerbefehle blockiert.
2) Voraussetzungen
- Box UNPAIRED.
3) Durchfuehrung Simulation / Emulation
```
curl -i -X POST http://127.0.0.1:8000/command -H "Content-Type: application/json" -d '{"command":"play_pause"}'
```
4) Durchfuehrung Reale Box (Hardware)
- Gleicher Request gegen Box-IP.
5) Erwartetes Ergebnis
- HTTP 403 "pairing required".
6) Abweichungen / Fehlersuche
- Wenn 200: Pairing-Status und API-Token pruefen.
---
## Test F: Verhalten nach Pairing
1) Ziel
- Nach Pairing sind Commands erlaubt (mit Token).
2) Voraussetzungen
- Box PAIRED und api_token vorhanden.
3) Durchfuehrung Simulation / Emulation
```
curl -i -X POST http://127.0.0.1:8000/command -H "Content-Type: application/json" -H "X-API-Token: <api_token>" -d '{"command":"play_pause"}'
```
4) Durchfuehrung Reale Box (Hardware)
- Gleiches Vorgehen gegen Box-IP.
5) Erwartetes Ergebnis
- HTTP 200, Status aktualisiert.
6) Abweichungen / Fehlersuche
- 403: Token pruefen, Pairing-Status pruefen.
---
## Test G: Neustart vor/nach Pairing
1) Ziel
- Pairing-Status und Token bleiben ueber Neustart erhalten.
2) Voraussetzungen
- Box PAIRED.
3) Durchfuehrung Simulation / Emulation
- Box-Prozess stoppen, neu starten.
- Mit Token Command senden.
4) Durchfuehrung Reale Box (Hardware)
- Power cycle.
5) Erwartetes Ergebnis
- Box bleibt PAIRED, Token weiterhin gueltig.
6) Abweichungen / Fehlersuche
- Token fehlt: `box/data/secrets.enc` pruefen.
---
## Test H: Factory Reset -> neue Identitaet
1) Ziel
- Factory Reset erzeugt neue Identitaet.
2) Voraussetzungen
- Box einmal gestartet.
3) Durchfuehrung Simulation / Emulation
- Box stoppen.
- Dateien loeschen:
```
rm -f box/data/box.json box/data/state.json box/data/secrets.enc
```
- Box neu starten.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code: kein Reset-Mechanismus.
5) Erwartetes Ergebnis
- Neue `box_id`.
6) Abweichungen / Fehlersuche
- `box_id` bleibt gleich: Dateien nicht geloescht.
---
## Test I: Offline / Server nicht erreichbar
1) Ziel
- Box laeuft weiter ohne Server.
2) Voraussetzungen
- Box laeuft, Server gestoppt.
3) Durchfuehrung Simulation / Emulation
- Server stoppen.
- Playback starten (mit Token falls gepairt).
4) Durchfuehrung Reale Box (Hardware)
- Server vom Netz trennen.
5) Erwartetes Ergebnis
- Lokale Playback-Logik laeuft weiter.
- Server-GUI zeigt Box ggf. als OFFLINE nach Timeout.
6) Abweichungen / Fehlersuche
- Box stoppt: lokale Logs pruefen.
---
## Test J: Simulation vs. Hardware Unterschiede
1) Ziel
- Unterschiede klar dokumentieren.
2) Voraussetzungen
- Box laeuft in Simulation.
3) Durchfuehrung Simulation / Emulation
- Beachte: WiFi/IP/Spotify/Audio sind Mock.
- WiFi/IP/Spotify/Audio sind Mock.
- Setup UI und API funktionieren lokal.
4) Durchfuehrung Reale Box (Hardware)
- Nicht testbar mit aktuellem Code: Hardware-Backends fehlen.
- Echte Backends notwendig (nicht implementiert).
5) Erwartetes Ergebnis
- Simulation laeuft ohne Hardware.
- Hardware-Backends koennen spaeter ersetzt werden.
6) Abweichungen / Fehlersuche
- Wenn Hardware genutzt wird, fehlen Implementierungen.
- Hardware-Backends fehlen.
+41 -27
View File
@@ -1,19 +1,19 @@
# Box HTTP API
## Purpose
Defines the stable contract between the GUI/CLI and the Box.
The Box is authoritative; the client only sends commands and reads status.
## Zweck
Stabiler Vertrag zwischen GUI/CLI und Box.
Die Box ist autoritativ, der Client sendet nur Commands und liest Status.
## Prerequisites
- Box running on `http://127.0.0.1:8000`
## Voraussetzungen
- Box laeuft auf `http://127.0.0.1:8000`
## Step-by-step
## Schritt-fuer-Schritt
### Status
```
curl http://127.0.0.1:8000/status
```
### Command (example)
### Command (Beispiel)
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
@@ -21,12 +21,13 @@ curl -X POST http://127.0.0.1:8000/command \
```
## GET /status
Response fields:
- `box_id`: unique device ID
Felder:
- `box_id`: eindeutige Box-ID
- `pairing_state`: `UNPAIRED | PAIRING_PENDING | PAIRED`
- `wifi_state`: `WIFI_UNCONFIGURED | WIFI_CONNECTING | WIFI_ONLINE | WIFI_OFFLINE`
- `ip_address`: simulated IP (`192.168.4.1` or `127.0.0.1`)
- `connected_ssid`: connected SSID or `null`
- `wifi_profiles_count`: number of stored profiles
- `ip_address`: simulierte IP (`192.168.4.1` oder `127.0.0.1`)
- `connected_ssid`: verbundene SSID oder `null`
- `wifi_profiles_count`: Anzahl gespeicherter Profile
- `spotify_status`: `NOT_CONFIGURED | READY`
- `playback_state`:
- `state`: `IDLE | PLAYING | PAUSED`
@@ -39,10 +40,11 @@ Response fields:
- `volume`
- `last_error`
Example response:
Beispielantwort:
```
{
"box_id": "klangkiste-a9f3k7m2q8",
"pairing_state": "UNPAIRED",
"wifi_state": "WIFI_ONLINE",
"ip_address": "127.0.0.1",
"connected_ssid": "HomeWiFi",
@@ -63,7 +65,7 @@ Example response:
```
## POST /command
Request body:
Request Body:
```
{
"command": "nfc_on",
@@ -71,7 +73,7 @@ Request body:
}
```
Supported commands:
Unterstuetzte Commands:
- `nfc_on` `{ uid }`
- `nfc_off` `{ uid }`
- `play_pause`
@@ -79,8 +81,8 @@ Supported commands:
- `prev`
- `volume_up`
- `volume_down`
- `vol_up` (alias)
- `vol_down` (alias)
- `vol_up` (Alias)
- `vol_down` (Alias)
- `stop`
- `trigger_error` `{ type }`
- `wifi_add_profile` `{ ssid, password, priority? }`
@@ -88,27 +90,39 @@ Supported commands:
- `spotify_set_tokens` `{ access_token, refresh_token, expires_at, account_id? }`
- `spotify_clear`
Example: add WiFi profile
## Auth fuer Commands
- Vor Pairing sind nur diese Commands erlaubt:
- `wifi_add_profile`, `wifi_reset`, `spotify_set_tokens`, `spotify_clear`
- Alle anderen Commands erfordern `X-API-Token` im Header.
Beispiel mit Token:\n```
curl -X POST http://127.0.0.1:8000/command \\
-H "Content-Type: application/json" \\
-H "X-API-Token: <api_token>" \\
-d '{"command":"nfc_on","payload":{"uid":"UID_1"}}'
```
Beispiel: WLAN-Profil hinzufuegen
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_add_profile","payload":{"ssid":"HomeWiFi","password":"secret","priority":10}}'
```
Example: set Spotify tokens
Beispiel: Spotify Tokens setzen
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"spotify_set_tokens","payload":{"access_token":"a","refresh_token":"b","expires_at":123}}'
```
## Checklist
-`GET /status` returns JSON
-`POST /command` returns `{ "ok": true }`
-No secrets appear in `/status`
## Checkliste
- ✅ `GET /status` liefert JSON
- ✅ `POST /command` liefert `{ "ok": true }`
-Keine Secrets in `/status`
## Troubleshooting
- 400 responses:
- Check required fields in the payload.
- CORS errors:
- Ensure Box API is running on the expected host/port.
- 400 Responses:
- Payload pruefen (Pflichtfelder).
- CORS-Fehler:
- API muss laufen und erreichbar sein.
+36 -31
View File
@@ -1,42 +1,47 @@
# Box Lifecycle
# Box-Lifecycle
## Purpose
Defines the virtual box lifecycle: identity, WiFi states, and restart behavior.
## Zweck
Definiert Identitaet, WLAN-Status und Neustartverhalten der virtuellen Box.
## Prerequisites
- Box started with `python3 box/main.py run`
## Voraussetzungen
- Box gestartet mit `python3 box/main.py run`
## Step-by-step
### Box identity
- Generated on first start if `box/data/box.json` has no `box_id`.
- Format: `klangkiste-<10 chars a-z0-9>`
- Stored in `box/data/box.json`
## Schritt-fuer-Schritt
### Box-Identitaet
- Wird beim ersten Start erzeugt, wenn `box/data/box.json` keine `box_id` enthaelt.
- Format: `klangkiste-<10 Zeichen a-z0-9>`
- Persistiert in `box/data/box.json`
### WiFi states (logical)
- `WIFI_UNCONFIGURED`: no profiles stored
- `WIFI_CONNECTING`: reserved for future; not emitted in mock
- `WIFI_ONLINE`: profile exists and mock is "connected"
- `WIFI_OFFLINE`: profile exists but not connected
### WLAN-Zustaende (logisch)
- `WIFI_UNCONFIGURED`: keine Profile gespeichert
- `WIFI_CONNECTING`: reserviert fuer spaeter (im Mock nicht aktiv)
- `WIFI_ONLINE`: Profil vorhanden und verbunden
- `WIFI_OFFLINE`: Profil vorhanden, aber nicht verbunden
### IP model (informational)
- Setup/AP mode: `192.168.4.1`
- Normal mode: `127.0.0.1`
### IP-Modell (informativ)
- Setup/AP-Modus: `192.168.4.1`
- Normalbetrieb: `127.0.0.1`
### Restart behavior
- Box reloads `box_id`, resume state, and WiFi profiles from `box/data/`
- Playback resume and WiFi status are preserved
### Neustartverhalten
- Box laedt `box_id`, Resume-State und WLAN-Profile aus `box/data/`
- Resume und WLAN-Status bleiben erhalten
### Resets
- WiFi reset: clears WiFi profiles only (`wifi_reset` command)
- Factory reset: delete `box/data/box.json`, `box/data/state.json`, `box/data/secrets.enc`
- WLAN-Reset: nur WLAN-Profile loeschen (`wifi_reset`)
- Factory-Reset: `box/data/box.json`, `box/data/state.json`, `box/data/secrets.enc` loeschen
## Checklist
- ✅ After first boot, `box/data/box.json` contains `box_id`
- `GET /status` returns `wifi_state` and `ip_address`
- ✅ WiFi reset clears profiles, `wifi_state=WIFI_UNCONFIGURED`
### Pairing-Status
- `UNPAIRED`: Box ist nicht gekoppelt
- `PAIRING_PENDING`: Box pollt Pairing-Status beim Server
- `PAIRED`: Box ist gekoppelt, API-Token vorhanden
## Checkliste
- ✅ Nach erstem Start existiert `box/data/box.json` mit `box_id`
-`GET /status` liefert `wifi_state` und `ip_address`
- ✅ WLAN-Reset setzt `wifi_state=WIFI_UNCONFIGURED`
## Troubleshooting
- `wifi_state` not changing:
- Add a WiFi profile via setup UI or API.
- Missing `box_id`:
- Ensure `box/data/box.json` is writable.
- `wifi_state` aendert sich nicht:
- WLAN-Profil via Setup UI oder API hinzufuegen.
- `box_id` fehlt:
- Schreibrechte fuer `box/data/box.json` pruefen.
+19 -19
View File
@@ -1,34 +1,34 @@
# Box CLI Reference
# CLI-Referenz
## Purpose
List all CLI interactions available in the virtual box.
## Zweck
Alle CLI-Interaktionen der virtuellen Box.
## Prerequisites
- Box code available in `box/`
## Voraussetzungen
- Box-Code in `box/`
## Step-by-step
### One-shot commands
## Schritt-fuer-Schritt
### One-shot Commands
Status:
```
python3 box/main.py status
```
Simulate NFC:
NFC simulieren:
```
python3 box/main.py simulate-nfc UID_1
python3 box/main.py simulate-nfc-off UID_1
```
Manual tick:
Manuelles Tick:
```
python3 box/main.py tick 30
```
### Interactive run mode
### Interaktiver Run-Modus
```
python3 box/main.py run
```
Commands inside run mode:
Commands im Run-Modus:
```
status
nfc <UID>
@@ -43,13 +43,13 @@ help
exit | quit
```
## Checklist
-`status` prints runtime state
-`simulate-nfc` starts playback
-`run` accepts interactive commands
## Checkliste
-`status` gibt Runtime-State aus
-`simulate-nfc` startet Playback
-`run` akzeptiert interaktive Commands
## Troubleshooting
- Commands not found:
- Ensure you run from repo root or use full path to `box/main.py`.
- No playback:
- Check tags in `box/data/box.json` and media under `box/data/media`.
- Commands nicht gefunden:
- Aus Repo-Root ausfuehren oder `box/main.py` voll referenzieren.
- Kein Playback:
- Tags in `box/data/box.json` und Medien in `box/data/media` pruefen.
+59 -60
View File
@@ -1,99 +1,98 @@
# Debugging Guide
# Debugging-Guide
## Purpose
Fast diagnosis guide: "If X happens, check Y" with separate paths for Simulation and Real Box.
## Zweck
Schnelle Diagnose: "Wenn X passiert, pruefe Y" getrennt nach Simulation und Hardware.
## Prerequisites
- Box running (simulation or device)
- Access to `/status` and data files
## Voraussetzungen
- Box laeuft (Simulation oder Hardware)
- Zugriff auf `/status` und Datenfiles
## Step-by-step
Use the sections below by symptom.
## Schritt-fuer-Schritt
Nutze die Sektionen nach Symptomen.
## Simulation (IDE)
### If GUI shows "Failed to fetch"
- Check API is running:
### GUI zeigt "Failed to fetch"
- API pruefen:
```
curl http://127.0.0.1:8000/status
```
- Verify GUI API base:
- `VITE_BOX_API_URL` or default `http://localhost:8000`
- GUI API-Base pruefen:
- `VITE_BOX_API_URL` oder Default `http://localhost:8000`
### If playback does not start
- Check tags:
### Playback startet nicht
- Tags pruefen:
```
cat box/data/box.json
```
- Check media files:
- Medien pruefen:
```
ls -la box/data/media
```
- Check `/status`:
- Status pruefen:
```
curl http://127.0.0.1:8000/status
```
### If resume does not persist
- Check state file:
### Resume bleibt leer
- State pruefen:
```
cat box/data/state.json
```
- Ensure playback was stopped via `nfc_off` or end-of-media.
- Playback mit `nfc_off` beenden.
### If setup UI not reachable
- Check port 9000 free.
- Ensure Box is running.
### Setup UI nicht erreichbar
- Port 9000 frei?
- Box laeuft?
### If box not recognized by server
- Not testable with current code: no announce/pairing implemented.
### Box wird nicht erkannt (Server)
- Server-URL in `box/data/box.json` pruefen (`server_url`).
- Server laeuft auf Port 7000?
- `server/data/boxes.json` pruefen.
### If box appears multiple times
- Not testable with current code: no announce/pairing implemented.
### Box erscheint mehrfach
- `box_id` in `box/data/box.json` pruefen.
- Factory-Reset nur gezielt ausfuehren.
### If fingerprint changes
- Not testable with current code: fingerprint not implemented.
### Fingerprint aendert sich
- `secret_seed` in `box/data/secrets.enc` darf sich nicht aendern.
- `box/data/box.json` darf nicht geloescht werden.
### If pairing hangs
- Not testable with current code: pairing flow not implemented.
### Pairing haengt
- `server/data/boxes.json` pruefen: state, api_token.
- Box-Status: `pairing_state` in `/status` pruefen.
## Real Box (Hardware)
### If device not reachable in browser
- Ensure it is powered on and connected to the same network.
- Check the IP shown by your router.
## Reale Box (Hardware)
### Box nicht im Browser erreichbar
- Stromversorgung ok?
- Im gleichen Netzwerk?
- Router-IP pruefen.
### If NFC does nothing
- Confirm tag UID exists in `box.json`.
- Confirm NFC hardware is working (not implemented in code).
### NFC reagiert nicht
- UID in `box.json` vorhanden?
- NFC-Hardware (nicht implementiert) pruefen.
### If audio is silent
- Audio backend is mock in current code.
- Not testable with current code.
### Audio bleibt stumm
- Audio-Backend ist Mock im aktuellen Code.
- Nicht testbar mit aktuellem Code.
### If box not recognized by server
- Not testable with current code: no announce/pairing implemented.
### Box wird nicht erkannt / erscheint mehrfach / Fingerprint wechselt / Pairing haengt
- Server-GUI (`/ui`) pruefen.
- Box-IP/Server-IP/VLAN pruefen.
- Box-Logs pruefen.
### If box appears multiple times
- Not testable with current code: no announce/pairing implemented.
## Files
- `box/data/box.json`: Identitaet, Tags, Settings
- `box/data/state.json`: Resume und WiFi-State
- `box/data/secrets.enc`: verschluesselte Secrets
### If fingerprint changes
- Not testable with current code: fingerprint not implemented.
### If pairing hangs
- Not testable with current code: pairing flow not implemented.
## Files to inspect
- `box/data/box.json`: identity, tags, settings
- `box/data/state.json`: resume and wifi state
- `box/data/secrets.enc`: encrypted secrets
## Status fields to watch
## Statusfelder
- `wifi_state`, `ip_address`, `connected_ssid`
- `playback_state.state`, `playback_state.active_uid`
- `playback_state.position`, `playback_state.file_index`
- `playback_state.last_error`
## Troubleshooting
- 400 from `/command`:
- Check required payload fields in `docs/api.md`.
- No change after command:
- Poll `/status` after sending.
- 400 von `/command`:
- Payload pruefen (Pflichtfelder).
- Keine Aenderung nach Command:
- `/status` danach erneut abrufen.
+31 -29
View File
@@ -1,22 +1,22 @@
# Dev Workflow
# Dev-Workflow
## Purpose
How to start/stop the virtual box, how to access logs, and how to point the GUI to the API.
## Zweck
Start/Stop der virtuellen Box, Logs, und GUI-API-Konfiguration.
## Prerequisites
## Voraussetzungen
- Python 3
- Node.js + npm
- Run from repo root
- Aus dem Repo-Root ausfuehren
## Step-by-step
### Start everything (recommended)
## Schritt-fuer-Schritt
### Alles starten (empfohlen)
```
./dev.sh
```
- GUI runs in the background
- Box runs in the foreground (you can type CLI commands)
- GUI laeuft im Hintergrund.
- Box laeuft im Vordergrund und akzeptiert CLI-Kommandos.
### Start separately
### Komponenten separat starten
Box only:
```
python3 box/main.py run
@@ -28,34 +28,36 @@ npm run dev
```
## Ports / Hosts
- Server API + GUI: `http://127.0.0.1:7000` (GUI unter `/ui`)
- Box API: `http://127.0.0.1:8000`
- Setup UI: `http://127.0.0.1:9000/setup`
- GUI: `http://127.0.0.1:5174`
## API URL for GUI
Default is `http://localhost:8000`. Override with:
## API-URL fuer die GUI
Default ist `http://localhost:8000`. Override:
```
VITE_BOX_API_URL=http://127.0.0.1:8000 npm run dev
```
## Logs
- Box logs appear in the same terminal where you run `python3 box/main.py run`.
- GUI logs appear in the browser console.
## Logs ansehen
- Server-Logs: Terminal mit `python3 server/main.py`
- Box-Logs: Terminal mit `python3 box/main.py run`
- GUI-Logs: Browser-Konsole
## Stop / Cleanup
- Press `Ctrl+C` in the terminal where the box is running.
- `dev.sh` will also stop the GUI process.
## Sauber beenden
- `Ctrl+C` im Box-Terminal.
- `dev.sh` beendet die GUI automatisch.
## Checklist
- Run `./dev.sh`
- ✅ Box shows interactive prompt
- ✅ GUI loads in browser
-`curl http://127.0.0.1:8000/status` returns JSON
## Checkliste
-`./dev.sh` gestartet
- ✅ Box-CLI Prompt sichtbar
- ✅ GUI im Browser erreichbar
-`curl http://127.0.0.1:8000/status` liefert JSON
## Troubleshooting
- Port already in use:
- 8000 or 9000: stop other processes, or change ports in `box/main.py`.
- CORS error in browser:
- Ensure API is running; CORS is already enabled in the Box API.
- GUI shows "Failed to fetch":
- Check `VITE_BOX_API_URL` and Box API port.
- Port belegt:
- 8000/9000: andere Prozesse beenden oder Ports in `box/main.py` aendern.
- CORS-Fehler:
- API muss laufen und erreichbar sein.
- GUI zeigt "Failed to fetch":
- `VITE_BOX_API_URL` pruefen.
+27 -24
View File
@@ -1,21 +1,21 @@
# GUI Control Panel
## Purpose
How to use the Vite-based GUI to control and observe the virtual box.
## Zweck
Nutzung des GUI Control Panels zur Steuerung und Beobachtung der virtuellen Box.
## Prerequisites
- Box running on `http://127.0.0.1:8000`
- GUI started (`npm run dev` or `./dev.sh`)
## Voraussetzungen
- Box laeuft auf `http://127.0.0.1:8000`
- GUI gestartet (`npm run dev` oder `./dev.sh`)
- Bei gepaarter Box: `VITE_BOX_API_TOKEN` gesetzt
## Step-by-step
1) Open GUI:
## Schritt-fuer-Schritt
1) GUI oeffnen:
```
http://127.0.0.1:5174
```
2) Click buttons to send commands.
3) Observe status updates (polling + refresh after command).
2) Buttons klicken, Status beobachten.
## Controls (current in GUI)
## Controls (im GUI vorhanden)
- Play / Pause
- Next
- Prev
@@ -25,29 +25,32 @@ http://127.0.0.1:5174
- NFC UID_1 OFF
- Refresh Status
## Controls (available via API only)
## Controls (nur API)
- WiFi add/reset
- Spotify set/clear
Use API for those:
Beispiel via API:
```
## Auth-Token fuer GUI
- Nach Pairing: Token ueber Server-API oder Server-GUI ermitteln.\n- GUI mit Token starten:\n```\nVITE_BOX_API_TOKEN=<api_token> npm run dev\n```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_add_profile","payload":{"ssid":"HomeWiFi","password":"secret","priority":10}}'
```
## Expected results
- After any button click, status refreshes within 1 second.
- `last_error` is visible if an error occurs.
## Erwartete Ergebnisse
- Nach jedem Klick wird der Status innerhalb von 1s aktualisiert.
- `last_error` wird angezeigt, wenn gesetzt.
## Checklist
- ✅ GUI loads and shows API base URL
- ✅ Status updates every 1s
-Clicking a button updates `/status`
## Checkliste
- ✅ GUI laedt und zeigt API-URL
- ✅ Status aktualisiert sich automatisch
-Buttons aendern `/status`
## Troubleshooting
- GUI shows "Failed to fetch":
- Check `VITE_BOX_API_URL`.
- Verify Box API is running.
- Buttons have no effect:
- Check the API command names in `docs/api.md`.
- GUI zeigt "Failed to fetch":
- `VITE_BOX_API_URL` pruefen.
- Box API muss laufen.
- Buttons ohne Wirkung:
- API-Command-Namen in `docs/api.md` pruefen.
+22 -22
View File
@@ -1,57 +1,57 @@
# Offline Scenarios
# Offline-Szenarien
## Purpose
Simulate online/offline behavior in the virtual box and verify status fields.
## Zweck
Offline/Online-Verhalten in der virtuellen Box pruefen.
## Prerequisites
- Box running on `http://127.0.0.1:8000`
## Voraussetzungen
- Box laeuft auf `http://127.0.0.1:8000`
## Step-by-step
### WiFi offline (no profiles)
## Schritt-fuer-Schritt
### WLAN offline (keine Profile)
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_reset"}'
```
Expected:
Erwartung:
- `wifi_state = WIFI_UNCONFIGURED`
- `connected_ssid = null`
### WiFi online
### WLAN online
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"wifi_add_profile","payload":{"ssid":"HomeWiFi","password":"secret","priority":10}}'
```
Expected:
Erwartung:
- `wifi_state = WIFI_ONLINE`
- `connected_ssid = HomeWiFi`
### Spotify unavailable
### Spotify nicht konfiguriert
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"spotify_clear"}'
```
Expected:
Erwartung:
- `spotify_status = NOT_CONFIGURED`
### Spotify ready
### Spotify bereit
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"spotify_set_tokens","payload":{"access_token":"a","refresh_token":"b","expires_at":123}}'
```
Expected:
Erwartung:
- `spotify_status = READY`
## Checklist
- ✅ WiFi reset shows UNCONFIGURED
- ✅ WiFi add profile shows ONLINE
- ✅ Spotify clear shows NOT_CONFIGURED
- ✅ Spotify set shows READY
## Checkliste
- ✅ WLAN-Reset zeigt UNCONFIGURED
- ✅ WLAN-Profil zeigt ONLINE
- ✅ Spotify clear zeigt NOT_CONFIGURED
- ✅ Spotify set zeigt READY
## Troubleshooting
- Status not changing:
- Ensure `/status` is polled after sending commands.
- Check that Box is running and secrets file is writable.
- Status aendert sich nicht:
- `/status` nach Commands abfragen.
- Schreibrechte auf `box/data/secrets.enc` pruefen.
+34 -37
View File
@@ -1,65 +1,62 @@
# Playback Testing
# Playback-Tests
## Purpose
Test NFC playback, recursive media order, resume, and playback controls.
## Zweck
Tests fuer NFC-Playback, Rekursion, Resume und Buttons.
## Prerequisites
- Box running: `python3 box/main.py run`
- Media in `box/data/media/`
## Voraussetzungen
- Box laeuft: `python3 box/main.py run`
- Medien unter `box/data/media/`
## Step-by-step
### NFC simulation
Start playback:
## Schritt-fuer-Schritt
### NFC-Simulation
Start:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"nfc_on","payload":{"uid":"UID_1"}}'
```
Stop playback:
Stop:
```
curl -X POST http://127.0.0.1:8000/command \
-H "Content-Type: application/json" \
-d '{"command":"nfc_off","payload":{"uid":"UID_1"}}'
```
### Resume tests
### Resume
1) `nfc_on UID_1`
2) Wait or use `tick` in CLI
2) Warten (Auto-Tick)
3) `nfc_off UID_1`
4) `nfc_on UID_1` again
Expected: position resumes from saved state.
4) `nfc_on UID_1`
Erwartung: Resume nutzt gespeicherte Position.
### Tag switching
### Tag-Wechsel
1) `nfc_on UID_1`
2) `nfc_on UID_2`
3) `nfc_on UID_1`
Expected: UID_1 resumes from previous position.
Erwartung: UID_1 resumed.
### Recursive order
- Order is depth-first, folders first, lexicographic.
- Change folder names or nesting to see ordering changes.
### Rekursive Reihenfolge
- Ordner werden DFS traversiert (Unterordner zuerst, dann Dateien).
- Sortierung lexikografisch.
### End of media behavior
- When the last file ends:
- Playback stops
- Position saved at end
- State becomes IDLE
### Ende-des-Mediums
- Letzte Datei endet -> Playback stoppt, Position wird am Ende gespeichert.
### Buttons
- `next`: file_index + 1
- `prev`: if position > 3s then position=0, else file_index - 1
- `play_pause`: toggle PLAYING/PAUSED
- `volume_up` / `volume_down`: change volume within 0..max
- `prev`: wenn position > 3s -> position=0, sonst file_index - 1
- `play_pause`: toggelt PLAYING/PAUSED
- `volume_up` / `volume_down`: innerhalb 0..max
## Checklist
- ✅ Resume works for same UID
- ✅ Tag switching preserves per-UID resume
- ✅ Recursive order matches folder structure
- ✅ End-of-media stops playback and saves
## Checkliste
- ✅ Resume pro UID
- ✅ Tag-Wechsel speichert und resumed
- ✅ Rekursion entspricht Ordnerstruktur
- ✅ Ende-des-Mediums stoppt und speichert
## Troubleshooting
- No playback starts:
- Check tags in `box/data/box.json`.
- Ensure files exist under `box/data/media`.
- Duration is 0:
- Filenames not matched; mock uses hash-based durations for unknown names.
- Kein Playback:
- Tags in `box/data/box.json` pruefen.
- Dateien in `box/data/media` pruefen.
- Duration = 0:
- Unbekannte Dateinamen -> Hash-Dauer, nicht 0.
+38 -37
View File
@@ -1,60 +1,61 @@
# Security & Storage
## Purpose
Explain where data lives, what is encrypted, and how keys are derived.
## Zweck
Beschreibt Datenablage, Verschluesselung und Key-Ableitung.
## Prerequisites
- Box running at least once to create `box/data/`
## Voraussetzungen
- Box mindestens einmal gestartet
## Step-by-step
1) Start the box:
## Schritt-fuer-Schritt
1) Box starten:
```
python3 box/main.py run
```
2) Inspect data files:
2) Datenordner pruefen:
```
ls -la box/data
```
## Data layout
## Datenstruktur
```
box/data/
├── box.json # box_id, settings, tags (plaintext)
├── state.json # resume, wifi state (plaintext)
├── media/ # audio files
└── secrets.enc # encrypted secrets (JSON inside)
├── box.json # box_id, settings, tags (Klartext)
├── state.json # resume, wifi state (Klartext)
├── media/ # Audiodateien
└── secrets.enc # verschluesselte Secrets
```
## What is plaintext
## Klartext
- box_id
- box name
- volume
- resume positions
- playback state
- media paths
- UI state
- Box-Name
- Lautstaerke
- Resume-Positionen
- Playback-State
- Media-Pfade
- UI-Zustaende
## What is encrypted
- WiFi profiles
- Spotify tokens
- Server tokens (future)
## Verschluesselt
- WLAN-Profile
- Spotify Tokens
- Server Tokens (api_token)
- secret_seed (Fingerprint-Seed)
## Key derivation
## Key-Ableitung
- Key = PBKDF2-HMAC-SHA256(box_id + STATIC_SALT)
- Key is never stored on disk
- secrets.enc is not portable between boxes
- Key wird nie gespeichert
- secrets.enc ist nicht zwischen Boxen austauschbar
## Reset behavior
- WiFi reset: removes only wifi_profiles from secrets.enc
- Factory reset: delete `box/data/box.json`, `box/data/state.json`, `box/data/secrets.enc`
## Reset-Verhalten
- WLAN-Reset: nur wifi_profiles entfernen
- Factory-Reset: `box/data/box.json`, `box/data/state.json`, `box/data/secrets.enc` loeschen
## Checklist
-`secrets.enc` exists after WiFi/Spotify set
-`box.json` contains `box_id`
-No secrets appear in `/status`
## Checkliste
-`secrets.enc` existiert nach WiFi/Spotify set
-`box.json` enthaelt `box_id`
-Keine Secrets in `/status`
## Troubleshooting
- secrets.enc not created:
- Ensure you ran `wifi_add_profile` or `spotify_set_tokens`.
- secrets.enc unreadable after reset:
- Factory reset creates a new box_id and new key.
- secrets.enc fehlt:
- WiFi/Spotify set ausfuehren.
- secrets.enc unlesbar nach Reset:
- Neue box_id erzeugt neuen Key.
+31 -28
View File
@@ -1,40 +1,43 @@
# Setup WebGUI
# Setup-WebGUI
## Purpose
Configure WiFi profiles in the virtual box without touching the API or CLI.
## Zweck
WLAN-Profile in der virtuellen Box konfigurieren, ohne CLI oder API.
## Prerequisites
- Box running: `python3 box/main.py run`
## Voraussetzungen
- Box laeuft: `python3 box/main.py run`
## Step-by-step
1) Open the Setup UI:
## Schritt-fuer-Schritt
1) Setup-UI oeffnen:
```
http://127.0.0.1:9000/setup
```
2) Choose SSID and enter password.
3) Click "Verbinden".
4) Refresh `/status` to see `wifi_state` and `connected_ssid`.
2) SSID auswaehlen, Passwort eingeben.
3) "Verbinden" klicken.
4) Status pruefen:
```
curl http://127.0.0.1:8000/status
```
## Fields
- SSID: drop-down from mock scan
- Password: free text
## Felder
- SSID: Dropdown aus dem Mock-Scan
- Passwort: Freitext
## Expected results
- WiFi profile is saved (encrypted)
- `wifi_state` becomes `WIFI_ONLINE`
- `connected_ssid` matches selected SSID
## Erwartete Ergebnisse
- WLAN-Profil wird verschluesselt gespeichert
- `wifi_state` wird `WIFI_ONLINE`
- `connected_ssid` passt zur Auswahl
## Checklist
- ✅ Setup UI loads
- ✅ Submit stores a profile
-`/status` reflects `WIFI_ONLINE`
## Checkliste
- ✅ Setup-UI laedt
- ✅ Submit speichert Profil
-`/status` zeigt `WIFI_ONLINE`
## Troubleshooting
- Setup UI not reachable:
- Ensure Box is running and port `9000` is free.
- Submit does nothing:
- Check Box logs for errors; secrets file must be writable.
- Setup-UI nicht erreichbar:
- Port 9000 frei? Box laeuft?
- Submit ohne Wirkung:
- Logs pruefen, `box/data/secrets.enc` muss schreibbar sein.
## Notes
- Setup UI is write-enabled only when no profiles exist.
- When profiles exist, the page switches to read-only status.
## Hinweise
- Setup-UI ist nur schreibend, wenn keine Profile existieren.
- Bei vorhandenen Profilen ist die Seite read-only.
+8 -8
View File
@@ -1,13 +1,13 @@
# Storage (Legacy Notes)
# Storage (Legacy-Hinweis)
## Purpose
This file is kept for backward reference. Current storage documentation lives in `docs/security-storage.md`.
## Zweck
Dieses Dokument bleibt als Legacy-Hinweis bestehen. Die aktuelle Doku liegt in `docs/security-storage.md`.
## Prerequisites
- None
## Voraussetzungen
- Keine
## Step-by-step
- See `docs/security-storage.md`.
## Schritt-fuer-Schritt
- Siehe `docs/security-storage.md`.
## Troubleshooting
- If you ended up here from older links, update them to `docs/security-storage.md`.
- Falls Links hierher zeigen, bitte aktualisieren.
+16 -16
View File
@@ -1,22 +1,22 @@
# Virtual Box Flow (IDE)
# Virtuelle Box (IDE)
## Purpose
Describe the full, hardware-free box lifecycle used in development.
## Zweck
Beschreibt den vollstaendigen, hardwarefreien Box-Flow fuer die IDE.
## Prerequisites
## Voraussetzungen
- Python 3
- Box started with `python3 box/main.py run`
- Box gestartet mit `python3 box/main.py run`
## Step-by-step
1) Box starts and creates `box/data/box.json` if missing.
2) WiFi state is derived from stored profiles:
- none -> `WIFI_UNCONFIGURED`
3) Setup UI available at `http://127.0.0.1:9000/setup`.
4) After profile submission, box becomes `WIFI_ONLINE`.
5) API available at `http://127.0.0.1:8000`.
## Schritt-fuer-Schritt
1) Box erzeugt `box_id` in `box/data/box.json`.
2) WLAN-Status wird aus Profilen abgeleitet:
- keine Profile -> `WIFI_UNCONFIGURED`
3) Setup UI unter `http://127.0.0.1:9000/setup`.
4) Nach Submit wird `WIFI_ONLINE`.
5) API unter `http://127.0.0.1:8000`.
## Troubleshooting
- Setup UI not reachable:
- Ensure port 9000 is free and Box is running.
- API not reachable:
- Ensure port 8000 is free and Box is running.
- Setup UI nicht erreichbar:
- Port 9000 frei? Box laeuft?
- API nicht erreichbar:
- Port 8000 frei? Box laeuft?