-
Notifications
You must be signed in to change notification settings - Fork 0
PRESET_PLAYBACK
Stand: 2026-02-22
Status: Funktionsfähig (MVP)
Dieses Dokument beschreibt, wie OpenCloudTouch (OCT) die Preset-Wiedergabe auf Bose SoundTouch-Geräten nach der Abschaltung der Bose Cloud ermöglicht.
SoundTouch-Geräte unterstützen 6 Hardware-Presets (Tasten 1-6). Nach der Cloud-Abschaltung funktionieren die ursprünglichen TuneIn-basierten Presets nicht mehr. OCT bietet eine Alternative über die LOCAL_INTERNET_RADIO-Quelle mit direkten Stream-URLs.
┌─────────────────────────────────────────────────────────────────┐
│ SoundTouch-Gerät │
│ 1. Ruft /bmx/registry/v1/services beim Booten ab │
│ 2. Speichert Presets lokal (XML) │
│ 3. Bei Preset-Taste: ruft die Location-URL auf │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OCT-Server │
│ - /bmx/registry/v1/services → Service-Registry │
│ - /core02/svc-bmx-adapter-orion/prod/orion/station?data=... │
│ → Dekodiert Base64-JSON, gibt BmxPlaybackResponse zurück │
└─────────────────────────────────────────────────────────────────┘
<preset id="4">
<ContentItem source="LOCAL_INTERNET_RADIO"
type="stationurl"
location="http://content.api.bose.io:7777/core02/svc-bmx-adapter-orion/prod/orion/station?data={BASE64_DATA}"
sourceAccount=""
isPresetable="true">
<itemName>Station Name</itemName>
</ContentItem>
</preset>Der data-Query-Parameter enthält Base64url-kodiertes JSON:
{
"streamUrl": "http://absolut-relax.live-sm.absolutradio.de/absolut-relax/stream/mp3",
"name": "Absolut Relax",
"imageUrl": "https://example.com/logo.png"
}Kodierung in PowerShell:
$json = '{"streamUrl":"http://stream.example.com/radio.mp3","name":"My Station"}'
$base64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($json))
# Result: eyJzdHJlYW1VcmwiOiJodHRwOi8vc3RyZWFtLmV4YW1wbGUuY29tL3JhZGlvLm1wMyIsIm5hbWUiOiJNeSBTdGF0aW9uIn0=Kodierung in Python:
import base64
import json
data = {"streamUrl": "http://stream.example.com/radio.mp3", "name": "My Station"}
base64_data = base64.urlsafe_b64encode(json.dumps(data).encode()).decode()Endpunkt: GET /bmx/registry/v1/services
Wird vom Gerät beim Booten aufgerufen. Gibt die verfügbaren Services zurück.
Antwort:
{
"_links": {
"bmx_services_availability": {"href": "../servicesAvailability"}
},
"askAgainAfter": 60000,
"bmx_services": [
{
"_links": {...},
"id": {"name": "orion", "value": 999},
"baseUrl": "http://192.168.178.108:7777/core02/svc-bmx-adapter-orion/prod",
"assets": {"name": "OCT Radio", "description": "OpenCloudTouch"},
"streamTypes": ["liveRadio"],
"askAdapter": false
}
]
}Endpunkt: GET /core02/svc-bmx-adapter-orion/prod/orion/station?data={base64}
Wird vom Gerät aufgerufen, wenn eine Preset-Taste gedrückt wird.
Anfrage:
-
data(Query-Parameter): Base64url-kodiertes JSON mitstreamUrl,name,imageUrl
Antwort:
{
"audio": {
"hasPlaylist": true,
"isRealtime": true,
"maxTimeout": 60,
"streamUrl": "http://absolut-relax.live-sm.absolutradio.de/absolut-relax/stream/mp3",
"streams": [
{
"hasPlaylist": true,
"isRealtime": true,
"streamUrl": "http://absolut-relax.live-sm.absolutradio.de/absolut-relax/stream/mp3"
}
]
},
"imageUrl": "https://example.com/logo.png",
"name": "Absolut Relax",
"streamType": "liveRadio",
"_links": {
"bmx_nowplaying": {"href": "http://192.168.178.108:7777/bmx/orion/now-playing"},
"bmx_reporting": {"href": "http://192.168.178.108:7777/bmx/orion/reporting"}
}
}Das Gerät muss Bose-Domains zum OCT-Server umleiten:
192.168.178.108 streaming.bose.com
192.168.178.108 content.api.bose.io
192.168.178.108 events.api.bosecm.com
192.168.178.108 bmx.bose.com
Wichtige Einstellungen in der /mnt/nv/product.xml des Geräts:
<margeURL>http://content.api.bose.io:7777</margeURL>
<statsServerUrl>http://content.api.bose.io:7777/stats</statsServerUrl>import base64
import json
stream_data = {
"streamUrl": "http://absolut-relax.live-sm.absolutradio.de/absolut-relax/stream/mp3",
"name": "Absolut Relax",
"imageUrl": ""
}
encoded = base64.urlsafe_b64encode(json.dumps(stream_data).encode()).decode()
print(encoded)
# eyJzdHJlYW1VcmwiOiJodHRwOi8vYWJzb2x1dC1yZWxheC5saXZlLXNtLmFic29sdXRyYWRpby5kZS9hYnNvbHV0LXJlbGF4L3N0cmVhbS9tcDMiLCJuYW1lIjoiQWJzb2x1dCBSZWxheCIsImltYWdlVXJsIjoiIn0=$preset = @"
<preset id="4">
<ContentItem source="LOCAL_INTERNET_RADIO"
type="stationurl"
location="http://content.api.bose.io:7777/core02/svc-bmx-adapter-orion/prod/orion/station?data=eyJzdHJlYW1VcmwiOiJodHRwOi8vYWJzb2x1dC1yZWxheC5saXZlLXNtLmFic29sdXRyYWRpby5kZS9hYnNvbHV0LXJlbGF4L3N0cmVhbS9tcDMiLCJuYW1lIjoiQWJzb2x1dCBSZWxheCIsImltYWdlVXJsIjoiIn0="
sourceAccount=""
isPresetable="true">
<itemName>Absolut Relax OCT</itemName>
</ContentItem>
</preset>
"@
Invoke-RestMethod -Uri "http://192.168.178.79:8090/preset" -Method Post -Body $preset -ContentType "application/xml"$key = '<key state="press" sender="Gabbo">PRESET_4</key>'
Invoke-RestMethod -Uri "http://192.168.178.79:8090/key" -Method Post -Body $key -ContentType "application/xml"Invoke-RestMethod -Uri "http://192.168.178.79:8090/now_playing"
# Gibt zurück: source=LOCAL_INTERNET_RADIO, playStatus=PLAY_STATEDie ursprüngliche TuneIn-Quelle (source="TUNEIN") erfordert zusätzliche Bose-proprietäre Endpunkte, die komplex per Reverse Engineering nachzubilden sind. Beim Versuch der TuneIn-Wiedergabe gibt das Gerät HTTP 500 zurück.
LOCAL_INTERNET_RADIO ist einfacher:
- Das Gerät ruft die
location-URL direkt auf - OCT dekodiert den Base64-Payload
- Gibt die Stream-URL im BMX-Format zurück
- Das Gerät spielt den Stream ab
- hosts-Datei prüfen:
ssh root@device "cat /etc/hosts" - OCT-Erreichbarkeit testen:
curl http://oct-server:7777/health - Gerät nach Änderungen an der hosts-Datei neu starten
- OCT-Logs prüfen:
podman logs opencloudtouch-local - Orion-Endpunkt verifizieren:
curl "http://oct:7777/core02/svc-bmx-adapter-orion/prod/orion/station?data=..." - Sicherstellen, dass die Stream-URL eine gültige MP3/AAC-URL ist
- Prüfen, ob der
/bmx/registry/v1/services-Endpunkt gültiges JSON zurückgibt - Sicherstellen, dass das
_links-Feld vorhanden ist (vom Gerät benötigt) - Prüfen, ob
askAgainAfternicht zu kurz ist (mindestens 60000ms)
- apps/backend/src/opencloudtouch/bmx/routes.py – BMX-Endpunkt-Implementierung
- apps/backend/tests/unit/bmx/test_orion_adapter.py – Unit-Tests
- docs/USB_SETUP_LOG.md – Gerätekonfiguration über USB
🇩🇪 Benutzerhandbuch
🇬🇧 User Guide
Development
API & Architecture
- REST API
- ADR 001 Clean Architecture
- ADR 002 FastAPI App State
- ADR 003 SSDP Discovery
- ADR 004 React/TS/Vite
Technical Reference
Legal