Summary

The BluOS BSW150 is a network music player running the BluOS operating system. This spec covers the BluOS Custom Integration API (version 1.7), which exposes HTTP GET-based control over playback, volume, queue management, presets, input selection, player grouping, and device discovery. All commands are sent as HTTP requests to http://<player_ip>:11000/<endpoint> and responses are UTF-8 encoded XML.

Transport

protocols:
  - http
addressing:
  port: 11000
  base_url: "http://<player_ip>:11000"
  # Note: CI580 multi-zone chassis uses ports 11000/11010/11020/11030 per node.
  # Actual port should be discovered via mDNS (musc._tcp, musp._tcp) or LSDP (UDP port 11430).
auth:
  type: none  # inferred: no auth procedure in source

Traits

- queryable       # inferred from extensive query commands returning state (/Status, /SyncStatus, /Volume, /Playlist, /Presets)
- levelable       # inferred from volume control commands (set level, mute, dB control)
- routable        # inferred from input selection commands (/Play?url=, /Play?inputTypeIndex=)

Actions

# Playback Control
- id: play
  label: Play
  kind: action
  params: []
  notes: "GET /Play — starts playback of current source"

- id: play_seek
  label: Play with Seek
  kind: action
  params:
    - name: seek
      type: integer
      description: "Jump to position in current track (seconds). Only valid if /Status includes <totlen>."
  notes: "GET /Play?seek=<seconds>"

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

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

- id: pause_toggle
  label: Pause Toggle
  kind: action
  params: []
  notes: "GET /Pause?toggle=1 — toggles pause state"

- 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; wraps to first if at end"

- id: back
  label: Back / Previous Track
  kind: action
  params: []
  notes: "GET /Back — returns to start of current track if >4s elapsed, else previous track"

- id: shuffle
  label: Set Shuffle
  kind: action
  params:
    - name: state
      type: integer
      description: "0 = disable shuffle, 1 = 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 track, 2 = repeat off"
  notes: "GET /Repeat?state=<0|1|2>"

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

- id: volume_set_db
  label: Set Volume (Absolute dB)
  kind: action
  params:
    - name: abs_db
      type: number
      description: "Absolute dB volume level."
  notes: "GET /Volume?abs_db=<db>"

- id: volume_adjust_db
  label: Adjust Volume (Relative dB)
  kind: action
  params:
    - name: db
      type: number
      description: "Relative dB change; positive = up, negative = down. Typical step: ±2 dB."
  notes: "GET /Volume?db=<delta-db>"

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

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

# Preset Control
- id: preset_load
  label: Load Preset
  kind: action
  params:
    - name: id
      type: string
      description: "Preset id number, +1 (next preset), or -1 (previous preset)."
  notes: "GET /Preset?id=<id|+1|-1>"

# Play Queue Management
- id: queue_delete_track
  label: Delete Track from Queue
  kind: action
  params:
    - name: id
      type: integer
      description: "Track id (position) to remove from play queue."
  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."
  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 as Playlist
  kind: action
  params:
    - name: name
      type: string
      description: "Name for the saved playlist."
  notes: "GET /Save?name=<playlist_name>"

# Input Selection
- id: select_input_url
  label: Select Input by URL (Active Inputs)
  kind: action
  params:
    - name: url
      type: string
      description: "URL-encoded input capture URL from /RadioBrowse?service=Capture response."
  notes: "GET /Play?url=<URL_value> — for active inputs including HUB inputs"

- id: select_input_by_index
  label: Select Input by Index (Firmware < v4.2.0)
  kind: action
  params:
    - name: inputIndex
      type: integer
      description: "1-based index of input from /Settings?id=capture response (Bluetooth excluded)."
  notes: "GET /Play?inputIndex=<IndexId> — requires firmware newer than v3.8.0 and older than v4.2.0"

- id: select_input_by_type_index
  label: Select Input by Type-Index (Firmware >= v4.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. Index starts at 1."
  notes: "GET /Play?inputTypeIndex=<type>-<index> — requires firmware v4.2.0 or newer"

# Bluetooth Mode
- id: 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 on success"

# Player Grouping
- id: group_add_slave
  label: Add Secondary Player to Group
  kind: action
  params:
    - name: slave
      type: string
      description: "IP address of the secondary player."
    - name: port
      type: integer
      description: "Port of the secondary player (default 11000)."
    - name: group
      type: string
      description: "Optional group name. BluOS assigns default if omitted."
  notes: "GET /AddSlave?slave=<ip>&port=<port>&group=<name>"

- id: group_add_slaves
  label: Add Multiple Secondary 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 matching each slave IP."
  notes: "GET /AddSlave?slaves=<ip1,ip2>&ports=<port1,port2>"

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

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

# Reboot
- id: reboot
  label: Reboot Player
  kind: action
  params: []
  notes: "POST /reboot with body: yes=1 — soft reboot; no XML response, returns plain text confirmation"

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

# Streaming Radio Station Actions
- id: action_skip
  label: Skip (Streaming Radio)
  kind: action
  params: []
  notes: "GET action URL from <action name='skip'> element in /Status response"

- id: action_back
  label: Back (Streaming Radio)
  kind: action
  params: []
  notes: "GET action URL from <action name='back'> element in /Status response"

- id: action_love
  label: Love Track (Streaming Radio)
  kind: action
  params: []
  notes: "GET action URL from <action name='love'> element in /Status response"

- id: action_ban
  label: Ban Track (Streaming Radio)
  kind: action
  params: []
  notes: "GET action URL from <action name='ban'> element in /Status response — also skips to next track"

Feedbacks

# Playback Status — GET /Status
- id: playback_state
  type: enum
  values: [play, pause, stop, stream, connecting]
  notes: "From <state> element in /Status response. 'play' and 'stream' are equivalent."

- id: current_track_title
  type: string
  notes: "From <title1> element in /Status. MUST be used as first line of now-playing UI."

- id: current_track_artist
  type: string
  notes: "From <title2> element in /Status."

- id: current_track_album
  type: string
  notes: "From <title3> element in /Status."

- id: current_position_secs
  type: integer
  notes: "From <secs> element in /Status. Not included in etag calculation; client must self-increment during playback."

- id: track_total_length_secs
  type: integer
  notes: "From <totlen> element in /Status."

- id: can_seek
  type: boolean
  notes: "From <canSeek> element (1=true) in /Status."

- id: shuffle_state
  type: enum
  values: ["0", "1"]
  notes: "From <shuffle> in /Status. 0=off, 1=on."

- id: repeat_state
  type: enum
  values: ["0", "1", "2"]
  notes: "From <repeat> in /Status. 0=repeat queue, 1=repeat track, 2=repeat off."

- id: volume_level
  type: integer
  notes: "From <volume> in /Status or /Volume response. Range 0–100. -1 means fixed volume."

- id: volume_db
  type: number
  notes: "From db attribute in /Volume response."

- id: mute_state
  type: boolean
  notes: "From <mute> or mute attribute (1=muted, 0=unmuted) in /Status or /Volume response."

- id: sleep_timer_remaining
  type: integer
  notes: "From <sleep> in /Status — minutes remaining before sleep timer activates."

- id: current_service
  type: string
  notes: "From <service> element in /Status — service id of current audio source."

- id: current_image_url
  type: string
  notes: "From <image> in /Status — artwork URL for current audio."

- id: stream_format
  type: string
  notes: "From <streamFormat> in /Status."

- id: play_queue_id
  type: integer
  notes: "From <pid> in /Status — unique play queue id."

- id: preset_id
  type: integer
  notes: "From <prid> in /Status — unique preset id."

- id: battery_level
  type: integer
  notes: "From battery.level attribute in /Status — only present if player has battery pack."

# SyncStatus — GET /SyncStatus
- id: player_name
  type: string
  notes: "From name attribute in /SyncStatus response."

- id: player_model
  type: string
  notes: "From modelName attribute in /SyncStatus response."

- id: group_name
  type: string
  notes: "From group attribute in /SyncStatus response."

- id: group_volume
  type: integer
  notes: "From volume attribute in /SyncStatus response."

- id: sync_status_id
  type: string
  notes: "From syncStat in /Status — indicates any change in SyncStatus; matches syncStat in /SyncStatus."

- id: player_initialized
  type: boolean
  notes: "From initialized attribute in /SyncStatus — false means player needs setup via BluOS Controller app."

# Play Queue — GET /Playlist
- id: queue_length
  type: integer
  notes: "From <length> in /Playlist response."

- id: queue_modified
  type: boolean
  notes: "From modified attribute in /Playlist response. 1=modified since loaded."

# Doorbell — GET /Doorbell?play=1
- id: doorbell_volume
  type: integer
  notes: "From volume attribute in /Doorbell response."

- id: doorbell_chime
  type: string
  notes: "From chime attribute in /Doorbell response."

Variables

# Long-poll parameters available on /Status and /SyncStatus
- id: status_etag
  type: string
  description: "etag from /Status response — pass back to enable long-polling change detection."

- id: syncstatus_etag
  type: string
  description: "etag from /SyncStatus response — pass back to enable long-polling change detection."

# Volume range
- id: volume_range
  type: string
  description: "Configurable via BluOS Controller app (Settings -> Player -> Audio). Default range typically -80..0 dB."
  notes: "Not directly settable via CI API; shown for integration reference only."

Events

# Long polling provides change-notification semantics on /Status and /SyncStatus.
# When a long-poll request returns early (before timeout), the player state has changed.
# Clients must NOT make two consecutive long-poll requests less than 1 second apart.
# Regular polling (non-long-poll) must be no faster than once every 30 seconds.
- id: status_changed
  type: poll_response
  notes: "GET /Status?timeout=<seconds>&etag=<prev-etag> — returns immediately on state change or after timeout. Recommended timeout: 100s."

- id: syncstatus_changed
  type: poll_response
  notes: "GET /SyncStatus?timeout=<seconds>&etag=<prev-etag> — returns immediately on sync state change or after timeout. Recommended timeout: 180s."

Macros

# UNRESOLVED: no multi-step macro sequences described explicitly in source; populate if applicable

Safety

confirmation_required_for: []
interlocks: []
# UNRESOLVED: no safety warnings or interlock procedures stated in source
# Note: /reboot command performs a soft reboot — operator should confirm intent before issuing

Notes

Discovery: BluOS players advertise via mDNS (_musc._tcp, _musp._tcp) and also via the proprietary Lenbrook Service Discovery Protocol (LSDP) over UDP broadcast port 11430. LSDP class ID 0x0001 = BluOS Player. Discovery via LSDP is recommended for networks with unreliable multicast.

Port: Standard port is 11000 for all BluOS players except the CI580, which uses 11000/11010/11020/11030 per streamer node.

Protocol: All CI API commands are plain HTTP GET requests (except /reboot which is POST). Responses are UTF-8 XML. No authentication is required.

Long polling: Supported on /Status and /SyncStatus. Use timeout and etag parameters. The /Status response includes a <syncStat> element that indicates if /SyncStatus has changed, reducing the need to poll both endpoints simultaneously.

Input selection firmware split: External input selection via index changed with firmware version:

  • Firmware > v3.8.0 and < v4.2.0: use /Play?inputIndex=<1-based-index>
  • Firmware >= v4.2.0: use /Play?inputTypeIndex=<type>-<index> (e.g., spdif-1, analog-2)

Streaming radio actions: Skip, back, love, and ban for streaming radio stations (e.g., Slacker, Radio Paradise, Amazon Music Prime) are accessed via action URLs embedded in the /Status response <actions> block, not via fixed endpoint paths.

Browse / search: Content browsing and search available via /Browse?key=<key> with hierarchical navigation. All key parameters must be URL-encoded. Use withContextMenuItems=1 to retrieve inline context menus.

Grouping model: Primary player controls the music source; secondary players proxy most requests to the primary. Volume of each secondary player tracked via /SyncStatus long-polling.

Provenance

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

Verification Summary

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

Known Gaps

- "device-specific hardware capabilities (inputs, outputs, amplifier specs) not described in source; source is the generic BluOS CI API document applicable to multiple Bluesound/NAD/DALI products"
- "no multi-step macro sequences described explicitly in source; populate if applicable"
- "no safety warnings or interlock procedures stated in source"
- "BSW150-specific hardware capabilities (number/type of physical inputs, amplifier details) not described in source — source is generic BluOS CI API v1.7 document"
- "firmware version compatibility range for the BSW150 model specifically not stated"
- "whether BSW150 supports all endpoints documented (e.g., /Doorbell, HUB inputs) — source is a platform-wide API reference"
- "model-specific source not located"

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