Summary

The BluOS B160S is a network music player running the BluOS operating system. This spec covers the BluOS Custom Integration API v1.7 (HTTP/TCP, port 11000), which provides control of playback, volume, input selection, play queue management, presets, player grouping, and content browsing. All commands are HTTP GET requests (except reboot, which is an HTTP POST); responses are UTF-8 encoded XML.

Transport

protocols:
  - http
addressing:
  port: 11000
  base_url: "http://<player_ip>:11000"
  # Note: The CI580 uses ports 11010, 11020, 11030 for nodes 2–4. For standard
  # single-streamer players including the B160S, port 11000 is used.
  # Port should be confirmed via mDNS (services _musc._tcp and _musp._tcp) or
  # the Lenbrook Service Discovery Protocol (LSDP, UDP port 11430).
auth:
  type: none  # inferred: no auth procedure in source

Traits

- queryable    # inferred from query command examples (/Status, /SyncStatus, /Volume, /Playlist, /Presets)
- levelable    # inferred from volume/mute control commands (/Volume?level=, /Volume?db=, /Volume?mute=)
- routable     # inferred from input selection commands (/Play?url=, /Play?inputTypeIndex=)
- powerable    # inferred from /reboot (soft reboot) command; power on/off not separately documented

Actions

# --- Playback Control ---

- id: play
  label: Play
  kind: action
  params: []
  notes: "GET /Play — resumes current audio source"

- id: play_seek
  label: Play (Seek)
  kind: action
  params:
    - name: seek
      type: integer
      description: "Jump to position in seconds in the current track. Only valid if /Status response includes <totlen>."
    - name: id
      type: integer
      description: "Optional track id in queue (0-based). Combine with seek to jump to a position in a specific track."
  notes: "GET /Play?seek=<seconds>[&id=<trackid>]"

- id: play_url
  label: Play Stream URL
  kind: action
  params:
    - name: url
      type: string
      description: "URL-encoded stream URL of custom audio to play."
  notes: "GET /Play?url=<encodedStreamURL>"

- id: pause
  label: Pause
  kind: action
  params: []
  notes: "GET /Pause"

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

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

- id: skip
  label: Skip (Next Track)
  kind: action
  params: []
  notes: "GET /Skip — skips to next track in play queue. Wraps to first if on last track."

- id: back
  label: Back (Previous Track)
  kind: action
  params: []
  notes: "GET /Back — returns to start of current track if >4s played, else 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."
  notes: "GET /Shuffle?state=<0|1>"

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

- id: streaming_action
  label: Streaming Radio Action
  kind: action
  params:
    - name: action_url
      type: string
      description: "Full action URL taken from the <action> element in /Status response (e.g., /Action?service=Slacker&skip=<id>)."
  notes: "GET <action_url> — used for skip, back, love, ban on streaming radio stations such as Slacker, Radio Paradise, Amazon Music Prime Stations."

# --- Volume Control ---

- id: set_volume
  label: Set Volume
  kind: action
  params:
    - name: level
      type: integer
      description: "Absolute volume level 0–100."
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = affect only this player, 1 = affect all grouped players."
  notes: "GET /Volume?level=<level>[&tell_slaves=<0|1>]"

- id: set_volume_db
  label: Set Volume (Absolute dB)
  kind: action
  params:
    - name: abs_db
      type: number
      description: "Absolute volume in dB. Clamped to configured available range (typically -80..0)."
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = affect only this player, 1 = affect all grouped players."
  notes: "GET /Volume?abs_db=<db>[&tell_slaves=<0|1>]"

- id: volume_relative_db
  label: Volume Relative Adjust (dB)
  kind: action
  params:
    - name: db
      type: number
      description: "Relative dB change; positive = up, negative = down. Typical value is ±2."
    - name: tell_slaves
      type: integer
      description: "Optional. 0 = affect only this player, 1 = affect all grouped players."
  notes: "GET /Volume?db=<delta-db>[&tell_slaves=<0|1>]"

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

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

# --- Play Queue Management ---

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

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

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

- id: queue_save
  label: Save Play Queue
  kind: action
  params:
    - name: name
      type: string
      description: "Name for the saved BluOS playlist."
  notes: "GET /Save?name=<playlist_name>"

# --- Presets ---

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

# --- Input Selection ---

- id: select_input_active
  label: Select Active Input (by URL)
  kind: action
  params:
    - name: url
      type: string
      description: "URL-encoded input URL from /RadioBrowse?service=Capture response."
  notes: "GET /Play?url=<URL_value> — for active inputs. Discover available URL values via GET /RadioBrowse?service=Capture."

- id: select_input_by_index
  label: Select External Input (by Index, firmware <4.2.0)
  kind: action
  params:
    - name: inputIndex
      type: integer
      description: "1-based index of inputs from /Settings?id=capture&schemaVersion=32 response, excluding Bluetooth."
  notes: "GET /Play?inputIndex=<IndexId> — 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: "Input type-index string, e.g. 'spdif-1', 'analog-1', 'bluetooth-1', 'arc-1', 'earc-1', 'phono-1', 'coax-1', 'computer-1', 'aesebu-1', 'balanced-1', 'microphone-1'. Index starts at 1 per type."
  notes: "GET /Play?inputTypeIndex=<type-index> — for firmware v4.2.0 or newer."

# --- Player Grouping ---

- id: group_add_player
  label: Add Player to Group (Single)
  kind: action
  params:
    - name: slave
      type: string
      description: "IP address of secondary player to add."
    - name: port
      type: integer
      description: "Port of secondary player (default 11000)."
    - name: group
      type: string
      description: "Optional. Name for the group."
  notes: "GET /AddSlave?slave=<ip>&port=<port>[&group=<GroupName>] — direct to primary player."

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

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

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

# --- Player Reboot ---

- id: reboot
  label: Reboot Player (Soft)
  kind: action
  params:
    - name: yes
      type: string
      description: "Any value (e.g. '1')."
  notes: "HTTP POST /reboot with body yes=1. This is the only POST command in the API."

# --- Doorbell ---

- id: doorbell_play
  label: Play Doorbell Chime
  kind: action
  params: []
  notes: "GET /Doorbell?play=1"

# --- Bluetooth Mode ---

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

# --- Content Browsing ---

- id: browse
  label: Browse Music Content
  kind: action
  params:
    - name: key
      type: string
      description: "Optional. URL-encoded browse key from prior /Browse response (browseKey, nextKey, parentKey, or contextMenuKey). Absence returns top-level browse."
    - name: withContextMenuItems
      type: integer
      description: "Optional. Set to 1 to include inline context menu items in response."
  notes: "GET /Browse[?key=<key-value>][&withContextMenuItems=1]"

- id: search_music
  label: Search Music Content
  kind: action
  params:
    - name: key
      type: string
      description: "URL-encoded searchKey value from a prior /Browse response."
    - name: q
      type: string
      description: "Search query string."
  notes: "GET /Browse?key=<searchKey>&q=<searchText>"

Feedbacks

- id: playback_status
  type: object
  description: "Full playback and volume state. Poll via GET /Status. Supports long polling with timeout and etag parameters."
  attributes:
    state:
      type: enum
      values: [play, stream, pause, stop, connecting]
    volume:
      type: integer
      description: "Volume 0–100, or -1 for fixed volume."
    db:
      type: number
      description: "Volume level in dB."
    mute:
      type: integer
      values: [0, 1]
    muteVolume:
      type: integer
      description: "Unmuted volume (0–100) when player is muted."
    muteDb:
      type: number
      description: "Unmuted volume in dB when player is muted."
    repeat:
      type: integer
      values: [0, 1, 2]
      description: "0=repeat queue, 1=repeat track, 2=repeat off."
    shuffle:
      type: integer
      values: [0, 1]
    album:
      type: string
    artist:
      type: string
    name:
      type: string
    title1:
      type: string
      description: "First line of now-playing metadata. MUST be used instead of album/artist/name in UI."
    title2:
      type: string
    title3:
      type: string
    secs:
      type: integer
      description: "Seconds played. Not included in etag calculation."
    totlen:
      type: integer
      description: "Total track length in seconds."
    canSeek:
      type: integer
      values: [0, 1]
    pid:
      type: integer
      description: "Unique play queue id."
    prid:
      type: integer
      description: "Unique preset id."
    syncStat:
      type: integer
      description: "Changes when /SyncStatus response changes."
    service:
      type: string
    streamFormat:
      type: string
    quality:
      type: string
      description: "cd, hd, dolbyAudio, mqa, mqaAuthored, or numeric bitrate."
    sleep:
      type: string
      description: "Minutes remaining before sleep timer activates."
    actions:
      type: object
      description: "Present for streaming radio stations. Contains action elements with skip/back/love/ban URLs."
    etag:
      type: string
      description: "Opaque value for long-polling change detection."

- id: sync_status
  type: object
  description: "Player identity, grouping, and volume state. Poll via GET /SyncStatus. Supports long polling."
  attributes:
    name:
      type: string
      description: "Player name."
    modelName:
      type: string
    model:
      type: string
    brand:
      type: string
    volume:
      type: integer
      description: "Volume 0–100, or -1 for fixed."
    mute:
      type: integer
    group:
      type: string
    id:
      type: string
      description: "Player IP:port."
    mac:
      type: string
    initialized:
      type: boolean
    master:
      type: string
      description: "Primary player IP (only present if this player is secondary in a group)."
    slave:
      type: array
      description: "Secondary players (only present if this player is primary in a group)."
    syncStat:
      type: integer
    etag:
      type: string

- id: volume_state
  type: object
  description: "Current volume state returned by GET /Volume (also returned after any /Volume set command)."
  attributes:
    volume:
      type: integer
      description: "0–100, or -1 for fixed."
    db:
      type: number
    mute:
      type: integer
    muteVolume:
      type: integer
    muteDb:
      type: number

- id: playlist_state
  type: object
  description: "Play queue status from GET /Playlist or GET /Playlist?length=1."
  attributes:
    name:
      type: string
    modified:
      type: integer
      values: [0, 1]
    length:
      type: integer
    id:
      type: integer
    songs:
      type: array
      description: "Array of song elements. Each has albumid, service, artistid, songid, id (queue position), title, art, alb."

- id: presets_list
  type: object
  description: "All presets on the player from GET /Presets."
  attributes:
    prid:
      type: integer
    presets:
      type: array
      description: "Each preset has id, name, url, image."

Variables

# UNRESOLVED: no player-level settable parameters beyond the action endpoints above are documented in the source for per-player persistent configuration via the CI API.

Events

# The API supports long polling (timeout + etag) on /Status and /SyncStatus for change
# notification, but does not define unsolicited push events. The /Status <syncStat> element
# signals when /SyncStatus has changed, avoiding the need to separately poll both endpoints.
# UNRESOLVED: whether the B160S sends any unsolicited TCP/HTTP notifications outside of long polling.

Macros

# UNRESOLVED: no multi-step macro sequences are documented in the source.

Safety

confirmation_required_for:
  - reboot  # soft reboot is irreversible during the reboot window; source implies user acknowledgement via browser UI
interlocks: []
# UNRESOLVED: no power sequencing requirements or safety interlocks are described in the source for the B160S.

Notes

Request format: All commands (except reboot) are HTTP GET to http://<player_ip>:11000/<command>?<params>. Parameters are standard URL-encoded name/value pairs. Responses are UTF-8 encoded XML. The reboot command is HTTP POST to /reboot with body yes=1.

Port discovery: Port 11000 is the default for single-streamer BluOS players. The actual port should be discovered via mDNS (_musc._tcp, _musp._tcp) or the Lenbrook Service Discovery Protocol (LSDP) on UDP port 11430.

Long polling: /Status and /SyncStatus both support long polling. Pass timeout=<seconds> and etag=<value-from-previous-response>. The connection is held until state changes or timeout. Without long polling, clients should poll at most once every 30 seconds. With long polling active, do not make two consecutive requests for the same resource less than 1 second apart.

Player grouping: When players are grouped, the secondary players proxy most requests to the primary player. Volume of individual secondary players is tracked via /SyncStatus, not /Status.

Input selection (firmware branching): Two command variants exist for external input selection. Use /Play?inputIndex= for firmware newer than v3.8.0 and older than v4.2.0. Use /Play?inputTypeIndex= for firmware v4.2.0 or newer. Active inputs (visible in /RadioBrowse?service=Capture) can also be selected via /Play?url=<URL_value>.

Streaming radio actions: Skip, back, love, and ban are not available on all streaming sources. Check for the presence of <actions> in the /Status response before presenting these controls. The action URLs must be used verbatim from the response.

secs field in /Status: The secs value (elapsed playback seconds) is excluded from the etag calculation and will not by itself cause a long-poll to return. Clients must increment the displayed position locally based on elapsed time since the response when state is play or stream.

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-16T17:46:59.255Z
last_checked_at: 2026-06-02T21:54:04.057Z

Verification Summary

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

Known Gaps

- "The source document is the BluOS Custom Integration API, which covers the entire BluOS product family (Bluesound, NAD Electronics, DALI, etc.). It does not enumerate which specific features or inputs are present on the B160S model versus other models. Input types available on the B160S (analog, optical, Bluetooth, etc.) are not confirmed by the source for this specific model."
- "no player-level settable parameters beyond the action endpoints above are documented in the source for per-player persistent configuration via the CI API."
- "whether the B160S sends any unsolicited TCP/HTTP notifications outside of long polling."
- "no multi-step macro sequences are documented in the source."
- "no power sequencing requirements or safety interlocks are described in the source for the B160S."
- "API version 1.7 (2025-04-09) is the source version. The spec does not document which BluOS firmware version maps to API version 1.7."
- "Specific input types physically available on the B160S are not enumerated in this API document."

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