Summary

The NAD T778 (with Volume) Series uses the BluOS Custom Integration API (v1.7) for IP-based control over HTTP on port 11000. This spec covers playback control, volume and mute, play queue management, preset loading, content browsing, input selection, player grouping, and device discovery via the Lenbrook Service Discovery Protocol (LSDP). All commands are HTTP GET requests (except /reboot which uses HTTP POST); responses are UTF-8 encoded XML.

Transport

protocols:
  - http
addressing:
  port: 11000
  base_url: "http://<player_ip>:11000"
auth:
  type: none  # inferred: no auth procedure in source

Traits

- levelable       # inferred from volume control commands present
- queryable       # inferred from /Status and /SyncStatus query commands present
- routable        # inferred from input selection and player grouping commands present

Actions

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

- id: play_seek
  label: Play with Seek
  kind: action
  params:
    - name: seek
      type: integer
      description: Jump to position in seconds in the current track (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 of custom audio
  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 current 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"

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

- id: shuffle_on
  label: Shuffle On
  kind: action
  params: []
  notes: "GET /Shuffle?state=1"

- id: shuffle_off
  label: Shuffle Off
  kind: action
  params: []
  notes: "GET /Shuffle?state=0"

- id: repeat_queue
  label: Repeat Queue
  kind: action
  params: []
  notes: "GET /Repeat?state=0 — repeats the entire play queue"

- id: repeat_track
  label: Repeat Track
  kind: action
  params: []
  notes: "GET /Repeat?state=1 — repeats the current track"

- id: repeat_off
  label: Repeat Off
  kind: action
  params: []
  notes: "GET /Repeat?state=2"

- 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: "0 = only this player, 1 = all players in group (optional)"
  notes: "GET /Volume?level={level}&tell_slaves={on_off}"

- id: set_volume_db
  label: Set Volume (absolute dB)
  kind: action
  params:
    - name: abs_db
      type: number
      description: Absolute volume in dB (within configured range, typically -80..0)
  notes: "GET /Volume?abs_db={db}"

- id: volume_up
  label: Volume Up
  kind: action
  params:
    - name: db
      type: number
      description: Volume increase in dB (typical value 2)
  notes: "GET /Volume?db={positive_db_value}"

- id: volume_down
  label: Volume Down
  kind: action
  params:
    - name: db
      type: number
      description: Volume decrease in dB (typical value -2, must be negative)
  notes: "GET /Volume?db={negative_db_value}"

- 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"

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

- id: delete_track
  label: Delete Track from Queue
  kind: action
  params:
    - name: id
      type: integer
      description: Track id (position) to remove from current play queue
  notes: "GET /Delete?id={position}"

- id: move_track
  label: Move Track in Queue
  kind: action
  params:
    - name: new
      type: integer
      description: New position
    - name: old
      type: integer
      description: Old (current) position
  notes: "GET /Move?new={destination}&old={origin}"

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

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

- id: select_active_input
  label: Select Active Input
  kind: action
  params:
    - name: url
      type: string
      description: URL-encoded input URL from /RadioBrowse?service=Capture response
  notes: "GET /Play?url={URL_value}"

- id: select_input_by_type_index
  label: Select Input by Type Index (firmware v4.2.0+)
  kind: action
  params:
    - name: inputTypeIndex
      type: string
      description: "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'"
  notes: "GET /Play?inputTypeIndex={type-index}"

- id: select_input_by_index
  label: Select Input by Index (firmware v3.8.0 to v4.2.0)
  kind: action
  params:
    - name: inputIndex
      type: integer
      description: Index (1-based) of input from /Settings?id=capture response; Bluetooth excluded
  notes: "GET /Play?inputIndex={IndexId}"

- id: group_add_player
  label: Group Add Player
  kind: action
  params:
    - name: slave
      type: string
      description: IP address of the secondary player to add
    - name: port
      type: integer
      description: Port number of secondary player (default 11000)
    - name: group
      type: string
      description: Group name (optional)
  notes: "GET /AddSlave?slave={ip}&port={port}&group={name}"

- id: group_add_players
  label: Group Add Multiple Players
  kind: action
  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 slave
  notes: "GET /AddSlave?slaves={ip1,ip2}&ports={port1,port2}"

- id: group_remove_player
  label: Group Remove Player
  kind: action
  params:
    - name: slave
      type: string
      description: IP address of the secondary player to remove
    - name: port
      type: integer
      description: Port number of the secondary player
  notes: "GET /RemoveSlave?slave={ip}&port={port}"

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

- id: reboot
  label: Reboot Player
  kind: action
  params: []
  notes: "HTTP POST to /reboot with body yes=1"

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

- 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}"

- id: browse_content
  label: Browse Content
  kind: action
  params:
    - name: key
      type: string
      description: Optional URL-encoded browse key from previous /Browse response; omit for top-level browse
    - name: withContextMenuItems
      type: integer
      description: "Set to 1 to include inline context menus (optional)"
  notes: "GET /Browse?key={key-value}&withContextMenuItems=1"

- id: search_content
  label: Search Music Content
  kind: action
  params:
    - name: key
      type: string
      description: URL-encoded searchKey from a previous /Browse response
    - name: q
      type: string
      description: Search string
  notes: "GET /Browse?key={searchKey}&q={searchText}"

- id: streaming_radio_action
  label: Streaming Radio Action (skip/back/love/ban)
  kind: action
  params:
    - name: service
      type: string
      description: Service name (e.g. Slacker)
    - name: action_params
      type: string
      description: Action parameters taken from the <action> element URL in /Status response
  notes: "GET /Action?service={service}&{action_params} — URLs are provided by <action> elements in /Status response"

Feedbacks

- id: playback_status
  label: Playback Status
  type: object
  description: "Full playback state from GET /Status. Key fields: state (play|pause|stop|stream|connecting), volume (0–100 or -1 for fixed), mute (0|1), shuffle (0|1), repeat (0|1|2), title1, title2, title3, artist, album, totlen, secs, service, streamFormat, syncStat, pid, prid, sleep"
  notes: "Supports long polling: GET /Status?timeout={seconds}&etag={etag-from-previous-response}"

- id: sync_status
  label: Player and Group Sync Status
  type: object
  description: "Player and grouping info from GET /SyncStatus. Key fields: volume (0–100 or -1 for fixed), mute (0|1), name, modelName, model, brand, group, id (IP:port), mac, initialized, master, slave entries, syncStat, zone, zoneMaster, zoneSlave"
  notes: "Supports long polling: GET /SyncStatus?timeout={seconds}&etag={etag}"

- id: volume_state
  label: Volume State
  type: object
  description: "Volume info from GET /Volume. Fields: volume (0–100 or -1 for fixed), db (dB level), mute (0|1), muteDb (pre-mute dB if muted), muteVolume (pre-mute level 0–100 if muted)"

- id: player_state
  label: Playback State
  type: enum
  values: [play, pause, stop, stream, connecting]
  description: "Current player state from /Status <state> element. play and stream are equivalent."

- id: queue_state
  label: Play Queue State
  type: object
  description: "Queue metadata from GET /Playlist?length=1. Fields: length (total tracks), id (queue id matching /Status <pid>), name, modified (0|1)"

- id: preset_list
  label: Preset List
  type: object
  description: "List of all presets from GET /Presets. Each preset has: id, name, url, image. prid matches /Status <prid>."

Variables

- id: volume_level
  label: Volume Level
  type: integer
  range: [0, 100]
  description: "Absolute volume level (0–100). -1 indicates fixed volume. Set via GET /Volume?level={level}"

- id: volume_db
  label: Volume Level (dB)
  type: number
  description: "Volume in dB within configured range (typically -80..0). Set via GET /Volume?abs_db={db}"

- id: mute
  label: Mute State
  type: integer
  values: [0, 1]
  description: "0 = unmuted, 1 = muted. Set via GET /Volume?mute={0|1}"

- id: shuffle_state
  label: Shuffle State
  type: integer
  values: [0, 1]
  description: "0 = off, 1 = on. Set via GET /Shuffle?state={0|1}"

- id: repeat_state
  label: Repeat State
  type: integer
  values: [0, 1, 2]
  description: "0 = repeat queue, 1 = repeat track, 2 = off. Set via GET /Repeat?state={0|1|2}"

- id: bluetooth_mode
  label: Bluetooth Mode
  type: integer
  values: [0, 1, 2, 3]
  description: "0 = Manual, 1 = Automatic, 2 = Guest, 3 = Disabled. Set via GET /audiomodes?bluetoothAutoplay={value}"

Events

# No unsolicited push notifications described in source.
# Long polling on /Status and /SyncStatus is the mechanism for change detection.
# - /Status long poll: recommended timeout 100 seconds; minimum 10 seconds between consecutive calls
# - /SyncStatus long poll: recommended timeout 180 seconds
# - Without long polling: client must not poll faster than once every 30 seconds
# UNRESOLVED: no WebSocket or other push-notification mechanism described in source

Macros

# UNRESOLVED: no multi-step macros described in source

Safety

confirmation_required_for:
  - reboot
interlocks: []
# Note: /reboot requires HTTP POST (not GET) with body yes=1; a GET will not trigger reboot.
# Note: /Delete with wrong track id removes the wrong track permanently from the queue — no undo described in source.

Notes

Port discovery: Port 11000 is used for all BluOS players except the NAD CI580 (which uses 11000, 11010, 11020, 11030 for its four internal nodes). The correct port should be discovered using mDNS (services _musc._tcp / _musp._tcp) or the Lenbrook Service Discovery Protocol (LSDP) on UDP broadcast port 11430.

Long-polling pattern: Two endpoints support long polling: /Status (playback + volume) and /SyncStatus (grouping + volume per player). The etag from the previous response is passed back; a response is only returned when state has changed or timeout expires. Clients must not issue two consecutive long-poll requests for the same resource less than 1 second apart.

Playback state — play vs stream: The state values play and stream have the same meaning. When <streamUrl> is present in /Status, the play queue is not the source; shuffle, repeat, and track position (song) are irrelevant.

Input selection — firmware version dependency: Two different parameter forms exist for external input selection:

  • Firmware v3.8.0 to v4.2.0: /Play?inputIndex={1-based-index} (Bluetooth excluded from index)
  • Firmware v4.2.0 or newer: /Play?inputTypeIndex={type-index} (e.g. spdif-1, analog-1, arc-1)

Grouped players: Secondary players proxy most requests (/Status, playback, queue, browse) to the primary player. Use /SyncStatus long polling to track per-player volume in a group. When setting group volume, use tell_slaves=1 to apply to all players.

Polling rate without long polling: Clients must restrict to at most one request every 30 seconds per resource.

LSDP discovery: The Lenbrook Service Discovery Protocol uses UDP broadcast on port 11430. BluOS players broadcast Announce messages approximately every minute. Class ID 0x0001 identifies a BluOS Player.

Provenance

source_domains: []
source_urls: []
retrieved_at: 2026-06-02T22:09:56.008Z
last_checked_at: 2026-06-02T22:09:56.008Z

Verification Summary

verdict: verified
checked_at: 2026-06-02T22:09:56.008Z
matched_actions: 37
action_count: 37
confidence: medium
summary: "All 37 spec actions traced to source (dip-safe re-verify). (6 unresolved item(s) noted in Known Gaps.)"

Known Gaps

- "The source document covers all BluOS-capable NAD products; NAD T778-specific input types (HDMI ARC, analog, optical, etc.) available on this model are not individually enumerated in the source."
- "no WebSocket or other push-notification mechanism described in source"
- "no multi-step macros described in source"
- "NAD T778-specific model capabilities (e.g. exact set of physical inputs, HDMI ARC/eARC support, preamp output behavior) not specified in source — source covers all BluOS devices generically."
- "firmware version compatibility for all API commands not stated; inputTypeIndex requires v4.2.0+, inputIndex requires v3.8.0 to v4.2.0."
- "volume range minimum/maximum values are device-configured (\"typically -80..0\") and not hard-coded in the API; the actual configured range for the T778 is not stated in source."
- "model-specific source not located"

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