Summary
BluOS network audio player supporting HTTP-based control for playback, volume, play queue management, presets, content browsing, player grouping, and input selection. All commands are HTTP GET requests (except reboot which is POST) to http://<player_ip>:<port>/<request>, returning UTF-8 XML responses. Default port is 11000 (CI580 uses 11000/11010/11020/11030 per node). LSDP (Lenbrook Service Discovery Protocol) provides UDP-based player discovery on port 11430 as an mDNS alternative.
Transport
protocols:
- http
- udp
addressing:
base_url: "http://{player_ip}:11000"
port: 11000
auth:
type: none # inferred: no auth procedure in source
udp:
port: 11430 # LSDP discovery protocol broadcast port
mode: broadcast # inferred: source states UDP broadcast packets to/from port 11430
notes: "Lenbrook Service Discovery Protocol (LSDP). IANA-assigned to Lenbrook March 27 2014. Used as mDNS alternative for player discovery."
Traits
traits:
- queryable # inferred: /Status, /SyncStatus, /Volume queries return state
- levelable # inferred: volume control with level, dB absolute, dB relative
- routable # inferred: input selection and player grouping commands
Actions
actions:
- id: status_query
label: Playback Status Query
kind: query
command: "/Status"
params:
- name: timeout
type: integer
description: "Long-poll timeout in seconds (recommended 100)"
- name: etag
type: string
description: "etag from previous /Status response for long-polling"
notes: "Returns volume, playback state, track info, shuffle, repeat, etc. Long-polling returns only on change or timeout."
- id: sync_status_query
label: Player and Group Sync Status Query
kind: query
command: "/SyncStatus"
params:
- name: timeout
type: integer
description: "Long-poll timeout in seconds (recommended 180)"
- name: etag
type: string
description: "etag from previous /SyncStatus response for long-polling"
notes: "Returns player info, grouping, volume. Long-polling returns only on change or timeout."
- id: set_volume_level
label: Set Volume Level
kind: action
command: "/Volume?level={level}"
params:
- name: level
type: integer
description: "Absolute volume level 0-100"
- name: tell_slaves
type: integer
description: "0 = this player only, 1 = all grouped players"
- id: set_volume_db_absolute
label: Set Volume dB Absolute
kind: action
command: "/Volume?abs_db={db}"
params:
- name: db
type: number
description: "Absolute volume in dB (within configured range, typically -80..0)"
- name: tell_slaves
type: integer
description: "0 = this player only, 1 = all grouped players"
- id: set_volume_db_relative
label: Set Volume dB Relative
kind: action
command: "/Volume?db={delta_db}"
params:
- name: delta_db
type: number
description: "Relative volume change in dB (positive or negative, typical step 2)"
- name: tell_slaves
type: integer
description: "0 = this player only, 1 = all grouped players"
notes: "Positive values increase volume, negative decrease. Typical step is 2dB."
- id: volume_query
label: Volume Query
kind: query
command: "/Volume"
params:
- name: timeout
type: integer
description: "Optional long-poll timeout in seconds"
- name: etag
type: string
description: "Optional etag from previous /Volume response for long-polling"
notes: "Pure query form of /Volume. Source 3.1 states /Volume both queries and sets volume; query supports long-polling. Returns volume, db, mute, muteDb, muteVolume, offsetDb, etag."
- id: mute_on
label: Mute On
kind: action
command: "/Volume?mute=1"
params: []
- id: mute_off
label: Mute Off
kind: action
command: "/Volume?mute=0"
params: []
- id: play
label: Play
kind: action
command: "/Play"
params:
- name: seek
type: integer
description: "Jump to position in seconds (only if /Status has totlen)"
- name: id
type: integer
description: "Track ID in queue (used with seek)"
notes: "Cannot use seek with inputType/index parameters."
- id: play_url
label: Play Stream URL
kind: action
command: "/Play?url={encodedStreamURL}"
params:
- name: url
type: string
description: "URL-encoded stream URL to play"
- id: pause
label: Pause
kind: action
command: "/Pause"
params: []
notes: "Cancels alarm timeout if alarm is playing."
- id: pause_toggle
label: Pause Toggle
kind: action
command: "/Pause?toggle=1"
params: []
- id: stop
label: Stop
kind: action
command: "/Stop"
params: []
notes: "Cancels alarm timeout if alarm is playing."
- id: skip
label: Skip to Next Track
kind: action
command: "/Skip"
params: []
notes: "Only works when playing from play queue (no streamUrl in /Status). Wraps from last to first track in queue regardless of repeat setting."
- id: back
label: Back to Previous Track
kind: action
command: "/Back"
params: []
notes: "If track playing >4s, returns to start. Otherwise goes to previous track. Wraps from first to last in queue."
- id: shuffle
label: Set Shuffle
kind: action
command: "/Shuffle?state={state}"
params:
- name: state
type: integer
description: "0 = shuffle off, 1 = shuffle on"
- id: repeat
label: Set Repeat
kind: action
command: "/Repeat?state={state}"
params:
- name: state
type: integer
description: "0 = repeat queue, 1 = repeat track, 2 = repeat off"
- id: streaming_action
label: Streaming Radio Action
kind: action
command: "/Action?service={service}&{action_param}={action_value}"
params:
- name: service
type: string
description: "Music service name (e.g. Slacker)"
- name: action_param
type: string
description: "Action name: skip, love, ban"
- name: action_value
type: string
description: "Action value from /Status <action> element"
notes: "URL is dynamic, taken from <actions> element in /Status response."
- id: playlist_list
label: List Play Queue
kind: query
command: "/Playlist"
params: []
notes: "Returns all tracks in play queue. Use start/end params for pagination. Not recommended without length or start/end params (very long response)."
- id: playlist_status
label: Play Queue Status
kind: query
command: "/Playlist?length=1"
params: []
notes: "Returns queue status without track details."
- id: playlist_range
label: List Play Queue Range
kind: query
command: "/Playlist?start={first}&end={last}"
params:
- name: start
type: integer
description: "First entry index (0-based)"
- name: end
type: integer
description: "Last entry index"
- id: delete_track
label: Delete Track from Queue
kind: action
command: "/Delete?id={position}"
params:
- name: id
type: integer
description: "Track position in queue to delete"
- id: move_track
label: Move Track in Queue
kind: action
command: "/Move?new={destination}&old={origin}"
params:
- name: new
type: integer
description: "New position"
- name: old
type: integer
description: "Old position"
- id: clear_queue
label: Clear Play Queue
kind: action
command: "/Clear"
params: []
- id: save_queue
label: Save Play Queue as Playlist
kind: action
command: "/Save?name={playlist_name}"
params:
- name: name
type: string
description: "Name for the saved playlist"
- id: list_presets
label: List Presets
kind: query
command: "/Presets"
params: []
- id: load_preset
label: Load Preset
kind: action
command: "/Preset?id={presetId}"
params:
- name: id
type: string
description: "Preset ID number, +1 for next, -1 for previous"
notes: "Preset numbers need not be sequential. Wraps top-to-bottom and bottom-to-top."
- id: browse
label: Browse Content
kind: query
command: "/Browse"
params:
- name: key
type: string
description: "Browse key from previous response (URL-encoded). Omit for top level."
- name: withContextMenuItems
type: integer
description: "Set to 1 to include inline context menu"
notes: "Error responses are enclosed in <error> root element with <message> and zero or more <detail> text nodes."
- id: search_content
label: Search Music Content
kind: query
command: "/Browse?key={key}&q={searchText}"
params:
- name: key
type: string
description: "Value from searchKey attribute from earlier browse response. Omit for top-level search."
- name: q
type: string
description: "Search string. URL-encoded."
notes: "Source section 7.2 documents search as distinct command from browse. Returns top-level search categories (Artists/Albums/Songs/Playlists); further results require /Browse with browseKey."
- id: group_add_slave
label: Group Player (Single)
kind: action
command: "/AddSlave?slave={slave_ip}&port={slave_port}"
params:
- name: slave
type: string
description: "IP address of secondary player"
- name: port
type: integer
description: "Port of secondary player (default 11000)"
- name: group
type: string
description: "Optional group name"
- id: group_add_slaves_multi
label: Group Players (Multiple)
kind: action
command: "/AddSlave?slaves={slave_ips}&ports={slave_ports}"
params:
- name: slaves
type: string
description: "Comma-separated IP addresses of secondary players"
- name: ports
type: string
description: "Comma-separated port numbers of secondary players"
- id: ungroup_remove_slave
label: Ungroup Player (Single)
kind: action
command: "/RemoveSlave?slave={slave_ip}&port={slave_port}"
params:
- name: slave
type: string
description: "IP of secondary player to remove"
- name: port
type: integer
description: "Port of secondary player to remove"
notes: "If removing primary from group of 3+, primary ungroups and remaining slaves form new group."
- id: ungroup_remove_slaves_multi
label: Ungroup Players (Multiple)
kind: action
command: "/RemoveSlave?slaves={slave_ips}&ports={slave_ports}"
params:
- name: slaves
type: string
description: "Comma-separated IP addresses to remove"
- name: ports
type: string
description: "Comma-separated port numbers to remove"
- id: reboot
label: Reboot Player
kind: action
command: "POST /reboot"
params:
- name: yes
type: string
description: "Any value (e.g. 1) to confirm reboot"
notes: "HTTP POST, not GET. Soft reboot only."
- id: doorbell_chime
label: Play Doorbell Chime
kind: action
command: "/Doorbell?play=1"
params: []
- id: select_active_input
label: Select Active Input
kind: action
command: "/Play?url={URL_value}"
params:
- name: url
type: string
description: "URL from /RadioBrowse?service=Capture response item"
notes: "First query /RadioBrowse?service=Capture to get available input URLs. Only source supporting BluOS HUB inputs selection."
- id: radio_browse_capture
label: Browse Capture Inputs
kind: query
command: "/RadioBrowse?service=Capture"
params: []
notes: "Returns available input sources with URL values for active input selection."
- id: settings_capture_query
label: Capture Settings Query
kind: query
command: "/Settings?id=capture&schemaVersion=32"
params:
- name: id
type: string
description: "Setting group id (e.g. capture)"
- name: schemaVersion
type: integer
description: "Schema version (32 is latest per source)"
notes: "Returns input list for inputIndex lookup. Referenced by source section 11.2 step 1 for external input selection."
- id: select_external_input_index
label: Select External Input by Index
kind: action
command: "/Play?inputIndex={IndexId}"
params:
- name: inputIndex
type: integer
description: "1-based index from /Settings?id=capture response (Bluetooth excluded)"
notes: "BluOS firmware newer than v3.8.0 and older than v4.2.0 only. Recommended for CI external input selection."
- id: select_external_input_type_index
label: Select External Input by Type Index
kind: action
command: "/Play?inputTypeIndex={typeIndex}"
params:
- name: inputTypeIndex
type: string
description: "Format: {type}-{index}. Types: spdif, analog, coax, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone. Index starts at 1."
notes: "BluOS firmware v4.2.0 or newer."
- id: change_bluetooth_mode
label: Change Bluetooth Mode
kind: action
command: "/audiomodes?bluetoothAutoplay={value}"
params:
- name: bluetoothAutoplay
type: integer
description: "0=Manual, 1=Automatic, 2=Guest, 3=Disabled"
- id: lsdp_query
label: LSDP Discovery Query
kind: query
command: "UDP broadcast port 11430 - Message Type 0x51 (Q) broadcast or 0x52 (R) unicast"
params:
- name: message_type
type: string
description: "\"Q\" (0x51) for broadcast response, \"R\" (0x52) for unicast response"
- name: count
type: integer
description: "Number of classes to query (1 byte)"
- name: classes
type: array
description: "16-bit class identifiers (e.g. 0x0001 BluOS Player, 0x0002 BluOS Server, 0x0003 secondary, 0x0008 BluOS Hub, 0xFFFF All Classes)"
notes: "Lenbrook Service Discovery Protocol (LSDP). Packet header: Length (1B), Magic 'LSDP' (4B ASCII), Protocol Version 1 (1B). All multi-byte values big-endian. On startup send 7 query packets at t=[0,1,2,3,5,7,10s] + 0-250ms random."
- id: lsdp_announce
label: LSDP Discovery Announce
kind: event
command: "UDP broadcast port 11430 - Message Type 0x41 (A)"
params:
- name: node_id
type: string
description: "Unique node ID (usually MAC address), variable length prefixed by Node ID Length (1B)"
- name: address
type: string
description: "IP address of node, variable length prefixed by Address Length (1B, =4 for IPv4)"
- name: records
type: array
description: "Announce records: Class (2B) + TXT records (Count 1B + key/value length-prefixed pairs)"
notes: "Sent in response to LSDP Query after random delay 0-750ms. Steady-state broadcast ~every 57s + 0-6s random. Sent 7x at startup with intervals [0,1,2,3,5,7,10s] + 0-250ms random."
- id: lsdp_delete
label: LSDP Discovery Delete
kind: event
command: "UDP broadcast port 11430 - Message Type 0x44 (D)"
params:
- name: node_id
type: string
description: "Unique node ID of node sending delete (usually MAC address)"
- name: count
type: integer
description: "Number of classes to follow"
- name: classes
type: array
description: "16-bit class identifiers being withdrawn"
notes: "Broadcast when service no longer available. Sent 7x at startup intervals per LSDP timing."
Feedbacks
feedbacks:
- id: playback_state
type: enum
values: [play, pause, stop, stream, connecting]
description: "Current player state from /Status response <state> element. play and stream have same meaning."
- id: volume_level
type: integer
values: "0..100 or -1 (fixed)"
description: "Current volume percentage from /Status or /Volume response"
- id: volume_db
type: number
description: "Current volume in dB from /Volume response"
- id: volume_offset_db
type: number
description: "Volume offset in dB from /Volume response offsetDb attribute"
- id: mute_state
type: enum
values: ["0", "1"]
description: "Mute state from /Status or /Volume. 1=muted, 0=unmuted."
- id: mute_volume
type: integer
description: "Unmuted volume level (0..100) from /Volume response when muted"
- id: mute_db
type: number
description: "Unmuted volume in dB from /Volume or /Status response when muted"
- id: shuffle_state
type: enum
values: ["0", "1"]
description: "Shuffle state from /Status response"
- id: repeat_state
type: enum
values: ["0", "1", "2"]
description: "Repeat state: 0=repeat queue, 1=repeat track, 2=off"
- id: now_playing
type: object
description: "Track metadata from /Status: title1, title2, title3, album, artist, name"
- id: now_playing_twoline
type: object
description: "Two-line now-playing metadata from /Status: twoline_title1, twoline_title2 (when present, MUST be used for two-line UI)"
- id: stream_url_flag
type: boolean
description: "Presence of <streamUrl> in /Status. If present: queue not source, shuffle/repeat not relevant, next/prev unavailable."
- id: can_seek
type: boolean
description: "From /Status canSeek. If 1, scrubbing via /Play?seek supported in range 0..totlen."
- id: can_move_playback
type: boolean
description: "From /Status canMovePlayback. True if current content can be moved to another player."
- id: sleep_timer
type: integer
description: "Minutes remaining before sleep timer activates from /Status <sleep>"
- id: alarm_seconds_remaining
type: integer
description: "Seconds before alarm-triggered playback stops (from /Status, when playback is from alarm)"
- id: audio_quality
type: string
description: "Quality of playing source from /Status: cd, hd, dolbyAudio, mqa, mqaAuthored, or numeric bitrate"
- id: stream_format
type: string
description: "Audio format from /Status streamFormat (e.g. MP3 320 kb/s)"
- id: battery_state
type: object
description: "From /Status or /SyncStatus when player has battery: level (percent), charging (1/0), icon URL"
- id: sync_status
type: object
description: "Player/group info from /SyncStatus: name, model, modelName, brand, volume, group, master, slaves, zone, zoneMaster, zoneSlave, initialized, mac, schemaVersion, icon"
- id: group_name
type: string
description: "Group name from /Status groupName. Player must be primary in group."
- id: group_volume
type: integer
description: "Group volume level from /Status groupVolume. Player must be primary in group."
- id: queue_info
type: object
description: "Play queue info from /Playlist: name, length, id, modified"
- id: presets_list
type: object
description: "Preset list from /Presets: prid, preset items with id/name/url/image"
- id: etag
type: string
description: "Opaque etag from /Status or /SyncStatus for long-polling change detection"
Variables
variables:
- id: volume
type: integer
min: 0
max: 100
description: "Player volume level percentage. -1 means fixed volume."
- id: volume_db
type: number
description: "Player volume in dB. Range typically -80..0."
- id: volume_offset_db
type: number
description: "Volume offset in dB from /Volume offsetDb"
- id: mute
type: boolean
description: "Mute state"
- id: shuffle
type: boolean
description: "Shuffle state"
- id: repeat
type: integer
description: "0=repeat queue, 1=repeat track, 2=off"
- id: sleep_timer
type: integer
description: "Minutes remaining before sleep timer activates"
- id: bluetooth_mode
type: integer
description: "0=Manual, 1=Automatic, 2=Guest, 3=Disabled"
Events
# Source does not document unsolicited push events over HTTP.
# Long-polling /Status and /SyncStatus provide change detection but are client-initiated.
# LSDP Announce and Delete are unsolicited UDP broadcast events (see lsdp_announce, lsdp_delete actions).
Macros
# UNRESOLVED: no multi-step sequences explicitly documented in source
Safety
confirmation_required_for: []
interlocks: []
# UNRESOLVED: no safety warnings, interlock procedures, or power-on sequencing documented in source
Notes
- All commands are HTTP GET requests except
/rebootwhich is POST. - Default TCP port is 11000 for all BluOS players. CI580 uses ports 11000, 11010, 11020, 11030 for its four streamer nodes.
- Port should be discovered via mDNS (services
musc.tcpfor primary player,musp.tcpfor secondary,muss.tcpfor server,mush.tcpfor Hub) or LSDP (UDP broadcast port 11430). - Long-polling supported on
/Status(recommended timeout 100s) and/SyncStatus(recommended timeout 180s) and/Volume. - Without long-polling, restrict polling to max one request per 30 seconds.
- With long-polling, minimum 1 second between consecutive requests for same resource.
- Volume range is configurable via BluOS Controller app (Settings → Player → Audio), typically -80..0 dB.
- When grouped, secondary players proxy many requests to the primary player internally: /Status, Playback Control, Play Queue Management, Content Browsing and Searching.
/Play?inputTypeIndexformat uses type-index (e.g.spdif-1,analog-1,arc-1). Available types: spdif, analog, coax, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone.- LSDP discovery protocol uses UDP broadcast on port 11430 (IANA-assigned to Lenbrook March 27 2014). Packet starts with header: Length (1B), Magic "LSDP" (4B ASCII), Protocol Version (1B, currently 1). Followed by message blocks: Query (Q=0x51 broadcast, R=0x52 unicast), Announce (A=0x41), Delete (D=0x44). All multi-byte values big-endian unsigned.
- LSDP Class IDs: 0x0001 BluOS Player (_musc._tcp), 0x0002 BluOS Server (_muss._tcp), 0x0003 BluOS secondary in multi-zone players like CI580 (_musp._tcp), 0x0004 sovi-mfg, 0x0005 sovi-keypad, 0x0006 BluOS pair slave (_musz._tcp), 0x0007 Remote Web App / AVR OSD Web Page (_remote-web-ui._tcp), 0x0008 BluOS Hub (_mush._tcp), 0xFFFF All Classes (for Query only).
- LSDP startup timing: 7 packets at absolute times [0,1,2,3,5,7,10s] + 0-250ms random. Main Announce period: 57s + 0-6s random. Query response delay: 0-750ms random. LSDP supports TXT records similar to mDNS for arbitrary metadata.
- XML responses may contain undocumented elements which should be ignored.
- For UI displaying three lines of now-playing metadata, MUST use title1/title2/title3 from /Status (not album/artist/name). For two-line displays, use twoline_title1/twoline_title2 when present.
- /Browse responses: items have type attribute (link/audio/artist/composer/album/playlist/track/text/section/folder) as display hint. browseKey presence indicates descendable; playURL presence indicates playable; autoplayURL indicates queue-and-play. Relative URIs resolved per RFC 3986. All keys must be percent-escaped when used as key parameter.
- Context menu item types from /Browse with withContextMenuItems=1: favourite-add, favourite-delete, add-now, add-next, add-last, addAll-now, addAll-next, addAll-last, add-shuffle, playRadio, delete (user confirmation required).
Added 6 new actions: `volume_query`, `search_content`, `settings_capture_query`, `lsdp_query`, `lsdp_announce`, `lsdp_delete`. Added `udp` protocol + LSDP block in transport. Added 12 new feedbacks (volume_offset_db, mute_volume, mute_db, now_playing_twoline, stream_url_flag, can_seek, can_move_playback, sleep_timer, alarm_seconds_remaining, audio_quality, stream_format, battery_state, group_name, group_volume). Added 2 new variables (volume_offset_db, sleep_timer). Expanded notes w/ LSDP details + browse context menu types. Preserved all existin' entries.
## Provenance
```yaml
source_domains:
- bluos.io
source_urls:
- https://bluos.io/wp-content/uploads/2025/06/BluOS-Custom-Integration-API_v1.7.pdf
- https://bluos.io/downloads/
retrieved_at: 2026-06-01T21:51:30.565Z
last_checked_at: 2026-07-21T23:32:30.729Z
```
## Verification Summary
```yaml
verdict: verified
checked_at: 2026-07-21T23:32:30.729Z
matched_actions: 44
action_count: 44
confidence: medium
summary: "All 44 spec actions match literal BluOS HTTP endpoints and LSDP message types in source with correct params, and the source command catalogue is fully represented. (10 unresolved item(s) noted in Known Gaps.)"
```
## Known Gaps
```yaml
- "specific hardware model details not stated — document covers all BluOS players generically"
- "firmware version compatibility ranges not stated"
- "no multi-step sequences explicitly documented in source"
- "no safety warnings, interlock procedures, or power-on sequencing documented in source"
- "exact firmware versions for inputTypeIndex vs inputIndex feature boundaries (stated as v3.8.0 < fw < v4.2.0 for inputIndex, v4.2.0+ for inputTypeIndex)"
- "voltage, current, power specifications not stated in source"
- "fault behavior or error recovery sequences not documented"
- "maximum number of grouped players not stated"
- "maximum play queue size not stated"
- "full /Settings endpoint schema beyond capture sub-step reference not documented"
```
---
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0