Upload
This commit is contained in:
+34
-34
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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?
|
||||
|
||||
Reference in New Issue
Block a user