Summary

NAD 450 is a BluOS-enabled streaming player. This spec covers the BluOS Custom Integration HTTP API for playback control, volume, queue management, presets, content browsing, grouping, and direct input selection, plus the Lenbrook Service Discovery Protocol (LSDP) for UDP-based device discovery.

Transport

protocols:
  - http
  - udp
addressing:
  port: 11000  # BluOS CI HTTP port (CI580 uses 11000/11010/11020/11030 per node)
auth:
  type: none  # inferred: no auth procedure in source

LSDP discovery:

protocols:
  - udp
addressing:
  port: 11430  # LSDP discovery port (IANA assigned to Lenbrook)

Traits

# - powerable       (soft reboot via POST /reboot only; no power on/off)
# - routable        # inferred from input selection commands (/Play?url=, /Play?inputTypeIndex=)
# - queryable       # inferred from /Status, /SyncStatus, /Playlist, /Presets queries
# - levelable       # inferred from /Volume?level=, /Volume?db=, /Volume?mute= commands
- routable
- queryable
- levelable

Actions

# --- Status queries ---
- id: status
  label: Playback Status
  kind: query
  command: "/Status"
  params: []

- id: status_long_poll
  label: Playback Status (long poll)
  kind: query
  command: "/Status?timeout={timeout}&etag={etag}"
  params:
    - name: timeout
      type: integer
      description: Long-poll interval in seconds (recommended 100; never faster than 10).
    - name: etag
      type: string
      description: etag value from previous /Status response.

- id: sync_status
  label: Player and Group Sync Status
  kind: query
  command: "/SyncStatus"
  params: []

- id: sync_status_long_poll
  label: Sync Status (long poll)
  kind: query
  command: "/SyncStatus?timeout={timeout}&etag={etag}"
  params:
    - name: timeout
      type: integer
      description: Long-poll interval in seconds (recommended 180).
    - name: etag
      type: string
      description: etag value from previous /SyncStatus response.

# --- Volume control ---
- id: volume_set
  label: Set Volume Level
  kind: action
  command: "/Volume?level={level}&tell_slaves={tell_slaves}"
  params:
    - name: level
      type: integer
      description: Absolute volume 0-100.
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all grouped players.

- id: volume_mute_set
  label: Set Mute State
  kind: action
  command: "/Volume?mute={mute}&tell_slaves={tell_slaves}"
  params:
    - name: mute
      type: integer
      description: 0 = mute; 1 = unmute.
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all grouped players.

- id: volume_set_db
  label: Set Volume (dB)
  kind: action
  command: "/Volume?abs_db={db}&tell_slaves={tell_slaves}"
  params:
    - name: db
      type: number
      description: Absolute volume in dB.
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all grouped players.

- id: volume_change_db
  label: Change Volume (relative dB)
  kind: action
  command: "/Volume?db={delta}&tell_slaves={tell_slaves}"
  params:
    - name: delta
      type: number
      description: Relative dB change (positive = up, negative = down; typical step 2dB).
    - name: tell_slaves
      type: integer
      description: 0 = only this player; 1 = apply to all grouped players.

# --- Playback control ---
- id: play
  label: Play
  kind: action
  command: "/Play"
  params: []

- id: play_seek
  label: Play with Seek
  kind: action
  command: "/Play?seek={seconds}"
  params:
    - name: seconds
      type: integer
      description: Position to jump to in current track.

- id: play_seek_id
  label: Play with Seek and Track ID
  kind: action
  command: "/Play?seek={seconds}&id={trackid}"
  params:
    - name: seconds
      type: integer
      description: Position to jump to in track.
    - name: trackid
      type: integer
      description: Track id within the queue.

- id: play_url
  label: Play Stream URL
  kind: action
  command: "/Play?url={encodedStreamURL}"
  params:
    - name: encodedStreamURL
      type: string
      description: URL-encoded stream URL.

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

- id: pause_toggle
  label: Pause Toggle
  kind: action
  command: "/Pause?toggle=1"
  params: []

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

- id: skip
  label: Skip Next Track
  kind: action
  command: "/Skip"
  params: []

- id: back
  label: Skip Previous Track
  kind: action
  command: "/Back"
  params: []

- id: shuffle
  label: Shuffle Queue
  kind: action
  command: "/Shuffle?state={state}"
  params:
    - name: state
      type: integer
      description: 0 = disable shuffle; 1 = enable shuffle.

- id: repeat
  label: Repeat Mode
  kind: action
  command: "/Repeat?state={state}"
  params:
    - name: state
      type: integer
      description: 0 = repeat queue; 1 = repeat current track; 2 = repeat off.

# --- Streaming radio actions ---
- id: action_radio
  label: Streaming Radio Action
  kind: action
  command: "/Action?service={service-name}&action={action-URL}"
  params:
    - name: service-name
      type: string
      description: Music service name (e.g. Slacker).
    - name: action-URL
      type: string
      description: URL provided in /Status <action> element (skip/back/love/ban).

# --- Play queue management ---
- id: playlist
  label: List Play Queue
  kind: query
  command: "/Playlist"
  params: []

- id: playlist_status
  label: Play Queue Status
  kind: query
  command: "/Playlist?length=1"
  params: []

- id: playlist_range
  label: Play Queue Range
  kind: query
  command: "/Playlist?start={first}&end={last}"
  params:
    - name: first
      type: integer
      description: First entry index (0-based).
    - name: last
      type: integer
      description: Last entry index.

- id: delete_track
  label: Delete Track
  kind: action
  command: "/Delete?id={position}"
  params:
    - name: position
      type: integer
      description: Track position in queue to remove.

- id: move_track
  label: Move Track
  kind: action
  command: "/Move?new={destination}&old={origin}"
  params:
    - name: destination
      type: integer
      description: New track position.
    - name: origin
      type: integer
      description: Old track position.

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

- id: save_queue
  label: Save Play Queue
  kind: action
  command: "/Save?name={playlist_name}"
  params:
    - name: playlist_name
      type: string
      description: Name for saved BluOS playlist.

# --- Presets ---
- id: presets
  label: List Presets
  kind: query
  command: "/Presets"
  params: []

- id: preset_load
  label: Load Preset
  kind: action
  command: "/Preset?id={presetId}"
  params:
    - name: presetId
      type: string
      description: Preset id, or +1 for next preset, or -1 for previous preset.

# --- Content browsing / search ---
- id: browse
  label: Browse Content
  kind: query
  command: "/Browse?key={key-value}"
  params:
    - name: key-value
      type: string
      description: URL-encoded browseKey from prior response.

- id: browse_with_context
  label: Browse with Context Menu
  kind: query
  command: "/Browse?key={key-value}&withContextMenuItems=1"
  params:
    - name: key-value
      type: string
      description: URL-encoded browseKey from prior response.

- id: browse_search
  label: Search Music Content
  kind: query
  command: "/Browse?key={key-value}&q={searchText}"
  params:
    - name: key-value
      type: string
      description: URL-encoded searchKey from prior response.
    - name: searchText
      type: string
      description: Search term.

# --- Player grouping ---
- id: add_slave
  label: Group One Player
  kind: action
  command: "/AddSlave?slave={secondaryPlayerIP}&port={secondaryPlayerPort}&group={GroupName}"
  params:
    - name: secondaryPlayerIP
      type: string
      description: IP of secondary player.
    - name: secondaryPlayerPort
      type: integer
      description: Port of secondary player (default 11000).
    - name: GroupName
      type: string
      description: Optional group name.

- id: add_slaves
  label: Group Multiple Players
  kind: action
  command: "/AddSlave?slaves={secondaryPlayerIPs}&ports={secondaryPlayerPorts}"
  params:
    - name: secondaryPlayerIPs
      type: string
      description: Comma-separated IPs of secondary players.
    - name: secondaryPlayerPorts
      type: string
      description: Comma-separated ports of secondary players.

- id: remove_slave
  label: Remove One Player From Group
  kind: action
  command: "/RemoveSlave?slave={secondaryPlayerIP}&port={secondaryPlayerPort}"
  params:
    - name: secondaryPlayerIP
      type: string
      description: IP of secondary player to remove.
    - name: secondaryPlayerPort
      type: integer
      description: Port of secondary player to remove.

- id: remove_slaves
  label: Remove Multiple Players From Group
  kind: action
  command: "/RemoveSlave?slaves={secondaryPlayerIPs}&ports={secondaryPlayerPorts}"
  params:
    - name: secondaryPlayerIPs
      type: string
      description: Comma-separated IPs of secondary players.
    - name: secondaryPlayerPorts
      type: string
      description: Comma-separated ports of secondary players.

# --- System ---
- id: reboot
  label: Soft Reboot Player
  kind: action
  command: "POST /reboot"
  params: []

- id: doorbell
  label: Play Doorbell Chime
  kind: action
  command: "/Doorbell?play=1"
  params: []

# --- Input selection ---
- id: active_input
  label: Select Active Input (URL-based)
  kind: action
  command: "/Play?url={URL_value}"
  params:
    - name: URL_value
      type: string
      description: URL from /RadioBrowse?service=Capture response.

- id: external_input_legacy
  label: Select External Input (legacy, firmware >3.8.0 and <4.2.0)
  kind: action
  command: "/Play?inputIndex={IndexId}"
  params:
    - name: IndexId
      type: integer
      description: Input index from /Settings?id=capture (Bluetooth excluded; starts at 1).

- id: external_input
  label: Select External Input (firmware >=4.2.0)
  kind: action
  command: "/Play?inputTypeIndex={type-index}"
  params:
    - name: type-index
      type: string
      description: "Type and index (e.g. spdif-2). Types: spdif, analog, coax, bluetooth, arc, earc, phono, computer, aesebu, balanced, microphone."

# --- Bluetooth ---
- id: bluetooth_mode
  label: Set Bluetooth Mode
  kind: action
  command: "/audiomodes?bluetoothAutoplay={value}"
  params:
    - name: value
      type: integer
      description: 0 = Manual; 1 = Automatic; 2 = Guest; 3 = Disabled.

# --- LSDP discovery (UDP broadcast) ---
- id: lsdp_query_standard
  label: LSDP Query (Broadcast Response)
  kind: action
  command: "UDP 11430 broadcast Query Message Type Q (0x51)"
  params:
    - name: classes
      type: string
      description: List of 16-bit service class identifiers to query.

- id: lsdp_query_unicast
  label: LSDP Query (Unicast Response)
  kind: action
  command: "UDP 11430 unicast Query Message Type R (0x52)"
  params:
    - name: classes
      type: string
      description: List of 16-bit service class identifiers to query.

Feedbacks

- id: playback_state
  type: enum
  values: [play, pause, stop, stream, connecting]
  description: Reported via <state> in /Status and command responses.
- id: volume_level
  type: integer
  description: 0..100, or -1 if volume fixed. From <volume> in /Status or /Volume response.
- id: volume_db
  type: number
  description: Current volume in dB. From <db> in /Status or /Volume response.
- id: mute_state
  type: integer
  description: 0 = unmuted; 1 = muted. From <mute> in /Status or /Volume response.
- id: shuffle_state
  type: integer
  description: 0 = off; 1 = on. From <shuffle> in /Status.
- id: repeat_state
  type: integer
  description: 0 = repeat queue; 1 = repeat track; 2 = repeat off. From <repeat> in /Status.
- id: etag
  type: string
  description: Opaque change-detection token returned in /Status and /SyncStatus root attributes.
- id: sync_stat
  type: integer
  description: Change id for /SyncStatus; matches <syncStat> in /Status.
- id: group_name
  type: string
  description: Name of the player group. From <group> in /SyncStatus.
- id: player_name
  type: string
  description: Player name. From <name> in /SyncStatus.
- id: player_model
  type: string
  description: Player model name. From <modelName> in /SyncStatus.
- id: player_brand
  type: string
  description: Player brand name. From <brand> in /SyncStatus.
- id: player_mac
  type: string
  description: Player unique id (often MAC address). From <mac> in /SyncStatus.
- id: track_title
  type: string
  description: Title of current track. From <title1>/<title2>/<title3> in /Status.
- id: track_artist
  type: string
  description: Artist of current track.
- id: track_album
  type: string
  description: Album of current track.
- id: service
  type: string
  description: Current music service id. From <service> in /Status.
- id: track_position_secs
  type: integer
  description: Seconds elapsed in current track. From <secs> in /Status.
- id: track_length_secs
  type: integer
  description: Total track length in seconds. From <totlen> in /Status.
- id: can_seek
  type: integer
  description: 1 if seeking is allowed. From <canSeek> in /Status.

Variables

# UNRESOLVED: volume range is configured per-player (default -80..0 dB); not settable via documented CI commands.

Events

# UNRESOLVED: unsolicited push notifications are not described in source; clients poll /Status or /SyncStatus with long polling.

Macros

# UNRESOLVED: source does not document named macros.

Safety

confirmation_required_for:
  - delete  # context-menu delete action: "User confirmation should be requested" (per source)
interlocks: []
# UNRESOLVED: no other safety procedures or interlocks documented in source.

Notes

  • All BluOS CI requests are HTTP GET to http://<player_ip>:11000/<request> unless noted. Reboot is HTTP POST to /reboot.
  • Default port is 11000. The NAD CI580 (4-streamer chassis) uses 11000/11010/11020/11030; not applicable to NAD 450 but mentioned for completeness.
  • Discover port via mDNS services musc.tcp and musp.tcp, or use LSDP (UDP 11430 broadcast).
  • Long polling recommended for /Status (100s) and /SyncStatus (180s); do not poll /Status faster than once per 10s without long polling.
  • Streaming radio actions (/Action) use URLs returned in the /Status <actions> block — endpoint and parameters vary by service.
  • /Skip and /Back require no <streamUrl> in /Status; use /Action for streaming radio navigation.
  • Input selection has two flavors: URL-based (active inputs via /RadioBrowse) and inputTypeIndex (firmware >=4.2.0). The legacy /Play?inputIndex= form applies only to firmware >3.8.0 and <4.2.0.

Self-check: no voltage/current invented, port 11000 + 11430 stated, no baud assumed, status=draft, confidence=low, YAML valid, unresolved markers present.

## Provenance

```yaml
source_domains:
  - bluos.io
source_urls:
  - https://bluos.io/wp-content/uploads/2025/06/BluOS-Custom-Integration-API_v1.7.pdf
retrieved_at: 2026-07-14T19:51:48.199Z
last_checked_at: 2026-09-12T22:18:26.887Z

Verification Summary

verdict: verified
checked_at: 2026-09-12T22:18:26.887Z
matched_actions: 44
action_count: 44
confidence: medium
summary: "All 44 spec actions have literal source matches and transport values are confirmed; coverage ratio above 0.9. (6 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- /RadioBrowse
- /Settings
- "power on/off commands not documented in source (only soft reboot via POST /reboot is shown)"
- "volume range is configured per-player (default -80..0 dB); not settable via documented CI commands."
- "unsolicited push notifications are not described in source; clients poll /Status or /SyncStatus with long polling."
- "source does not document named macros."
- "no other safety procedures or interlocks documented in source."
- "NAD 450-specific firmware version not stated in source; protocol applicability based on BluOS family coverage."

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