yamaha-receiver-control
Drive a Yamaha MusicCast/YXC receiver from the shell via the yamaha CLI.
Setup (run once)
go install github.com/ljagiello/yamaha-cli/cmd/yamaha@latest
yamaha --version # verify install: prints "yamaha-cli <version>"
Then point the CLI at a receiver (pick one):
| Path | When | Persistence |
|---|---|---|
yamaha discover --add |
First time, interactive | Saved to config with UDN; survives DHCP IP changes |
YAMAHA_HOST=<ip> env var |
One-shot, scripted | None |
--host <ip> flag on each invocation |
Quick experiments | None |
If no host is configured and stdout is a TTY, any command triggers an interactive wizard. If non-TTY, it exits 64 with a hint.
Doing things
All commands work against the active device (resolved per references/CONFIG.md). The --output flag controls formatting; default auto picks table for TTY and JSON when piped.
Core zone control
yamaha status # current zone state
yamaha status | jq -r .power # → "on"
yamaha power on|off|toggle [--no-wait] # default: blocks until power reflects
yamaha volume 60 # absolute integer (0..max from device)
yamaha volume +5 # one HTTP call: setVolume?volume=up&step=5
yamaha volume -- -5 # NOTE: negative deltas need "--" because of cobra
yamaha volume -22.5 --db # absolute, dB-scaled
yamaha volume 50 --percent # absolute, 0..100 → device range
yamaha mute on|off|toggle
yamaha input hdmi1 # validated against device's input list
Sound, scene, tone, sleep
yamaha sound straight # DSP sound program (validated against features)
yamaha decoder dolby_surround # surround decoder type (validated)
yamaha scene 2 # recall scene N (1..features.scene_num)
yamaha tone bass +3 # bass / treble step in device's range (e.g. -12..+12)
yamaha tone treble -2
yamaha tone reset # auto mode, bass=0, treble=0
yamaha sleep 60 # minutes: 0|30|60|90|120 or "off"
Tuner (FM/AM)
yamaha tuner status
yamaha tuner fm 102.5 # MHz; validated against fm_freq range when known
yamaha tuner am 1530 # kHz
yamaha tuner preset 7 --band fm # recall preset on a band
yamaha tuner presets --band fm # list saved presets
NetUSB / MusicCast playback
yamaha netusb info # now-playing payload (input/playback/repeat/shuffle/track/...)
yamaha netusb play | pause | stop | toggle
yamaha netusb next | prev
yamaha netusb ff | rew # ~200 ms hold; auto-ends so the receiver doesn't get stuck
yamaha netusb shuffle | netusb repeat # toggles
yamaha preset list # NetUSB MusicCast presets
yamaha preset recall 3
Discovery, config, ergonomics
yamaha discover # SSDP scan, list found Yamaha devices (no state changes)
yamaha config show # dump resolved config as JSON/YAML/table
yamaha config path # print config file path
Phase 3: push events, multi-room, passthroughs
yamaha watch # NDJSON push events from the active device
yamaha watch --device living-room,bedroom # multi-device watch
yamaha link create living-room bedroom # MusicCast Link group: leader, followers...
yamaha link info # current distribution state
yamaha link dissolve # tear down the group
yamaha reboot --yes # destructive; --yes always required
yamaha raw <method> [k=v ...] # generic YXC GET passthrough (~184 endpoints)
yamaha ynca <line> # legacy line protocol on TCP/50000
For multi-zone receivers (RX-V583 has main + zone2):
yamaha --zone zone2 volume 40
yamaha --zone zone2 input av1
For a second receiver saved in config:
yamaha --device bedroom power on
Full command reference + flag matrix: references/COMMANDS.md.
Scripting and error handling
Always pass --output json when parsing. auto mode emits a table when stdout is a TTY which is unparseable.
volume=$(yamaha status --output json | jq -r .volume)
power=$(yamaha status --output json | jq -r .power)
Exit codes (sysexits-lite):
| Code | Meaning | Typical handling |
|---|---|---|
| 0 | Success | Continue |
| 1 | Validation error / power-on timeout | Bad input; surface to user |
| 2 | CLI usage error (invalid flag combo) | Fix the command |
| 64 | No device configured, non-interactive | Set YAMAHA_HOST or --host |
| 69 | Device unreachable (transport failure, retry exhausted) | Network problem; receiver may be off |
| 70 | YXC response_code != 0, or YNCA @UNDEFINED (feature unsupported, device not ready) |
Check yxc_response_code in error JSON |
| 75 | YNCA @RESTRICTED — valid command, not allowed in the current device state (e.g. zone in standby) |
Fix device state (power on the zone) and retry |
| 130 | SIGINT during a non-watch command | User cancelled |
With --output json, failed commands also emit a structured error to stdout:
{"error": "...", "code": 69, "yxc_response_code": null}
Gotchas
These are non-obvious; check before assuming.
- Negative volume deltas need
--to prevent cobra from parsing-5as a flag:yamaha volume -- -5(or useyamaha volume down --step 5). --dband--percentare absolute-only. Combining with+N/-Nexits 2. To shift by ~2.5 dB, usevolume +5(one integer step ≈ 0.5 dB).- Volume range is per-device. Don't hardcode 0..100. RX-V583 is 0..161 → -80.5..+16.5 dB. Read from
getFeatures(the CLI does this for you;volume 50 --percentis the safe scripted form). - Power on takes 2–5 s to reflect. Default
power onblocks via polling (max 10 s). Use--no-waitonly if you don't need the receiver ready immediately afterwards. - Input switching may auto-fire
prepareInputChangefirst for inputs whosefunc_listrequires it (e.g.,server,net_radio). This is internal and free; just don't be surprised by two HTTP requests in--debug. rebootalways requires--yes, even on a TTY. The flag exists to prevent a stray pipeline from power-cycling the receiver. The CLI treats a post-ack transport error as success — the receiver routinely drops the TCP connection mid-reboot.watchis long-lived. It runs until SIGINT (Ctrl-C); the subscriber auto-reconnects with exponential backoff (1 s → 60 s) when the receiver goes silent for 30 s. Don't set a timeout shorter than the silent-after window or you'll get spurious reconnects.link create/link dissolveneed aliases, not raw IPs — the operation runs against multiple receivers. Add devices withdiscover --addfirst if you only have one configured.tuner fm <MHz>takes MHz,tuner am <kHz>takes kHz. Easy to mix up — the CLI validates against the device's reported range when available, but a typo can still tune to a wrong (valid) frequency.rawparameters are url-encoded automatically. Repeated keys append, soclient_list[0].ip_address=…works as a positionalk=varg. Quote args with brackets to keep the shell happy.yncaruns a one-shot probe before sending. Devices that don't speak YNCA fail with exit 70 and adoes not support YNCAmessage. RX-V583 supports both protocols.- DHCP IP changes are handled transparently when the device was added via
discover --add(the UDN is stored). Anonymous--host/YAMAHA_HOSTcalls do not auto-recover. - No HTTPS, no auth. Anyone on the LAN can issue commands. Don't expose the receiver to untrusted networks.
- YXC is GET-only. All operations are
GET /YamahaExtendedControl/v1/<method>?<params>on port 80. No POSTs, no JSON request bodies. (YNCA is a separate line protocol on TCP/50000.)
Debugging
Pass --debug (or set YAMAHA_DEBUG=1) to log every YXC request + response to stderr in →/← form. Stdout stays clean — pipe through jq while debugging:
yamaha --debug status 2> trace.log | jq .volume_db
Useful debug-trace prefixes:
→ GET <url>/← <status> <body>— every request/response.→ retry— the silent single retry on transient transport errors fired.→ rediscover alias=… udn=…— DHCP-resilience kicked in (config will be updated atomically).link: rollback after …—link createpartial-failure rollback ran (leader stopped, follower server pointers cleared).
Workflows
Power on, set inputs and volume, check state
yamaha power on \
&& yamaha input hdmi2 \
&& yamaha volume 50 --percent \
&& yamaha status
No manual sleep needed — power on blocks until ready.
Discover and save a receiver in CI/scripts
yamaha discover --output json | jq '.[0]' # see what's there
yamaha discover --add # interactive; wizards through pick + alias
Scripted volume nudge with sanity check
current=$(yamaha status --output json | jq -r .volume)
if [ "$current" -lt 100 ]; then
yamaha volume +5
fi
Movie night: set up scene, sound, sleep
yamaha scene 1 \
&& yamaha sound movie \
&& yamaha volume 55 --percent \
&& yamaha sleep 120
Watch volume changes during a session
yamaha watch | jq 'select(.delta.main.volume) | {ts, vol: .delta.main.volume}'
Runs until Ctrl-C; auto-reconnects on a 30 s silent window.
Group two rooms, play, then dissolve
yamaha link create living-room bedroom \
&& yamaha --device living-room netusb play
# ... later ...
yamaha link dissolve living-room
References
- references/COMMANDS.md — full subcommand reference, flag matrix, zone-scope table.
- references/CONFIG.md — config schema, resolution order, env vars, DHCP-resilience details.
- Upstream repo: https://github.com/ljagiello/yamaha-cli.