Summary
The NAD M17 V2i MultiZone Series is a BluOS-enabled AV preamplifier/processor that supports network control via the BluOS Custom Integration API (version 1.7). All commands are sent as HTTP GET requests (except soft reboot which is HTTP POST) to the player's IP address on port 11000. The device responds with UTF-8 encoded XML data. The API supports playback control, volume management, play queue management, input selection, player grouping (multizone), content browsing, and device discovery via LSDP.
Transport
protocols:
- http
addressing:
port: 11000
base_url: "http://<player_ip>:11000"
auth:
type: none # inferred: no auth procedure in source
Traits
- powerable # inferred from reboot command presence; power on/off not explicitly documented
- queryable # inferred from query command examples (/Status, /SyncStatus, /Volume, /Playlist, /Presets)
- routable # inferred from input selection and player grouping commands
- levelable # inferred from volume control commands (/Volume?level=, /Volume?db=, /Volume?abs_db=)
Actions
- id: play
label: Play
kind: action
description: Start playback of the current audio source.
params: []
wire:
method: GET
path: /Play
- id: play_seek
label: Play with Seek
kind: action
description: Start playback at a specified position in the current track.
params:
- name: seek
type: integer
description: Position in seconds to seek to in the current track.
wire:
method: GET
path: /Play?seek={seek}
- id: play_url
label: Play Stream URL
kind: action
description: Start playback of a custom streamed audio URL.
params:
- name: url
type: string
description: URL-encoded stream URL to play.
wire:
method: GET
path: /Play?url={url}
- id: play_input_active
label: Select Active Input
kind: action
description: Select an active input source by its URL from /RadioBrowse?service=Capture.
params:
- name: url
type: string
description: URL-encoded input URL from /RadioBrowse?service=Capture response.
wire:
method: GET
path: /Play?url={url}
- id: play_input_by_index
label: Select External Input by Index (firmware < 4.2.0)
kind: action
description: Select external input by index number (BluOS firmware newer than v3.8.0 and older than v4.2.0). Index starts at 1; Bluetooth excluded.
params:
- name: inputIndex
type: integer
description: Index of input from /Settings?id=capture response, excluding Bluetooth (1-based).
wire:
method: GET
path: /Play?inputIndex={inputIndex}
- id: play_input_by_type_index
label: Select External Input by Type-Index (firmware >= 4.2.0)
kind: action
description: Select external input by type and index (BluOS firmware v4.2.0 or newer).
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'."
wire:
method: GET
path: /Play?inputTypeIndex={inputTypeIndex}
- id: pause
label: Pause
kind: action
description: Pause the current playing audio.
params: []
wire:
method: GET
path: /Pause
- id: pause_toggle
label: Pause Toggle
kind: action
description: Toggle the current pause state.
params: []
wire:
method: GET
path: /Pause?toggle=1
- id: stop
label: Stop
kind: action
description: Stop the current playing audio.
params: []
wire:
method: GET
path: /Stop
- id: skip
label: Skip
kind: action
description: Skip to the next audio track in the play queue.
params: []
wire:
method: GET
path: /Skip
- id: back
label: Back
kind: action
description: Go back to the beginning of the track or previous track in the play queue.
params: []
wire:
method: GET
path: /Back
- id: set_volume
label: Set Volume
kind: action
description: Set the absolute volume level (0–100).
params:
- name: level
type: integer
description: Volume level from 0 to 100.
- name: tell_slaves
type: integer
description: "Optional. 0 = only this player; 1 = all players in group."
wire:
method: GET
path: /Volume?level={level}
- id: volume_up
label: Volume Up
kind: action
description: Increase volume by a relative dB amount (typical value 2 dB).
params:
- name: db
type: number
description: Positive dB value to increase volume by (e.g. 2).
wire:
method: GET
path: /Volume?db={db}
- id: volume_down
label: Volume Down
kind: action
description: Decrease volume by a relative dB amount (typical value -2 dB).
params:
- name: db
type: number
description: Negative dB value to decrease volume by (e.g. -2).
wire:
method: GET
path: /Volume?db={db}
- id: volume_abs_db
label: Set Volume (dB)
kind: action
description: Set the absolute volume level using a dB scale.
params:
- name: abs_db
type: number
description: Absolute dB volume value.
wire:
method: GET
path: /Volume?abs_db={abs_db}
- id: mute_on
label: Mute
kind: action
description: Mute the player.
params: []
wire:
method: GET
path: /Volume?mute=1
- id: mute_off
label: Unmute
kind: action
description: Unmute the player.
params: []
wire:
method: GET
path: /Volume?mute=0
- id: shuffle_on
label: Shuffle On
kind: action
description: Enable shuffle on the current play queue.
params: []
wire:
method: GET
path: /Shuffle?state=1
- id: shuffle_off
label: Shuffle Off
kind: action
description: Disable shuffle.
params: []
wire:
method: GET
path: /Shuffle?state=0
- id: repeat_set
label: Set Repeat
kind: action
description: Set the repeat mode (0=repeat queue, 1=repeat track, 2=off).
params:
- name: state
type: integer
description: "0 = repeat queue, 1 = repeat current track, 2 = repeat off."
wire:
method: GET
path: /Repeat?state={state}
- id: load_preset
label: Load Preset
kind: action
description: Load a preset by preset ID. Use +1 or -1 to navigate to next/previous preset.
params:
- name: id
type: string
description: "Preset id number, +1 for next, or -1 for previous."
wire:
method: GET
path: /Preset?id={id}
- id: browse
label: Browse Content
kind: action
description: Browse available music sources, inputs, and playlists.
params:
- name: key
type: string
description: Optional URL-encoded key from a previous browse response. Absent for top-level browse.
wire:
method: GET
path: /Browse?key={key}
- id: search
label: Search Content
kind: action
description: Search within a music service.
params:
- name: key
type: string
description: Value from a searchKey attribute in a previous browse response.
- name: q
type: string
description: The search string.
wire:
method: GET
path: /Browse?key={key}&q={q}
- id: delete_track
label: Delete Track from Queue
kind: action
description: Remove a track from the current play queue.
params:
- name: id
type: integer
description: Track id (position) of the track to be deleted.
wire:
method: GET
path: /Delete?id={id}
- id: move_track
label: Move Track in Queue
kind: action
description: Move a track from one position to another within the play queue.
params:
- name: old
type: integer
description: Current position of the track.
- name: new
type: integer
description: Destination position of the track.
wire:
method: GET
path: /Move?new={new}&old={old}
- id: clear_queue
label: Clear Queue
kind: action
description: Remove all tracks from the current play queue.
params: []
wire:
method: GET
path: /Clear
- id: save_queue
label: Save Queue
kind: action
description: Save the current play queue as a named BluOS playlist.
params:
- name: name
type: string
description: Name for the saved playlist.
wire:
method: GET
path: /Save?name={name}
- id: add_slave
label: Group Player (Add Secondary)
kind: action
description: Add a secondary player to this (primary) player's group.
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.
wire:
method: GET
path: /AddSlave?slave={slave}&port={port}
- id: add_slaves
label: Group Multiple Players
kind: action
description: Add two or more secondary players to this (primary) player's group.
params:
- name: slaves
type: string
description: Comma-separated IP addresses of secondary players.
- name: ports
type: string
description: Comma-separated port numbers of secondary players.
wire:
method: GET
path: /AddSlave?slaves={slaves}&ports={ports}
- id: remove_slave
label: Ungroup Player (Remove Secondary)
kind: action
description: Remove a secondary player from the group.
params:
- name: slave
type: string
description: IP address of the secondary player to remove.
- name: port
type: integer
description: Port of the secondary player.
wire:
method: GET
path: /RemoveSlave?slave={slave}&port={port}
- id: remove_slaves
label: Ungroup Multiple Players
kind: action
description: Remove two or more secondary players from the group.
params:
- name: slaves
type: string
description: Comma-separated IP addresses of secondary players to remove.
- name: ports
type: string
description: Comma-separated port numbers of secondary players.
wire:
method: GET
path: /RemoveSlave?slaves={slaves}&ports={ports}
- id: reboot
label: Soft Reboot
kind: action
description: Soft reboot the player. Sent as HTTP POST.
params:
- name: yes
type: string
description: Any value (e.g. 1).
wire:
method: POST
path: /reboot
- id: doorbell_chime
label: Doorbell Chime
kind: action
description: Activate doorbell chime.
params: []
wire:
method: GET
path: /Doorbell?play=1
- id: set_bluetooth_mode
label: Set Bluetooth Mode
kind: action
description: Change Bluetooth operating mode.
params:
- name: bluetoothAutoplay
type: integer
description: "0 = Manual, 1 = Automatic, 2 = Guest, 3 = Disabled."
wire:
method: GET
path: /audiomodes?bluetoothAutoplay={bluetoothAutoplay}
- id: streaming_action
label: Streaming Radio Action
kind: action
description: Execute a skip, back, love, or ban action on a supported streaming radio station (e.g. Slacker, Radio Paradise, Amazon Music). The specific URL is provided in the /Status response <actions> element.
params:
- name: action_url
type: string
description: Action URL as provided in the <action> element of the /Status response (e.g. /Action?service=Slacker&skip=...).
wire:
method: GET
path: "{action_url}"
Feedbacks
- id: playback_status
label: Playback Status
type: object
description: Full playback and volume state returned by /Status. Includes state (play/pause/stop/stream/connecting), volume (0-100 or -1 for fixed), mute (0/1), artist, album, track title, shuffle (0/1), repeat (0/1/2), position (secs), track length (totlen), service, quality, and more.
wire:
method: GET
path: /Status
- id: sync_status
label: Player and Group Sync Status
type: object
description: Player identity and grouping information returned by /SyncStatus. Includes player name, model, brand, volume, mute, group name, master/slave IPs, zone information, and schemaVersion.
wire:
method: GET
path: /SyncStatus
- id: volume_state
label: Volume State
type: object
description: Current volume returned by /Volume (no parameters). Includes volume (0-100), db (dB level), mute (0/1), muteVolume, muteDb.
wire:
method: GET
path: /Volume
- id: playlist_state
label: Play Queue State
type: object
description: Current play queue status and track listing returned by /Playlist.
wire:
method: GET
path: /Playlist
- id: presets_list
label: Presets List
type: object
description: All configured presets returned by /Presets. Each preset includes id, name, url, and image.
wire:
method: GET
path: /Presets
- id: browse_result
label: Browse Result
type: object
description: Content browse result returned by /Browse. Includes items of various types (audio, link, artist, album, track, playlist, etc.) with browseKey, playURL, and other navigation attributes.
wire:
method: GET
path: /Browse
- id: input_list
label: Active Input List
type: object
description: List of active inputs returned by /RadioBrowse?service=Capture. Includes Bluetooth, Analog, Optical, Spotify, and hub inputs with their URL values for use with /Play?url=.
wire:
method: GET
path: /RadioBrowse?service=Capture
- id: capture_settings
label: Capture Input Settings
type: object
description: All configured capture inputs in order, returned by /Settings?id=capture&schemaVersion=32. Used to determine inputIndex values for external input selection (firmware < 4.2.0).
wire:
method: GET
path: /Settings?id=capture&schemaVersion=32
Variables
- id: long_poll_timeout
label: Long-Poll Timeout
type: integer
description: "When using long polling on /Status or /SyncStatus, provide a timeout parameter (seconds). Recommended: 100 seconds for /Status, 180 seconds for /SyncStatus. Never faster than 10 seconds for /Status. When not using long polling, restrict polling to at most once every 30 seconds."
- id: long_poll_etag
label: Long-Poll ETag
type: string
description: The etag attribute from the previous /Status or /SyncStatus response. Passed back in the next long-poll request so the server can detect changes and return early.
Events
# UNRESOLVED: The BluOS API uses long polling rather than push notifications.
# The player does not send unsolicited notifications; clients must poll /Status
# or /SyncStatus with long-poll parameters. No separate event/push mechanism
# is documented in the source.
Macros
# UNRESOLVED: no multi-step sequences described explicitly in source beyond the two-step
# input selection procedure (query /RadioBrowse?service=Capture, then /Play?url=...).
Safety
confirmation_required_for:
- reboot
interlocks: []
# NOTE: The /reboot command (POST /reboot?yes=1) soft-reboots the player immediately.
# User confirmation is advisable before issuing this command in an integration.
# No other safety warnings or power interlock sequences are documented in the source.
Notes
-
Port: All BluOS commands use port 11000. The exception is multi-node devices such as the CI580 (four streamers in one chassis): node 1 uses port 11000, node 2 uses 11010, node 3 uses 11020, node 4 uses 11030. For the M17 V2i, port 11000 is expected, but the actual port should be confirmed via MDNS discovery using services
musc._tcpandmusp._tcp. -
Protocol version: The source document is BluOS Custom Integration API version 1.7 (dated 2025-04-09). It documents a subset of the full BluOS API.
-
Response format: All responses are UTF-8 encoded XML. Undocumented XML elements and attributes should be ignored.
-
Long polling: Two polling mechanisms are supported. Regular polling returns immediately; long polling keeps the connection open until state changes or timeout. For /Status, use
timeout(recommended ~100 s, minimum 10 s) andetagparameters. For /SyncStatus, recommended interval is 180 s. Only /Status or /SyncStatus needs long polling at any one time; the /Status<syncStat>element indicates if /SyncStatus has changed. -
Grouped players: When players are grouped, secondary players proxy most requests (/Status, Playback Control, Play Queue Management, Browsing) to the primary player. /SyncStatus long polling is required to track volume of individual secondary players.
-
Input selection (firmware version dependency): The
/Play?inputIndex=parameter works for BluOS firmware newer than v3.8.0 and older than v4.2.0. The/Play?inputTypeIndex=parameter requires BluOS firmware v4.2.0 or newer. For active inputs,/Play?url=(with URL from /RadioBrowse?service=Capture) works across firmware versions. -
Volume range: Volume is constrained to the player's configured available range (typically -80 to 0 dB). The range can be adjusted in the BluOS Controller app under Settings → Player → Audio.
-
Reboot: The soft reboot command uses HTTP POST (
curl -d yes=1 <player_ip>/reboot), not GET. Note this endpoint omits the port in the source example; port 11000 should be assumed. -
Discovery — LSDP: The Lenbrook Service Discovery Protocol (LSDP) uses UDP broadcast on port 11430. Class 0x0001 = BluOS Player (
_musc._tcp). The protocol broadcasts Announce messages approximately every minute at steady state. -
Streaming radio actions: Skip, back, love, and ban actions on streaming radio stations (e.g. Slacker, Radio Paradise, Amazon Music Prime) are available only when a
<streamUrl>element is present in the /Status response and the corresponding<action>element is present with a URL. -
Polling rate guideline: When not using long polling, clients must restrict to at most one request every 30 seconds. When using long polling, no two consecutive requests for the same resource should be made less than 1 second apart.
Provenance
source_domains: []
source_urls: []
retrieved_at: 2026-06-02T22:09:46.883Z
last_checked_at: 2026-06-02T22:09:46.883Z
Verification Summary
verdict: verified
checked_at: 2026-06-02T22:09:46.883Z
matched_actions: 35
action_count: 35
confidence: medium
summary: "All 35 spec actions traced to source (dip-safe re-verify). (4 unresolved item(s) noted in Known Gaps.)"
Known Gaps
- "RS-232 / serial control not documented in this source (BluOS CI API only). Firmware version compatibility ranges not stated. Authentication credentials not mentioned."
- "The BluOS API uses long polling rather than push notifications."
- "no multi-step sequences described explicitly in source beyond the two-step"
- "RS-232/serial control, Telnet control, and any non-HTTP control interface are not covered in the source document. Firmware version compatibility ranges beyond the inputIndex/inputTypeIndex distinction are not stated. Authentication mechanisms are not mentioned (assumed none)."
- "model-specific source not located"
From the AI4AV catalog (https://ai4av.net) · ODbL-1.0