Summary

The BluOS Pulse Series (and compatible BluOS players from Bluesound, NAD Electronics, DALI Loudspeakers, and others) are networked music streaming devices controlled via HTTP GET requests over TCP/IP. This spec covers the Custom Integration API v1.7, which provides commands for playback control, volume, play queue management, presets, content browsing, player grouping, and input selection. Responses are UTF-8 encoded XML. Player discovery is available via mDNS (services musc.tcp / musp.tcp) or the proprietary LSDP UDP broadcast protocol on port 11430.

Transport

protocols:
  - http
addressing:
  port: 11000  # stated: "Port 11000 is used for all BluOS players"; CI580 nodes use 11000/11010/11020/11030
  base_url: http://<player_ip>:11000
auth:
  type: none  # inferred: no auth procedure described in source

Traits

- queryable    # inferred from query command examples (/Status, /SyncStatus, /Volume, /Playlist, /Presets)
- levelable    # inferred from volume control commands (/Volume?level=, /Volume?db=, /Volume?mute=)
- routable     # inferred from input selection commands (/Play?url=, /Play?inputTypeIndex=) and player grouping (/AddSlave, /RemoveSlave)
- powerable    # inferred from /reboot command (POST /reboot); no explicit power on/off found beyond reboot

Actions

# --- Playback Control ---

- id: play
  label: Play
  kind: action
  description: Start playback of the current audio source.
  endpoint: GET /Play
  params: []

- id: play_seek
  label: Play with Seek
  kind: action
  description: Start playback at a specified position in the current track.
  endpoint: GET /Play
  params:
    - name: seek
      type: integer
      description: Position in seconds to jump to within the current track. Only valid if /Status response includes <totlen>.
    - name: id
      type: integer
      description: Optional track id (position in queue, 0-based). Use with seek.

- id: play_url
  label: Play URL
  kind: action
  description: Start playback of a custom stream URL.
  endpoint: GET /Play
  params:
    - name: url
      type: string
      description: URL-encoded stream URL to play.

- id: pause
  label: Pause
  kind: action
  description: Pause the currently playing audio.
  endpoint: GET /Pause
  params: []

- id: pause_toggle
  label: Pause Toggle
  kind: action
  description: Toggle the current pause state.
  endpoint: GET /Pause
  params:
    - name: toggle
      type: integer
      description: Set to 1 to toggle pause state.

- id: stop
  label: Stop
  kind: action
  description: Stop the currently playing audio.
  endpoint: GET /Stop
  params: []

- id: skip
  label: Skip (Next Track)
  kind: action
  description: Skip to the next audio track in the play queue.
  endpoint: GET /Skip
  params: []

- id: back
  label: Back (Previous Track)
  kind: action
  description: Go back to the beginning of the current track or to the previous track.
  endpoint: GET /Back
  params: []

- id: shuffle
  label: Shuffle
  kind: action
  description: Enable or disable shuffle on the current play queue.
  endpoint: GET /Shuffle
  params:
    - name: state
      type: integer
      description: "0 to disable shuffle; 1 to enable shuffle."

- id: repeat
  label: Repeat
  kind: action
  description: Set the repeat mode.
  endpoint: GET /Repeat
  params:
    - name: state
      type: integer
      description: "0 = repeat queue; 1 = repeat current track; 2 = repeat off."

- id: streaming_action
  label: Streaming Radio Action
  kind: action
  description: Execute a skip/back/love/ban action on a streaming radio station. Action URL is taken from the <action> element in /Status response.
  endpoint: GET /Action
  params:
    - name: service
      type: string
      description: Service name (e.g. Slacker).
    - name: skip
      type: string
      description: Track id to skip to (from action URL).
    - name: ban
      type: string
      description: Track id to ban (from action URL).
    - name: love
      type: string
      description: Track id to love (from action URL).

# --- Volume Control ---

- id: set_volume_level
  label: Set Volume Level
  kind: action
  description: Set the player volume as an absolute level (0-100).
  endpoint: GET /Volume
  params:
    - name: level
      type: integer
      description: Absolute volume level, integer from 0 to 100.
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = only this player; 1 = all players in group."

- id: set_volume_db
  label: Set Volume (Absolute dB)
  kind: action
  description: Set the player volume using an absolute dB value.
  endpoint: GET /Volume
  params:
    - name: abs_db
      type: number
      description: Absolute volume level in dB.
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = only this player; 1 = all players in group."

- id: adjust_volume_db
  label: Adjust Volume (Relative dB)
  kind: action
  description: Increase or decrease volume by a relative dB amount (e.g., +2 or -2).
  endpoint: GET /Volume
  params:
    - name: db
      type: number
      description: Relative volume change in dB. Positive to increase, negative to decrease.
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = only this player; 1 = all players in group."

- id: mute_on
  label: Mute On
  kind: action
  description: Mute the player.
  endpoint: GET /Volume
  params:
    - name: mute
      type: integer
      description: Set to 1 to mute.

- id: mute_off
  label: Mute Off
  kind: action
  description: Unmute the player.
  endpoint: GET /Volume
  params:
    - name: mute
      type: integer
      description: Set to 0 to unmute.

# --- Play Queue Management ---

- id: list_queue
  label: List Play Queue
  kind: action
  description: Return tracks in the play queue, optionally paginated.
  endpoint: GET /Playlist
  params:
    - name: length
      type: integer
      description: "Optional. Set to 1 to return only queue status (no track details)."
    - name: start
      type: integer
      description: "Optional. First track index (0-based) to include."
    - name: end
      type: integer
      description: "Optional. Last track index to include."

- id: delete_track
  label: Delete Track from Queue
  kind: action
  description: Remove a track from the current play queue.
  endpoint: GET /Delete
  params:
    - name: id
      type: integer
      description: Track position (id) to delete from the queue.

- id: move_track
  label: Move Track in Queue
  kind: action
  description: Move a track within the current play queue.
  endpoint: GET /Move
  params:
    - name: new
      type: integer
      description: New position for the track.
    - name: old
      type: integer
      description: Current position of the track.

- id: clear_queue
  label: Clear Queue
  kind: action
  description: Remove all tracks from the current play queue.
  endpoint: GET /Clear
  params: []

- id: save_queue
  label: Save Queue
  kind: action
  description: Save the current play queue as a named BluOS playlist.
  endpoint: GET /Save
  params:
    - name: name
      type: string
      description: Name for the saved playlist (URL-encoded).

# --- Presets ---

- id: list_presets
  label: List Presets
  kind: action
  description: List all presets stored on the BluOS player.
  endpoint: GET /Presets
  params: []

- id: load_preset
  label: Load Preset
  kind: action
  description: Start playing a preset by id, or load the next/previous preset.
  endpoint: GET /Preset
  params:
    - name: id
      type: string
      description: "Preset id to load. Use +1 for next preset, -1 for previous preset."

# --- Content Browsing and Searching ---

- id: browse
  label: Browse Content
  kind: action
  description: Navigate through available music sources, inputs, and playlists. Omit key for top-level browse.
  endpoint: GET /Browse
  params:
    - name: key
      type: string
      description: "Optional. URL-encoded browse key from a previous /Browse response (browseKey, nextKey, parentKey, contextMenuKey)."
    - name: withContextMenuItems
      type: integer
      description: "Optional. Set to 1 to include inline context menus in the response."
    - name: q
      type: string
      description: "Optional. Search string. Used with key (searchKey) to search within a service."

# --- Player Grouping ---

- id: add_slave
  label: Group Player (Add Secondary)
  kind: action
  description: Add one secondary player to this primary player's group.
  endpoint: GET /AddSlave
  params:
    - name: slave
      type: string
      description: IP address of the secondary player.
    - name: port
      type: integer
      description: Port number of the secondary player (default 11000).
    - name: group
      type: string
      description: "Optional. Group name. BluOS assigns a default if not provided."

- id: add_slaves
  label: Group Multiple Players (Add Multiple Secondaries)
  kind: action
  description: Add two or more secondary players to this primary player's group.
  endpoint: GET /AddSlave
  params:
    - name: slaves
      type: string
      description: Comma-separated IP addresses of secondary players.
    - name: ports
      type: string
      description: Comma-separated port numbers corresponding to each secondary player.

- id: remove_slave
  label: Ungroup Player (Remove Secondary)
  kind: action
  description: Remove one player from a group.
  endpoint: GET /RemoveSlave
  params:
    - name: slave
      type: string
      description: IP address of the player to remove.
    - name: port
      type: integer
      description: Port number of the player to remove.

- id: remove_slaves
  label: Ungroup Multiple Players (Remove Multiple Secondaries)
  kind: action
  description: Remove two or more players from a group.
  endpoint: GET /RemoveSlave
  params:
    - name: slaves
      type: string
      description: Comma-separated IP addresses of players to remove.
    - name: ports
      type: string
      description: Comma-separated port numbers of players to remove.

# --- Reboot ---

- id: reboot
  label: Reboot Player
  kind: action
  description: Soft reboot the player. Sent as HTTP POST.
  endpoint: POST /reboot
  params:
    - name: yes
      type: string
      description: Any value (e.g., "1").

# --- Doorbell Chime ---

- id: doorbell_play
  label: Play Doorbell Chime
  kind: action
  description: Activate the doorbell chime on the player.
  endpoint: GET /Doorbell
  params:
    - name: play
      type: integer
      description: Set to 1 to play the doorbell chime.

# --- Direct Input Selection ---

- id: play_input_active
  label: Select Active Input (URL)
  kind: action
  description: Select an active input source using the URL from /RadioBrowse?service=Capture.
  endpoint: GET /Play
  params:
    - name: url
      type: string
      description: URL-encoded input URL from /RadioBrowse?service=Capture response.

- id: play_input_index
  label: Select External Input by Index (firmware <4.2.0)
  kind: action
  description: Select an external input by index (firmware v3.8.0 to <v4.2.0). Bluetooth excluded from index.
  endpoint: GET /Play
  params:
    - name: inputIndex
      type: integer
      description: 1-based index of input from /Settings?id=capture&schemaVersion=32, excluding Bluetooth.

- id: play_input_type_index
  label: Select External Input by Type+Index (firmware >=4.2.0)
  kind: action
  description: "Select an external input by type-index string (firmware v4.2.0+). Format: <type>-<index>."
  endpoint: GET /Play
  params:
    - name: inputTypeIndex
      type: string
      description: "Type-index string. Type values: spdif, analog, coax, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone. Index starts at 1. Example: spdif-2 for Optical Input 2."

# --- Bluetooth Mode ---

- id: set_bluetooth_mode
  label: Set Bluetooth Mode
  kind: action
  description: Change the Bluetooth mode of the player.
  endpoint: GET /audiomodes
  params:
    - name: bluetoothAutoplay
      type: integer
      description: "0 = Manual; 1 = Automatic; 2 = Guest; 3 = Disabled."

Feedbacks

- id: playback_status
  label: Playback Status
  endpoint: GET /Status
  description: Returns volume, playback state, current track metadata, and player configuration. Supports long polling via timeout and etag parameters.
  type: xml
  key_attributes:
    - name: state
      type: enum
      values: [play, pause, stop, stream, connecting]
      description: Current player state.
    - name: volume
      type: integer
      description: Volume level 0-100; -1 if fixed volume.
    - name: mute
      type: integer
      description: "1 if muted, 0 if not."
    - name: title1
      type: string
      description: First line of now-playing metadata (use this, not name/artist/album directly).
    - name: title2
      type: string
      description: Second line of now-playing metadata.
    - name: title3
      type: string
      description: Third line of now-playing metadata.
    - name: shuffle
      type: integer
      description: "0 = shuffle off; 1 = shuffle on."
    - name: repeat
      type: integer
      description: "0 = repeat queue; 1 = repeat track; 2 = repeat off."
    - name: secs
      type: integer
      description: Seconds the current track has been playing. Not included in etag calculation.
    - name: totlen
      type: integer
      description: Total length of current track in seconds.
    - name: syncStat
      type: string
      description: Unique id that changes whenever /SyncStatus changes.
    - name: pid
      type: integer
      description: Current play queue id.
    - name: prid
      type: integer
      description: Current preset id.
    - name: etag
      type: string
      description: Opaque value for long-polling change detection.

- id: sync_status
  label: Player and Group Sync Status
  endpoint: GET /SyncStatus
  description: Returns player information and grouping info. Supports long polling via timeout and etag.
  type: xml
  key_attributes:
    - name: volume
      type: integer
      description: Volume level 0-100; -1 if fixed.
    - name: name
      type: string
      description: Player name.
    - name: modelName
      type: string
      description: Player model name (e.g., PULSE).
    - name: model
      type: string
      description: Player model id.
    - name: brand
      type: string
      description: Player brand.
    - name: group
      type: string
      description: Group name if player is in a group.
    - name: mute
      type: integer
      description: "1 if muted."
    - name: initialized
      type: boolean
      description: "true if player is set up; false if setup needed via BluOS Controller app."
    - name: syncStat
      type: string
      description: Unique id matching <syncStat> in /Status response.

- id: volume_status
  label: Volume Status
  endpoint: GET /Volume
  description: Returns current volume level, mute state, and dB values. Supports long polling.
  type: xml
  key_attributes:
    - name: volume
      type: integer
      description: Current volume 0-100; -1 for fixed.
    - name: db
      type: number
      description: Volume level in dB.
    - name: mute
      type: integer
      description: "1 if muted, 0 if unmuted."
    - name: muteVolume
      type: integer
      description: Pre-mute volume level (0-100), present only when muted.
    - name: muteDb
      type: number
      description: Pre-mute volume in dB, present only when muted.

- id: doorbell_status
  label: Doorbell Chime Status
  endpoint: GET /Doorbell
  type: xml
  key_attributes:
    - name: enable
      type: integer
      description: Indicates chime enable state.
    - name: volume
      type: integer
      description: Chime volume.
    - name: chime
      type: string
      description: Chime audio file URL.

Variables

- id: long_poll_timeout
  label: Long Poll Timeout (seconds)
  description: Recommended polling interval is 100s for /Status (not faster than 10s); 180s for /SyncStatus. Without long-polling, restrict polling to at most once every 30 seconds.
  type: integer

- id: volume_level
  label: Volume Level
  description: Integer 0-100. Constrained to the player's configured available volume range (typically -80..0 dB).
  type: integer
  range: [0, 100]

Events

# BluOS uses long-polling (etag + timeout parameters) to signal state changes on /Status and /SyncStatus.
# There are no unsolicited push notifications described in the source; clients must poll.
# UNRESOLVED: no unsolicited event/push mechanism documented in source beyond long-poll responses.

Macros

# --- Input Selection (two-step) ---
# Active input selection requires first calling GET /RadioBrowse?service=Capture to retrieve
# the URL attribute, then calling GET /Play?url=<URL> to activate the input.
# This two-step sequence is documented explicitly in section 11.1.

# UNRESOLVED: no other multi-step macros explicitly described in source.

Safety

confirmation_required_for:
  - reboot  # POST /reboot causes immediate soft reboot; user confirmation recommended
interlocks: []
# UNRESOLVED: no additional safety warnings or interlock procedures found in source.

Notes

Polling policy: When long-polling is not used, clients must restrict polling to at most one request every 30 seconds. When long-polling is active, a client must not make two consecutive requests for the same resource less than one second apart.

Long-polling pattern: Pass timeout (seconds) and etag (from previous response root element) to /Status or /SyncStatus. Response is held until state changes or timeout expires. It is only necessary to long-poll one of /Status or /SyncStatus at a time; /Status includes a <syncStat> element to detect /SyncStatus changes.

Player grouping — secondary players: Requests directed to a secondary player for /Status, playback control, queue management, and content browsing are internally proxied to the primary player. Use /SyncStatus long-polling to track per-secondary-player volume.

Port — multi-node devices: The NAD CI580 has four streamer nodes in one chassis. Node 1 uses port 11000, node 2 uses 11010, node 3 uses 11020, node 4 uses 11030. Actual port should be discovered via mDNS services musc.tcp and musp.tcp, or via LSDP (UDP port 11430).

Discovery — LSDP: The Lenbrook Service Discovery Protocol (LSDP) uses UDP broadcast on port 11430 (IANA registered). BluOS players (class 0x0001) broadcast Announce messages approximately every minute; clients issue Query messages to discover players. LSDP is an alternative to mDNS/SSDP and is documented in Appendix 13.1.

Input selection — firmware variants: External input selection behavior differs by firmware version. For firmware v3.8.0 to <v4.2.0, use /Play?inputIndex=<n>. For firmware v4.2.0 or newer, use /Play?inputTypeIndex=<type>-<index>. Active inputs can always be selected via /Play?url=<URL> from /RadioBrowse?service=Capture.

Streaming radio actions: Skip/back/love/ban actions for streaming radio stations (e.g., Slacker, Radio Paradise, Amazon Music Prime Stations) are available via /Action. The specific URL is provided in the <action> element of the /Status response; clients should not construct these URLs independently.

Reboot is HTTP POST: /reboot requires an HTTP POST with body parameter yes=<any-value>, not a GET request.

secs field and etag: The secs field in /Status (elapsed playback seconds) is not included in the etag calculation. Progress does not trigger a long-poll return; clients must increment playback position locally based on elapsed time when state is play or stream.

API version: This spec is based on BluOS Custom Integration API Version 1.7.

Provenance

source_domains:
  - bluos.io
source_urls:
  - https://bluos.io/wp-content/uploads/2025/06/BluOS-Custom-Integration-API_v1.7.pdf
retrieved_at: 2026-05-16T22:00:59.334Z
last_checked_at: 2026-06-02T21:56:23.784Z

Verification Summary

verdict: verified
checked_at: 2026-06-02T21:56:23.784Z
matched_actions: 34
action_count: 34
confidence: medium
summary: "All 34 spec actions traced to source. (8 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "No authentication procedure found in source — auth appears to be absent."
- "Firmware version compatibility ranges not fully stated; some input selection commands vary by firmware version (v3.8.0, v4.2.0)."
- "no unsolicited event/push mechanism documented in source beyond long-poll responses."
- "no other multi-step macros explicitly described in source."
- "no additional safety warnings or interlock procedures found in source."
- "No authentication mechanism described; assumed open HTTP with no credentials."
- "Firmware version compatibility ranges not comprehensively stated (only specific version thresholds noted for input selection)."
- "Error response format not fully documented; source notes errors are enclosed in <error> root element with <message> and optional <detail> text nodes (mentioned only for /Browse)."

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