|
| 1 | +# Loxone API Documentation |
| 2 | + |
| 3 | +Reverse-engineered API documentation extracted from the Loxone app bundle (`ftp://<loxone-miniserver>:21/web/commonv2.agz`) and the |
| 4 | +[Lox AudioServer](https://github.com/lox-audioserver/lox-audioserver) TypeScript source (`src/`) which is a community project — not an |
| 5 | +official Loxone document. |
| 6 | + |
| 7 | +**⚠️ Not an official Loxone document ⚠️** Use at your own risk. |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +## There are four distinct APIs |
| 12 | + |
| 13 | +``` |
| 14 | +┌─────────────────────────────────────────────────────────────────────────────┐ |
| 15 | +│ │ |
| 16 | +│ Loxone App ──── (1) ────► Loxone MiniServer │ |
| 17 | +│ │ │ │ |
| 18 | +│ │ (2) │ (3) │ |
| 19 | +│ ▼ ▼ │ |
| 20 | +│ Loxone AudioServer ◄──── (3) ──┘ │ |
| 21 | +│ │ │ |
| 22 | +│ │ (4) │ |
| 23 | +│ ▼ │ |
| 24 | +│ Lox AudioServer Admin UI (browser) │ |
| 25 | +│ │ |
| 26 | +└─────────────────────────────────────────────────────────────────────────────┘ |
| 27 | +``` |
| 28 | + |
| 29 | +| # | Link | Protocol | Spec file | |
| 30 | +| --- | ---------------------------------------------- | ------------------------------------------- | ------------------------------------ | |
| 31 | +| 1 | **Loxone App ↔ Loxone MiniServer** | WebSocket `jdev/sps/io/…` | `miniserver-api.yaml` | |
| 32 | +| 2 | **Loxone App ↔ Loxone AudioServer** | HTTP GET + WebSocket `audio/…` | `openapi.yaml` (App subset) | |
| 33 | +| 3 | **Loxone MiniServer ↔ Loxone AudioServer** | HTTP GET + WebSocket `audio/…` + `secure/…` | `openapi.yaml` (full, superset of 2) | |
| 34 | +| 4 | **Lox AudioServer Admin UI ↔ Lox AudioServer** | REST `/admin/api/…` with cookie auth | `audioserver-admin-api.yaml` | |
| 35 | + |
| 36 | +**Key insight for APIs 2 and 3:** The App and the MiniServer use the **same `audio/…` command protocol**, but connect on **different ports** |
| 37 | +and the MiniServer additionally sends a set of config-sync and pairing commands that the App never sends. |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## API 1: Loxone App ↔ Loxone MiniServer (`miniserver-api.yaml`) |
| 42 | + |
| 43 | +The Loxone App controls smart home devices by sending commands to the **Loxone MiniServer** over WebSocket or plain HTTP GET. |
| 44 | + |
| 45 | +**WebSocket endpoint:** `ws://{miniserverHost}/ws/rfc6455` **HTTP mirror:** `GET http://{miniserverHost}/{command}` |
| 46 | + |
| 47 | +### Authentication flow |
| 48 | + |
| 49 | +``` |
| 50 | +1. GET /jdev/cfg/api → discover firmware version & key algorithm |
| 51 | +2. GET /jdev/cfg/getPublicKey → fetch RSA-2048 public key |
| 52 | +3. GET /jdev/sys/gettoken/{hash}/{user}/{permission}/{clientId}/{clientName} |
| 53 | + → obtain auth token (hash = HMAC-SHA1 of credentials) |
| 54 | +4. GET /jdev/sys/authwithtoken/{token}/{user} |
| 55 | + → re-authenticate on reconnect |
| 56 | +5. GET /jdev/sys/refreshtoken/{token}/{user} |
| 57 | + → extend token before expiry |
| 58 | +6. GET /jdev/sys/killtoken/{token}/{user} |
| 59 | + → invalidate token on logout |
| 60 | +``` |
| 61 | + |
| 62 | +### Control command format |
| 63 | + |
| 64 | +``` |
| 65 | +jdev/sps/io/{uuid}/{command} |
| 66 | +jdev/sps/io/{uuid}/{command}/{value} |
| 67 | +``` |
| 68 | + |
| 69 | +`{uuid}` is the control's UUID from the structure file (`GET /data/LoxAPP3.json`). |
| 70 | + |
| 71 | +### State update push (WebSocket) |
| 72 | + |
| 73 | +The MiniServer pushes state changes as text frames: |
| 74 | + |
| 75 | +``` |
| 76 | +{stateUuid}:{value} |
| 77 | +``` |
| 78 | + |
| 79 | +State UUIDs are defined in `control.states` inside the structure file. |
| 80 | + |
| 81 | +### Selected control types and commands |
| 82 | + |
| 83 | +| Control type | Commands | |
| 84 | +| ----------------------- | -------------------------------------------------------------------------- | |
| 85 | +| `Jalousie` (blinds) | Up, Down, Stop, FullUp, FullDown, Auto, shade/{pos} | |
| 86 | +| `LightControllerV2` | On, Off, plus, minus, setMoodToId/{id}, addMood, removeMood, learn | |
| 87 | +| `Dimmer` | On, Off, setBrightness/{0-100} | |
| 88 | +| `ColorPickerV2` | color/{hsv\|lxt}, setFavoriteColor/{index}, setSaturation/{val} | |
| 89 | +| `IRoomControllerV2` | setOperatingMode/{0-5}, setComfortTemperature/{°C}, override/{mode}/{secs} | |
| 90 | +| `Switch` / `Pushbutton` | On, Off, pulse, pulse/{durationMs} | |
| 91 | +| `Alarm` | arm, disarm/{code}, delayedOn/{secs}, acknowledge | |
| 92 | +| `Gate` / `GarageDoor` | open, close, stop | |
| 93 | +| `DoorLock` | lock, unlock | |
| 94 | +| `Counter` | reset, set/{value} | |
| 95 | + |
| 96 | +--- |
| 97 | + |
| 98 | +## APIs 2 & 3: Loxone App ↔ Loxone AudioServer and Loxone MiniServer ↔ Loxone AudioServer (`openapi.yaml`) |
| 99 | + |
| 100 | +The AudioServer runs **two HTTP + WebSocket servers** that share identical route handling. The App connects to one port; the MiniServer |
| 101 | +connects to the other. |
| 102 | + |
| 103 | +| Server | Env var | Default port | Client | |
| 104 | +| --------- | ------------------------ | ------------ | ----------------- | |
| 105 | +| `appHttp` | `LOXONE_APP_PORT` | **7091** | Loxone App | |
| 106 | +| `msHttp` | `LOXONE_MINISERVER_PORT` | **7090** | Loxone MiniServer | |
| 107 | + |
| 108 | +The WebSocket **identification string** sent on connect differs: |
| 109 | + |
| 110 | +``` |
| 111 | +App: "LWSS V 16.1.10.01 | ~API:1.6~ | Session-Token: …" |
| 112 | +MiniServer: "MINISERVER V LWSS V 16.1.10.01 {mac} | ~API:1.6~ | Session-Token: …" |
| 113 | +``` |
| 114 | + |
| 115 | +When the **MiniServer** WebSocket connects, the AudioServer also emits a **server heartbeat** with current zone state — the App does not |
| 116 | +trigger this. |
| 117 | + |
| 118 | +### Shared commands (App + MiniServer) |
| 119 | + |
| 120 | +Both clients use these. All are HTTP GET; parameters are slash-separated. |
| 121 | + |
| 122 | +``` |
| 123 | +# Zone control |
| 124 | +GET /audio/{zoneId}/play |
| 125 | +GET /audio/{zoneId}/pause |
| 126 | +GET /audio/{zoneId}/volume/{0-100} |
| 127 | +GET /audio/{zoneId}/shuffle/{enable|disable} |
| 128 | +GET /audio/{zoneId}/repeat/{0|1|3} |
| 129 | +GET /audio/{zoneId}/queueplus |
| 130 | +GET /audio/{zoneId}/queueminus |
| 131 | +GET /audio/{zoneId}/position/{seconds} |
| 132 | +GET /audio/{zoneId}/status |
| 133 | +GET /audio/{zoneId}/getqueue/{start}/{limit} |
| 134 | +GET /audio/{zoneId}/serviceplay/{service}/{user}/{audiopath} |
| 135 | +GET /audio/{zoneId}/roomfav/play/{slot} |
| 136 | +
|
| 137 | +# Config / management |
| 138 | +GET /audio/cfg/getservices |
| 139 | +GET /audio/cfg/getplaylists2 |
| 140 | +GET /audio/cfg/getroomfavs/{playerId} |
| 141 | +GET /audio/cfg/search/{service}/{user}/{tag}/{query}/{limit}/{offset} |
| 142 | +GET /audio/cfg/globalsearch |
| 143 | +GET /audio/cfg/spotifyconnect/enable|disable |
| 144 | +GET /audio/cfg/airplay/enable|disable |
| 145 | +GET /audio/cfg/tunein/enable|disable |
| 146 | +… (95+ shared endpoints total — see openapi.yaml) |
| 147 | +``` |
| 148 | + |
| 149 | +### MiniServer-only commands |
| 150 | + |
| 151 | +These are sent **only by the MiniServer** on its dedicated port — the App never sends them. |
| 152 | + |
| 153 | +#### Pairing & Auth (`secure/…`) |
| 154 | + |
| 155 | +Sent immediately after WebSocket connect as a bootstrap handshake: |
| 156 | + |
| 157 | +| Command | Purpose | |
| 158 | +| -------------------------- | --------------------------------- | |
| 159 | +| `secure/info/pairing` | Query paired MiniServer serial | |
| 160 | +| `secure/hello/{publicKey}` | Key-exchange bootstrap | |
| 161 | +| `secure/authenticate` | Auth acknowledgement | |
| 162 | +| `secure/init` | Session init (returns legacy JWT) | |
| 163 | + |
| 164 | +#### Config Sync (`audio/cfg/…`) |
| 165 | + |
| 166 | +Sent by the MiniServer to push or pull configuration state: |
| 167 | + |
| 168 | +| Command | Purpose | |
| 169 | +| -------------------------------------- | ----------------------------------------------------------- | |
| 170 | +| `audio/cfg/ready` | MiniServer signals startup complete; gets session timestamp | |
| 171 | +| `audio/cfg/getconfig` | Fetch config CRC-32 to detect stale cache | |
| 172 | +| `audio/cfg/setconfig` | Push zone UUIDs and names to AudioServer | |
| 173 | +| `audio/cfg/setconfigtimestamp` | Sync config version timestamp | |
| 174 | +| `audio/cfg/miniservertime` | Sync wall-clock time | |
| 175 | +| `audio/cfg/volumes` | Bulk-set initial zone volumes | |
| 176 | +| `audio/cfg/playername` | Set zone display name | |
| 177 | +| `audio/cfg/identify` | Send MiniServer serial + firmware version | |
| 178 | +| `audio/cfg/defaultvolume/{zone}/{vol}` | Set startup volume | |
| 179 | +| `audio/cfg/maxvolume/{zone}/{vol}` | Set volume cap | |
| 180 | +| `audio/cfg/eventvolumes/{zone}/{vol}` | Set alert volume | |
| 181 | +| `audio/cfg/miniserverip` | _(placeholder — ignored)_ | |
| 182 | +| `audio/cfg/miniserverversion` | _(placeholder — ignored)_ | |
| 183 | +| `audio/cfg/timezone` | _(placeholder — ignored)_ | |
| 184 | +| `audio/cfg/presencemode` | _(placeholder — ignored)_ | |
| 185 | +| `audio/cfg/groupopts` | _(placeholder — ignored)_ | |
| 186 | +| `audio/cfg/speakertype` | _(placeholder — ignored)_ | |
| 187 | +| `audio/cfg/playeropts` | _(placeholder — ignored)_ | |
| 188 | + |
| 189 | +### Response format (both clients) |
| 190 | + |
| 191 | +```json |
| 192 | +{ "{command}_result": <payload>, "command": "<original-path>" } |
| 193 | +``` |
| 194 | + |
| 195 | +### WebSocket events (server → all connected clients) |
| 196 | + |
| 197 | +| Event key | Trigger | |
| 198 | +| ----------------------------- | ----------------------- | |
| 199 | +| `audio_event` | Zone state changed | |
| 200 | +| `audio_queue_event` | Queue contents changed | |
| 201 | +| `roomfavchanged_event` | Room favorites changed | |
| 202 | +| `recentlyplayedchanged_event` | Recently played changed | |
| 203 | +| `rescan_event` | Library scan progress | |
| 204 | +| `globalsearch_result` | Search result ready | |
| 205 | +| `audio_sync_event` | Sync group changed | |
| 206 | +| `lineinchanged_event` | Line-in inputs changed | |
| 207 | + |
| 208 | +--- |
| 209 | + |
| 210 | +## API 4: Lox AudioServer Admin UI ↔ Loxone AudioServer (`audioserver-admin-api.yaml`) |
| 211 | + |
| 212 | +Internal REST API used by the AudioServer's browser-based Admin UI. |
| 213 | + |
| 214 | +**Base URL:** `http://{host}:7091` **Auth:** Cookie session via `POST /admin/api/auth/login` (TTL 12 h) |
| 215 | + |
| 216 | +| Group | Base path | Notes | |
| 217 | +| ------------------- | ----------------------------------------------------------- | -------------------------------------------------------------- | |
| 218 | +| Auth | `/admin/api/auth/…` | login, logout, me | |
| 219 | +| Server info | `/admin/api/info` | public, no auth | |
| 220 | +| Zone admin states | `/admin/api/zones/…` | tech details, bulk purge | |
| 221 | +| Zone groups | `/admin/api/groups` | group listing | |
| 222 | +| Transport discovery | `/admin/api/transports/…` | AirPlay, Cast, DLNA, Sonos, Snapcast, Spotify, Music Assistant | |
| 223 | +| Snapcast clients | `/admin/api/snapcast/…` | stream assignment | |
| 224 | +| Spotify | `/admin/api/spotify/…` | OAuth, librespot, bridges | |
| 225 | +| Apple Music | `/admin/api/applemusic/…` | Widevine key upload | |
| 226 | +| Line-In bridges | `/api/linein/…` | bridge register/status/ingest | |
| 227 | +| Audio streams | `/streams/{zone}/{id}.{format}` | mp3/wav/pcm/aac/flac | |
| 228 | +| Media library | `/admin/api/content/library/…` | upload, rescan, storages | |
| 229 | +| Custom radio | `/admin/api/content/radio/…` | add/delete/validate | |
| 230 | +| Logs | `/admin/api/logs/…` | list, SSE stream, level control | |
| 231 | +| Config | `/admin/api/config/…` | CRUD for server config | |
| 232 | +| Alert sounds | `/admin/api/alerts/…` | upload, list, revert | |
| 233 | +| Updates | `/admin/api/adminui/update`, `/admin/api/components/update` | | |
| 234 | + |
| 235 | +--- |
| 236 | + |
| 237 | +## Viewing the docs |
| 238 | + |
| 239 | +Open `index.html` in a browser. It loads the YAML specs locally via [Swagger UI](https://swagger.io/tools/swagger-ui/) (fetched from |
| 240 | +unpkg.com — requires internet). |
| 241 | + |
| 242 | +The landing page has **three tabs**: |
| 243 | + |
| 244 | +| Tab | Spec file | Covers | |
| 245 | +| -------------------------------------- | ---------------------------- | ------------------- | |
| 246 | +| **MiniServer API** | `miniserver-api.yaml` | API 1 | |
| 247 | +| **AudioServer – App & MiniServer API** | `openapi.yaml` | APIs 2 & 3 combined | |
| 248 | +| **AudioServer – Admin API** | `audioserver-admin-api.yaml` | API 4 | |
| 249 | + |
| 250 | +In the **App & MiniServer API** tab, routes are grouped by tag: |
| 251 | + |
| 252 | +- Tags **without a prefix** → used by both App and MiniServer |
| 253 | +- Tags starting with **`MiniServer ›`** → MiniServer only |
| 254 | + |
| 255 | +--- |
| 256 | + |
| 257 | +## Files |
| 258 | + |
| 259 | +``` |
| 260 | +api-docs/ |
| 261 | +├── index.html # Swagger UI with 3-tab spec switcher |
| 262 | +├── openapi.yaml # APIs 2 & 3 — App + MiniServer audio protocol |
| 263 | +├── miniserver-api.yaml # API 1 — MiniServer smart home commands |
| 264 | +├── audioserver-admin-api.yaml # API 4 — AudioServer Admin REST API |
| 265 | +└── README.md # This file |
| 266 | +``` |
| 267 | + |
| 268 | +## Sources |
| 269 | + |
| 270 | +| File / directory | Used for | |
| 271 | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | |
| 272 | +| `ftp://<loxone-miniserver>:21/web/commonv2.agz` | MiniServer control types, `audio/…` schemas, WebSocket events | |
| 273 | +| `https://github.com/lox-audioserver/lox-audioserver/src/adapters/loxone/commands/router/routeRegistry.ts` | Exact route list for both App and MiniServer | |
| 274 | +| `https://github.com/lox-audioserver/lox-audioserver/src/adapters/loxone/http/loxoneHttpService.ts` | Two-port server architecture, heartbeat behaviour | |
| 275 | +| `https://github.com/lox-audioserver/lox-audioserver/src/config/loxone.ts` | Port names (`appHttp`/`msHttp`), identification strings | |
| 276 | +| `https://github.com/lox-audioserver/lox-audioserver/src/adapters/loxone/commands/handlers/` | Handler logic, MiniServer-only config sync | |
| 277 | +| `https://github.com/lox-audioserver/lox-audioserver/src/adapters/http/adminApi/adminApiHandler.ts` | Admin API routes | |
| 278 | +| `https://github.com/lox-audioserver/lox-audioserver/src/adapters/http/streams/` | Audio stream and proxy endpoints | |
| 279 | +| `https://github.com/lox-audioserver/lox-audioserver/src/adapters/http/lineInApi/` | Line-In bridge API | |
0 commit comments