Summary

BluOS-based control surface (HTTP API) for Bluesound/NAD Tunify players. All commands are HTTP GET requests with URL-encoded parameters against http://<player_ip>:<port>/<request> on TCP port 11000; responses are UTF-8 encoded XML. A subset of the BluOS Custom Integration API plus the UDP-based LSDP discovery protocol (port 11430) are documented.

Transport

protocols:
  - http
  - udp
addressing:
  port: 11000  # HTTP control port for BluOS players; LSDP discovery uses UDP port 11430
auth:
  type: none  # inferred: no auth procedure in source

Traits

# - powerable       inferred: no explicit power on/off command in source subset
- queryable       # inferred: /Status and /SyncStatus query commands present
- routable        # inferred: input selection via /Play?url= and /Play?inputTypeIndex= present
- levelable       # inferred: /Volume control with level, abs_db, and relative db parameters present

Actions

- id: status_query
  label: Playback Status
  kind: query
  command: "GET /Status?timeout={seconds}&etag={etag-value}"
  params:
    - name: timeout
      type: integer
      description: Long-polling interval in seconds (recommended 100)
    - name: etag
      type: string
      description: Etag from previous /Status response
- id: sync_status_query
  label: Player and Group Sync Status
  kind: query
  command: "GET /SyncStatus?timeout={seconds}&etag={etag-value}"
  params:
    - name: timeout
      type: integer
      description: Long-polling interval in seconds (recommended 180)
    - name: etag
      type: string
      description: Etag from previous /SyncStatus response
- id: set_volume_level
  label: Set Volume (0-100)
  kind: action
  command: "GET /Volume?level={level}&tell_slaves={on_off}"
  params:
    - name: level
      type: integer
      description: Absolute volume level 0-100
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all group members
- id: set_volume_abs_db
  label: Set Volume (dB)
  kind: action
  command: "GET /Volume?abs_db={db}&tell_slaves={on_off}"
  params:
    - name: abs_db
      type: number
      description: Absolute volume in dB
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all group members
- id: set_volume_relative_db
  label: Set Volume (relative dB)
  kind: action
  command: "GET /Volume?db={delta-db}&tell_slaves={on_off}"
  params:
    - name: db
      type: number
      description: Relative volume delta in dB (positive or negative)
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all group members
- id: volume_up
  label: Volume Up
  kind: action
  command: "GET /Volume?db=2"
  params: []
- id: volume_down
  label: Volume Down
  kind: action
  command: "GET /Volume?db=-2"
  params: []
- id: mute_on
  label: Mute On
  kind: action
  command: "GET /Volume?mute=1"
  params: []
- id: mute_off
  label: Mute Off
  kind: action
  command: "GET /Volume?mute=0"
  params: []
- id: play
  label: Play
  kind: action
  command: "GET /Play"
  params: []
- id: play_with_seek
  label: Play with Seek
  kind: action
  command: "GET /Play?seek={seconds}"
  params:
    - name: seek
      type: integer
      description: Jump to position in current track (seconds)
- id: play_with_seek_and_id
  label: Play with Seek and Track ID
  kind: action
  command: "GET /Play?seek={seconds}&id={trackid}"
  params:
    - name: seek
      type: integer
      description: Position in current track (seconds)
    - name: id
      type: integer
      description: Track id in the play queue
- id: play_url
  label: Play Stream URL
  kind: action
  command: "GET /Play?url={encodedStreamURL}"
  params:
    - name: url
      type: string
      description: URL-encoded stream URL
- id: play_input_index
  label: Play External Input (firmware <4.2.0)
  kind: action
  command: "GET /Play?inputIndex={IndexId}"
  params:
    - name: inputIndex
      type: integer
      description: Input index from /Settings?id=capture&schemaVersion=32 (1-based, Bluetooth excluded)
- id: play_input_type_index
  label: Play External Input (firmware >=4.2.0)
  kind: action
  command: "GET /Play?inputTypeIndex={type-index}"
  params:
    - name: inputTypeIndex
      type: string
      description: Format type-index, e.g. spdif-1, analog-1, coax-1, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone
- id: pause
  label: Pause
  kind: action
  command: "GET /Pause"
  params: []
- id: pause_toggle
  label: Pause Toggle
  kind: action
  command: "GET /Pause?toggle=1"
  params: []
- id: stop
  label: Stop
  kind: action
  command: "GET /Stop"
  params: []
- id: skip
  label: Skip to Next Track
  kind: action
  command: "GET /Skip"
  params: []
- id: back
  label: Back / Previous Track
  kind: action
  command: "GET /Back"
  params: []
- id: shuffle
  label: Shuffle On/Off
  kind: action
  command: "GET /Shuffle?state={0|1}"
  params:
    - name: state
      type: integer
      description: 0 to disable, 1 to enable
- id: repeat
  label: Repeat Mode
  kind: action
  command: "GET /Repeat?state={0|1|2}"
  params:
    - name: state
      type: integer
      description: 0 = repeat queue, 1 = repeat track, 2 = repeat off
- id: action_skip
  label: Streaming Radio Skip
  kind: action
  command: "GET /Action?service={service-name}&skip={id}"
  params:
    - name: service
      type: string
      description: Service name (e.g. Slacker)
    - name: skip
      type: string
      description: Opaque id from /Status <action> url attribute
- id: action_love
  label: Streaming Radio Love
  kind: action
  command: "GET /Action?service={service-name}&love={id}"
  params:
    - name: service
      type: string
      description: Service name
    - name: love
      type: string
      description: Opaque id from /Status <action> url attribute
- id: action_ban
  label: Streaming Radio Ban
  kind: action
  command: "GET /Action?service={service-name}&ban={id}"
  params:
    - name: service
      type: string
      description: Service name
    - name: ban
      type: string
      description: Opaque id from /Status <action> url attribute
- id: action_back
  label: Streaming Radio Back
  kind: action
  command: "GET /Action?service={service-name}&back={id}"
  params:
    - name: service
      type: string
      description: Service name
    - name: back
      type: string
      description: Opaque id from /Status <action> url attribute
- id: playlist_list
  label: List Play Queue Tracks
  kind: query
  command: "GET /Playlist"
  params: []
- id: playlist_status
  label: Play Queue Status
  kind: query
  command: "GET /Playlist?length=1"
  params: []
- id: playlist_paginate
  label: Play Queue Pagination
  kind: query
  command: "GET /Playlist?start={first}&end={last}"
  params:
    - name: start
      type: integer
      description: First queue index (0-based)
    - name: end
      type: integer
      description: Last queue index
- id: delete_track
  label: Delete Track from Queue
  kind: action
  command: "GET /Delete?id={position}"
  params:
    - name: id
      type: integer
      description: Track position in the play queue
- id: move_track
  label: Move Track in Queue
  kind: action
  command: "GET /Move?new={destination}&old={origin}"
  params:
    - name: new
      type: integer
      description: New track position
    - name: old
      type: integer
      description: Old track position
- id: clear_queue
  label: Clear Play Queue
  kind: action
  command: "GET /Clear"
  params: []
- id: save_queue
  label: Save Play Queue as Playlist
  kind: action
  command: "GET /Save?name={playlist_name}"
  params:
    - name: name
      type: string
      description: Playlist name (URL-encoded)
- id: list_presets
  label: List Presets
  kind: query
  command: "GET /Presets"
  params: []
- id: load_preset
  label: Load Preset
  kind: action
  command: "GET /Preset?id={presetId}"
  params:
    - name: id
      type: integer
      description: Preset id from /Presets response
- id: load_next_preset
  label: Load Next Preset
  kind: action
  command: "GET /Preset?id=+1"
  params: []
- id: load_previous_preset
  label: Load Previous Preset
  kind: action
  command: "GET /Preset?id=-1"
  params: []
- id: browse_top
  label: Top-Level Browse
  kind: query
  command: "GET /Browse"
  params: []
- id: browse_level
  label: Browse Hierarchy Level
  kind: query
  command: "GET /Browse?key={key-value}"
  params:
    - name: key
      type: string
      description: URL-encoded browseKey/nextKey/parentKey value
- id: browse_with_context
  label: Browse with Inline Context Menu
  kind: query
  command: "GET /Browse?key={key-value}&withContextMenuItems=1"
  params:
    - name: key
      type: string
      description: URL-encoded key value
    - name: withContextMenuItems
      type: integer
      description: Always 1
- id: search
  label: Search Music Service
  kind: query
  command: "GET /Browse?key={key-value}&q={searchText}"
  params:
    - name: key
      type: string
      description: URL-encoded searchKey from earlier browse response
    - name: q
      type: string
      description: Search term
- id: radio_browse_capture
  label: List Capture Inputs
  kind: query
  command: "GET /RadioBrowse?service=Capture"
  params: []
- id: add_slave
  label: Group One Secondary Player
  kind: action
  command: "GET /AddSlave?slave={secondaryPlayerIP}&port={secondaryPlayerPort}"
  params:
    - name: slave
      type: string
      description: IP of secondary player
    - name: port
      type: integer
      description: Port of secondary player (default 11000)
- id: add_slave_named
  label: Group Player with Group Name
  kind: action
  command: "GET /AddSlave?slave={secondaryPlayerIP}&port={secondaryPlayerPort}&group={GroupName}"
  params:
    - name: slave
      type: string
      description: IP of secondary player
    - name: port
      type: integer
      description: Port of secondary player
    - name: group
      type: string
      description: Optional group name
- id: add_slaves
  label: Group Multiple Secondary Players
  kind: action
  command: "GET /AddSlave?slaves={secondaryPlayerIPs}&ports={secondaryPlayerPorts}"
  params:
    - name: slaves
      type: string
      description: Comma-separated secondary IPs
    - name: ports
      type: string
      description: Comma-separated secondary ports
- id: remove_slave
  label: Remove One Player from Group
  kind: action
  command: "GET /RemoveSlave?slave={secondaryPlayerIP}&port={secondaryPlayerPort}"
  params:
    - name: slave
      type: string
      description: IP of secondary player
    - name: port
      type: integer
      description: Port of secondary player
- id: remove_slaves
  label: Remove Multiple Players from Group
  kind: action
  command: "GET /RemoveSlave?slaves={secondaryPlayerIPs}&ports={secondaryPlayerPorts}"
  params:
    - name: slaves
      type: string
      description: Comma-separated secondary IPs
    - name: ports
      type: string
      description: Comma-separated secondary ports
- id: reboot
  label: Soft Reboot Player
  kind: action
  command: "POST /reboot  body: yes={value}"
  params:
    - name: yes
      type: string
      description: Any value (e.g. 1)
- id: doorbell
  label: Play Doorbell Chime
  kind: action
  command: "GET /Doorbell?play=1"
  params:
    - name: play
      type: integer
      description: Always 1
- id: set_bluetooth_mode
  label: Set Bluetooth Mode
  kind: action
  command: "GET /audiomodes?bluetoothAutoplay={value}"
  params:
    - name: bluetoothAutoplay
      type: integer
      description: 0 = Manual, 1 = Automatic, 2 = Guest, 3 = Disabled
- id: get_capture_settings
  label: Get Capture Settings
  kind: query
  command: "GET /Settings?id=capture&schemaVersion=32"
  params:
    - name: schemaVersion
      type: integer
      description: 32 is the latest schema version per source

Feedbacks

- id: playback_state
  label: Playback State
  type: enum
  values:
    - play
    - pause
    - stop
    - stream
    - connecting
- id: volume_level
  label: Volume Level
  type: integer
  description: 0..100; -1 means fixed volume
- id: volume_db
  label: Volume in dB
  type: number
  description: Current volume in dB
- id: mute_state
  label: Mute State
  type: enum
  values:
    - "0"
    - "1"
- id: mute_volume
  label: Unmuted Volume (0..100)
  type: integer
  description: Pre-mute volume level
- id: mute_db
  label: Unmuted Volume (dB)
  type: number
  description: Pre-mute volume in dB
- id: shuffle_state
  label: Shuffle State
  type: enum
  values:
    - "0"
    - "1"
- id: repeat_state
  label: Repeat State
  type: enum
  values:
    - "0"
    - "1"
    - "2"
  description: 0 = queue, 1 = track, 2 = off
- id: track_position
  label: Current Track Index
  type: integer
  description: Position of current track in play queue
- id: track_total_length
  label: Total Track Length
  type: integer
  description: Total track length in seconds
- id: track_playback_seconds
  label: Seconds Played
  type: integer
- id: song_metadata
  label: Now-Playing Metadata
  type: object
  description: title1/title2/title3 from /Status; UI should prefer these over album/artist/name
- id: service
  label: Current Service ID
  type: string
- id: audio_quality
  label: Audio Quality
  type: string
  description: cd / hd / dolbyAudio / mqa / mqaAuthored / numeric bitrate
- id: stream_format
  label: Audio Stream Format
  type: string
- id: group_name
  label: Group Name
  type: string
  description: Present on primary player only
- id: group_volume
  label: Group Volume
  type: integer
  description: Group volume 0..100 (primary player only)
- id: sync_etag
  label: Sync Status Etag
  type: string
  description: Matches /Status <syncStat>; increments on group changes
- id: battery
  label: Battery (if equipped)
  type: object
  description: level, charging, icon URL
- id: doorbell_status
  label: Doorbell Status
  type: object
  description: enable, volume, chime URL
- id: preset_list
  label: Presets
  type: array
  description: id, name, url, image
- id: play_queue
  label: Play Queue Listing
  type: array
  description: name, modified, length, id, song entries
- id: browse_result
  label: Browse Result
  type: object
  description: item list with type/link/audio attributes and browseKey/playURL/actionURL

Variables

- name: volume
  type: integer
  range: 0..100
  description: Player volume level
- name: volume_db
  type: number
  description: Volume in dB; typically constrained to -80..0 via Controller app
- name: mute
  type: enum
  values: ["0", "1"]
- name: seek_position
  type: integer
  description: Seconds into current track
- name: track_id
  type: integer
  description: Track id in the play queue
- name: stream_url
  type: string
  description: URL-encoded custom audio stream
- name: shuffle
  type: enum
  values: ["0", "1"]
- name: repeat
  type: enum
  values: ["0", "1", "2"]
- name: bluetooth_mode
  type: enum
  values: ["0", "1", "2", "3"]
  description: 0=Manual, 1=Automatic, 2=Guest, 3=Disabled
- name: input_type_index
  type: string
  description: Format type-index (spdif, analog, coax, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone) with 1-based index
- name: preset_id
  type: integer
  description: Use +1 / -1 for next/previous

Events

# UNRESOLVED: source does not document any unsolicited event/notification stream.
# Long-polling on /Status and /SyncStatus is the only change-detection mechanism described.

Macros

# UNRESOLVED: source does not document any multi-step command sequences.

Safety

confirmation_required_for: []
interlocks: []
# UNRESOLVED: source contains no safety warnings, interlock procedures, or power-on sequencing requirements.

Notes

  • BluOS control API is HTTP GET; responses are UTF-8 XML. UDP/11430 (LSDP) is for service discovery only, not control. mDNS services musc.tcp and musp.tcp are used to discover the actual HTTP port per the source.
  • Default port 11000 for all BluOS players. CI580 (4 streamer nodes, 1 chassis) uses 11000 / 11010 / 11020 / 11030 for nodes 1-4.
  • Polling: at most one request per 30s without long-polling. With long-polling, never <1s between same-resource requests.
  • Recommended long-poll timeouts: 100s for /Status, 180s for /SyncStatus.
  • Secondary player endpoints (e.g. /Status, /Play, /Browse) are internally proxied to the primary player of a group.
  • /Action endpoint shape varies by service; back/skip/love/ban URLs come from /Status . The source's /Action?service=Slacker&skip=... is one concrete instance; per-action command templates above use the documented pattern.
  • Stream URLs must be URL-encoded. Capture URL examples: Capture%3Aplughw%3Aimxnadadc%2C0%2F48000%2F24%2F2%3Fid%3Dinput0, Hub%3A%2F%2F192.168.1.149%3A11000%2Finput0.
  • LSDP (Lenbrook Service Discovery Protocol): UDP broadcast on port 11430, magic word "LSDP", protocol version 1. Startup: 7 packets at [0,1,2,3,5,7,10s] + 0-250ms random. Steady-state Announce period: 57s + 0-6s random. Class IDs: 0x0001 BluOS Player, 0x0002 BluOS Server, 0x0003 BluOS Player secondary, 0x0004 sovi-mfg, 0x0005 sovi-keypad, 0x0006 BluOS Player pair slave, 0x0007 Remote Web App, 0x0008 BluOS Hub.
  • /Play?inputIndex is documented for firmware newer than v3.8.0 and older than v4.2.0; /Play?inputTypeIndex is documented for firmware v4.2.0 or newer.
  • /Settings?id=capture&schemaVersion=32 — schemaVersion=32 is "the latest" per the source; newer schema versions may be unsupported.
  • This spec covers a subset of the full BluOS API. Source explicitly states "It contains a subset of the API requests documented in the full BluOS API Control Protocol."

Provenance

source_domains:
  - content-bluesound-com.s3.amazonaws.com
  - bluesound.com
source_urls:
  - https://content-bluesound-com.s3.amazonaws.com/uploads/BluOS-Custom-Integration-API_v1.7.pdf
  - https://content-bluesound-com.s3.amazonaws.com/uploads/2022/04/Custom-Integration-API-v1.4.pdf
  - https://www.bluesound.com/pages/downloads
retrieved_at: 2026-06-01T21:59:58.800Z
last_checked_at: 2026-06-02T08:27:34.493Z

Verification Summary

verdict: verified
checked_at: 2026-06-02T08:27:34.493Z
matched_actions: 51
action_count: 51
confidence: medium
summary: "All 51 spec actions have literal endpoint matches in the source and transport values (port 11000, HTTP, no auth) are confirmed verbatim. (5 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "full command catalogue beyond sections 1-12 (the source describes only a subset of the full BluOS API). No safety or interlock material in source."
- "source does not document any unsolicited event/notification stream."
- "source does not document any multi-step command sequences."
- "source contains no safety warnings, interlock procedures, or power-on sequencing requirements."
- "firmware version compatibility ranges, authentication credentials (none documented), full set of capture / input types per device, response status/error codes not enumerated in source."

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