Summary

BluOS is a music management and streaming OS found in products from Bluesound, NAD Electronics, DALI Loudspeakers, and others. This spec covers the BluOS Custom Integration API v1.7, which provides HTTP GET-based control of BluOS players including playback control, volume, play queue management, preset loading, content browsing, player grouping, and input selection. All requests are sent as HTTP GET (or POST for reboot) to http://<player_ip>:<port>/<command> and responses are UTF-8 encoded XML. Port 11000 is standard for most players; the CI580 uses ports 11000/11010/11020/11030 for its four nodes.

Transport

protocols:
  - http
addressing:
  port: 11000  # default for all BluOS players except CI580 multi-node units; discover via mDNS (_musc._tcp / _musp._tcp) or LSDP (UDP port 11430)
  base_url: http://<player_ip>:11000
auth:
  type: none  # inferred: no auth procedure in source

Traits

- queryable    # inferred from query command examples (Status, SyncStatus, Volume, Playlist, Presets, Browse)
- levelable    # inferred from volume control commands (set level, mute, dB adjustment)
- routable     # inferred from input selection and player grouping commands
- powerable    # inferred from reboot command; no explicit power on/off command found — see Notes

Actions

# --- Playback Control ---

- id: play
  label: Play
  kind: action
  params:
    - name: seek
      type: integer
      description: Optional. Jump to this position in seconds in the current track. Only valid if /Status includes <totlen>.
      required: false
    - name: id
      type: integer
      description: Optional. Track position in queue to start playing (used with seek).
      required: false
    - name: url
      type: string
      description: Optional. URL-encoded stream URL to play directly.
      required: false
  notes: "GET /Play or /Play?seek=<seconds> or /Play?url=<encodedURL>"

- id: pause
  label: Pause
  kind: action
  params:
    - name: toggle
      type: integer
      description: Optional. If set to 1, toggles the current pause state.
      required: false
  notes: "GET /Pause or /Pause?toggle=1"

- id: stop
  label: Stop
  kind: action
  params: []
  notes: "GET /Stop"

- id: skip
  label: Skip to Next Track
  kind: action
  params: []
  notes: "GET /Skip — skips to next track in play queue. Not available for all streaming sources."

- id: back
  label: Back / Previous Track
  kind: action
  params: []
  notes: "GET /Back — returns to track start if >4 seconds played, otherwise goes to previous track."

- id: shuffle
  label: Set Shuffle
  kind: action
  params:
    - name: state
      type: integer
      description: "0 to disable shuffle, 1 to enable shuffle."
      required: true
  notes: "GET /Shuffle?state=0|1"

- id: repeat
  label: Set Repeat
  kind: action
  params:
    - name: state
      type: integer
      description: "0 = repeat play queue; 1 = repeat current track; 2 = repeat off."
      required: true
  notes: "GET /Repeat?state=0|1|2"

- id: action_streaming
  label: Streaming Radio Action (Skip/Back/Love/Ban)
  kind: action
  params:
    - name: service
      type: string
      description: Service name (e.g., Slacker).
      required: true
    - name: action_param
      type: string
      description: "Action-specific parameter taken from the <action> element in /Status response (e.g., skip=<id>, ban=<id>, love=<id>)."
      required: true
  notes: "GET /Action?service=<name>&<action>=<value>. Exact URL is provided in the <action> element of the /Status response."

# --- Volume Control ---

- id: set_volume_level
  label: Set Volume Level
  kind: action
  params:
    - name: level
      type: integer
      description: Absolute volume level, 0–100.
      required: true
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = change only this player; 1 = change all players in group."
      required: false
  notes: "GET /Volume?level=<0-100>&tell_slaves=<0|1>"

- id: set_volume_abs_db
  label: Set Volume (Absolute dB)
  kind: action
  params:
    - name: abs_db
      type: number
      description: Absolute volume in dB.
      required: true
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = change only this player; 1 = change all players in group."
      required: false
  notes: "GET /Volume?abs_db=<db>"

- id: set_volume_relative_db
  label: Adjust Volume (Relative dB)
  kind: action
  params:
    - name: db
      type: number
      description: Relative dB change (positive to increase, negative to decrease). Typical values +2 or -2.
      required: true
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = change only this player; 1 = change all players in group."
      required: false
  notes: "GET /Volume?db=<delta> (e.g., /Volume?db=2 or /Volume?db=-2)"

- id: mute
  label: Mute
  kind: action
  params: []
  notes: "GET /Volume?mute=1"

- id: unmute
  label: Unmute
  kind: action
  params: []
  notes: "GET /Volume?mute=0"

# --- Play Queue Management ---

- id: list_tracks
  label: List Play Queue Tracks
  kind: action
  params:
    - name: length
      type: integer
      description: "Optional. Set to 1 to return only top-level queue attributes (no track details)."
      required: false
    - name: start
      type: integer
      description: "Optional. First entry in the queue to include (0-based). For pagination."
      required: false
    - name: end
      type: integer
      description: "Optional. Last entry in the queue to include."
      required: false
  notes: "GET /Playlist or /Playlist?length=1 or /Playlist?start=<n>&end=<m>"

- id: delete_track
  label: Delete Track from Queue
  kind: action
  params:
    - name: id
      type: integer
      description: Track position (id) in the play queue to remove.
      required: true
  notes: "GET /Delete?id=<position>"

- id: move_track
  label: Move Track in Queue
  kind: action
  params:
    - name: new
      type: integer
      description: New position for the track.
      required: true
    - name: old
      type: integer
      description: Current position of the track.
      required: true
  notes: "GET /Move?new=<dest>&old=<origin>"

- id: clear_queue
  label: Clear Play Queue
  kind: action
  params: []
  notes: "GET /Clear"

- id: save_queue
  label: Save Play Queue as Playlist
  kind: action
  params:
    - name: name
      type: string
      description: Name to save the play queue as.
      required: true
  notes: "GET /Save?name=<playlist_name>"

# --- Presets ---

- id: list_presets
  label: List Presets
  kind: action
  params: []
  notes: "GET /Presets"

- id: load_preset
  label: Load Preset
  kind: action
  params:
    - name: id
      type: string
      description: "Preset id to load. Use +1 for next preset, -1 for previous preset, or a specific preset id number."
      required: true
  notes: "GET /Preset?id=<presetId|+1|-1>"

# --- Content Browsing ---

- id: browse
  label: Browse Music Content
  kind: action
  params:
    - name: key
      type: string
      description: "Optional. URL-encoded browse key from a previous response (browseKey, nextKey, parentKey, or contextMenuKey attribute)."
      required: false
    - name: withContextMenuItems
      type: integer
      description: "Optional. Set to 1 to include inline context menu items."
      required: false
  notes: "GET /Browse or /Browse?key=<encoded-key>"

- id: search
  label: Search Music Content
  kind: action
  params:
    - name: key
      type: string
      description: "Value from a searchKey attribute in a previous /Browse response."
      required: true
    - name: q
      type: string
      description: Search string.
      required: true
  notes: "GET /Browse?key=<searchKey>&q=<searchText>"

# --- Player Grouping ---

- id: add_slave
  label: Group Player (Add Secondary Player)
  kind: action
  params:
    - name: slave
      type: string
      description: IP address of the secondary player to add.
      required: true
    - name: port
      type: integer
      description: Port number of the secondary player. Default is 11000.
      required: true
    - name: group
      type: string
      description: "Optional. Group name. If omitted, BluOS assigns a default."
      required: false
  notes: "GET /AddSlave?slave=<ip>&port=<port>"

- id: add_slaves
  label: Group Multiple Players
  kind: action
  params:
    - name: slaves
      type: string
      description: Comma-separated IP addresses of secondary players to add.
      required: true
    - name: ports
      type: string
      description: Comma-separated port numbers corresponding to the secondary players.
      required: true
  notes: "GET /AddSlave?slaves=<ip1,ip2,...>&ports=<port1,port2,...>"

- id: remove_slave
  label: Remove Player from Group
  kind: action
  params:
    - name: slave
      type: string
      description: IP address of the player to remove from the group.
      required: true
    - name: port
      type: integer
      description: Port of the player to remove.
      required: true
  notes: "GET /RemoveSlave?slave=<ip>&port=<port>"

- id: remove_slaves
  label: Remove Multiple Players from Group
  kind: action
  params:
    - name: slaves
      type: string
      description: Comma-separated IP addresses of players to remove.
      required: true
    - name: ports
      type: string
      description: Comma-separated port numbers of the players to remove.
      required: true
  notes: "GET /RemoveSlave?slaves=<ip1,ip2,...>&ports=<port1,port2,...>"

# --- Input Selection ---

- id: select_active_input
  label: Select Active Input (by URL)
  kind: action
  params:
    - name: url
      type: string
      description: "URL-encoded input URL from /RadioBrowse?service=Capture response."
      required: true
  notes: "GET /Play?url=<encoded-capture-url>. First query /RadioBrowse?service=Capture to get available input URLs."

- id: select_input_by_index
  label: Select External Input by Index (firmware <4.2.0)
  kind: action
  params:
    - name: inputIndex
      type: integer
      description: "Index (1-based) of the input in /Settings?id=capture&schemaVersion=32 response (Bluetooth excluded)."
      required: true
  notes: "GET /Play?InputId=<index>. For firmware newer than v3.8.0 and older than v4.2.0."

- id: select_input_by_type_index
  label: Select External Input by Type-Index (firmware >=4.2.0)
  kind: action
  params:
    - name: inputTypeIndex
      type: string
      description: "Format: <type>-<index>. Types: spdif, analog, coax, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone. Example: spdif-2 for Optical Input 2."
      required: true
  notes: "GET /Play?inputTypeIndex=<type>-<index>. For firmware v4.2.0 or newer."

# --- Doorbell ---

- id: doorbell_play
  label: Play Doorbell Chime
  kind: action
  params:
    - name: play
      type: integer
      description: Always set to 1.
      required: true
  notes: "GET /Doorbell?play=1"

# --- Bluetooth ---

- id: set_bluetooth_mode
  label: Set Bluetooth Mode
  kind: action
  params:
    - name: bluetoothAutoplay
      type: integer
      description: "0 = Manual; 1 = Automatic; 2 = Guest; 3 = Disabled."
      required: true
  notes: "GET /audiomodes?bluetoothAutoplay=<0|1|2|3>. No response body is returned."

# --- Reboot ---

- id: reboot
  label: Reboot Player
  kind: action
  params:
    - name: yes
      type: string
      description: Any value (e.g., 1).
      required: true
  notes: "POST /reboot with body parameter yes=1. Example: curl -d yes=1 http://<player_ip>/reboot"

Feedbacks

# --- Playback Status (/Status) ---

- id: playback_state
  type: enum
  values: [play, pause, stop, stream, connecting]
  source: "/Status response <state> element"
  notes: "play and stream should be treated as equivalent. Use /Play to resume from pause; cannot resume from stop state."

- id: playback_title1
  type: string
  source: "/Status response <title1> element"
  notes: "First line of now-playing metadata. MUST be used (not album/artist/name) in any 3-line UI."

- id: playback_title2
  type: string
  source: "/Status response <title2> element"

- id: playback_title3
  type: string
  source: "/Status response <title3> element"

- id: volume_level
  type: integer
  range: "-1 to 100"
  source: "/Status or /Volume response <volume> element"
  notes: "-1 means fixed volume."

- id: volume_db
  type: number
  source: "/Status or /Volume response db attribute"

- id: mute_state
  type: integer
  values: [0, 1]
  source: "/Status or /Volume response mute attribute"
  notes: "1 = muted, 0 = unmuted."

- id: shuffle_state
  type: integer
  values: [0, 1]
  source: "/Status response <shuffle> element"

- id: repeat_state
  type: integer
  values: [0, 1, 2]
  source: "/Status response <repeat> element"
  notes: "0 = repeat queue, 1 = repeat track, 2 = repeat off."

- id: current_track_position_secs
  type: integer
  source: "/Status response <secs> element"
  notes: "Not included in etag calculation; clients must increment position locally between polls."

- id: total_track_length_secs
  type: integer
  source: "/Status response <totlen> element"

- id: current_queue_id
  type: integer
  source: "/Status response <pid> element"

- id: preset_id
  type: integer
  source: "/Status response <prid> element"

- id: sync_stat
  type: integer
  source: "/Status response <syncStat> element"
  notes: "Changes when /SyncStatus changes. If monitoring playback only, /Status long-polling is sufficient; check this field to know when to also query /SyncStatus."

- id: service_name
  type: string
  source: "/Status response <service> element"

- id: stream_format
  type: string
  source: "/Status response <streamFormat> element"

- id: quality
  type: string
  source: "/Status response <quality> element"
  notes: "cd = CD-quality lossless; hd = hi-res lossless; dolbyAudio = Dolby/AC3; mqa = MQA decoded; mqaAuthored = MQA-Authored decoded; numeric = approximate compressed bitrate."

- id: is_stream_url_mode
  type: boolean
  source: "/Status response <streamUrl> presence"
  notes: "If present, play queue is not the source; shuffle/repeat/skip/back are not applicable."

# --- Sync Status (/SyncStatus) ---

- id: player_name
  type: string
  source: "/SyncStatus response name attribute"

- id: player_model
  type: string
  source: "/SyncStatus response model attribute"

- id: player_brand
  type: string
  source: "/SyncStatus response brand attribute"

- id: group_name
  type: string
  source: "/SyncStatus response group attribute"

- id: player_initialized
  type: boolean
  source: "/SyncStatus response initialized attribute"
  notes: "false means player needs setup via the BluOS Controller app."

- id: is_master
  type: boolean
  source: "/SyncStatus — master element absent means player is primary; master element present means secondary."

- id: slave_players
  type: array
  source: "/SyncStatus response <slave> elements"
  notes: "Each slave has id (IP:port) attribute."

Variables

# UNRESOLVED: no explicit settable parameter registry beyond what is covered in Actions/Feedbacks; volume range can be adjusted via BluOS Controller app (Settings -> Player -> Audio) but not via the API described in this document.

Events

# Long polling is supported on /Status and /SyncStatus. These are not unsolicited push events but rather held HTTP responses:

- id: status_changed
  description: "Long-poll on /Status?timeout=<seconds>&etag=<prev-etag> — response held until state changes or timeout. Recommended interval: 100 seconds; minimum: 10 seconds."

- id: sync_status_changed
  description: "Long-poll on /SyncStatus?timeout=<seconds>&etag=<prev-etag> — response held until grouping/volume/name changes or timeout. Recommended interval: 180 seconds."

- id: volume_changed
  description: "Long-poll on /Volume (supports long polling, not illustrated in source)."

Macros

# UNRESOLVED: no explicit multi-step macros defined in source.

Safety

confirmation_required_for:
  - reboot  # inferred: reboot command triggers a soft reboot; no warning text in source but integrators should confirm before invoking
interlocks: []
# UNRESOLVED: no explicit safety warnings or interlock procedures stated in source.

Notes

Port discovery: Port 11000 is standard. For CI580 (4-node chassis), use 11000/11010/11020/11030 per node. Actual port should be discovered via mDNS services _musc._tcp and _musp._tcp, or via the Lenbrook Service Discovery Protocol (LSDP) on UDP port 11430.

Long polling: The API supports long polling on /Status and /SyncStatus (and /Volume). When using long polling, clients must not make two consecutive requests for the same resource less than 1 second apart. When not using long polling, restrict polling to at most once every 30 seconds.

Grouped players: Secondary players proxy most requests to the primary player. To track individual secondary player volume, long-poll /SyncStatus on each secondary player.

Input selection API version split: Two APIs exist for external input selection — the older /Play?InputId=<index> (firmware >v3.8.0 and <v4.2.0) and the newer /Play?inputTypeIndex=<type>-<index> (firmware v4.2.0+). Integrators should detect firmware version or use active input selection via /RadioBrowse?service=Capture where possible.

Power on/off: No explicit power on or power off HTTP command was found in this API document. The reboot command (POST /reboot?yes=1) triggers a soft reboot only. Power-on may require Wake-on-LAN or physical power; power-off is not documented.

Streaming radio actions: Skip, back, love, and ban for streaming radio stations (e.g., Slacker, Radio Paradise) are performed via /Action with parameters taken from the <actions> block in the /Status response. The exact URL parameters vary per service.

LSDP (Lenbrook Service Discovery Protocol): A UDP broadcast discovery protocol using port 11430 with packet magic "LSDP". Announce packets are sent roughly every minute at steady state and burst 7 times at startup. Class ID 0x0001 = BluOS Player, 0x0003 = BluOS Player secondary (CI580), 0x0008 = BluOS Hub.

Provenance

source_domains: []
source_urls: []
retrieved_at: 2026-06-02T21:54:10.101Z
last_checked_at: 2026-06-02T21:54:10.101Z

Verification Summary

verdict: verified
checked_at: 2026-06-02T21:54:10.101Z
matched_actions: 32
action_count: 32
confidence: medium
summary: "All 32 spec actions traced to BluOS Custom Integration API v1.7. Standard playback, volume, grouping, preset, and queue operations documented. (7 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "specific BSP Series model numbers are not listed in the source document; the source covers all BluOS players generically"
- "no explicit settable parameter registry beyond what is covered in Actions/Feedbacks; volume range can be adjusted via BluOS Controller app (Settings -> Player -> Audio) but not via the API described in this document."
- "no explicit multi-step macros defined in source."
- "no explicit safety warnings or interlock procedures stated in source."
- "specific BSP Series model numbers that implement this API are not enumerated in the source. The source covers \"BluOS players\" generically across Bluesound, NAD, and DALI product lines."
- "authentication — source contains no login or credential procedure, so none is inferred. However, it is unclear whether the API enforces network-level isolation or whether any access control applies in enterprise environments."
- "error response format — source mentions <error> root element with <message> and <detail> text nodes for /Browse errors but does not document the general error response schema for other endpoints."
- "model-specific source not located"

From the AI4AV catalog (https://ai4av.net) · ODbL-1.0