Status: in progress.
- Fast CLI to control BluOS players (HTTP+XML Custom Integration API).
- Zero setup default: auto-discover players via mDNS; pick device via
--device/BLU_DEVICE/ config. - Core controls: status, playback, volume, grouping.
- Good defaults: timeouts, readable errors, JSON output for scripting.
- Strong hygiene: formatter + linter + CI + tests.
- Fixed groups / surround / advanced settings flows (unless API docs stabilize).
- Auth for BluOS API (it’s unauthenticated on LAN).
- Full streaming-service parity (Spotify search/control requires separate OAuth + Web API; supported as an optional “bonus”).
- Repo hygiene:
gofmt/golangci-lint/CI (.github/workflows/ci.yml) - Discovery: mDNS types
musc/musp/musz/mush+ cache (blu devices) - Device selection precedence + aliases +
BLU_DEVICE - Core playback:
play/pause/stop/next/prev - Shuffle + repeat
- Volume: get/set/up/down + mute on/off/toggle
- Grouping:
group status|add|remove - Watch: long-poll status/syncstatus
- Queue: list/clear/delete/move/save (+ optional add)
- Presets: list + load
- Browse/search:
browse,playlists,radiobrowse(inputs) - Sleep timer
- Diagnostics:
diag,doctor - Power-user:
rawendpoint runner +--dry-run - Optional: Spotify Web API integration (
blu spotify …) for search + “play ” - Compare against BluShell/pyblu/BluOS Controller.app notes in spec
- Real-device verification (no-breakage, minimal disruption)
Observed in /Applications/BluOS Controller.app (Electron):
- Browse
_musc._tcp,_musp._tcp,_musz._tcp,_mush._tcponlocal. - Use first IPv4 + advertised port as device endpoint.
- TXT record contains
version=...(when present).
- Default port:
11000 - Requests: mostly
GET - Responses: XML
Endpoints used by blu:
- Status:
GET /Status(optional long poll:?timeout=seconds&etag=etag) - Group status:
GET /SyncStatus(optional long poll:?timeout=seconds&etag=etag) - Playback:
GET /Play(+seek,id,url)GET /Pause(+toggle=1)GET /StopGET /SkipGET /BackGET /Shuffle?state=<0|1>GET /Repeat?state=<0|1|2>
- Volume:
GET /Volume?level=<0..100>&tell_slaves=1GET /Volume?db=<delta_db>&tell_slaves=1(typical ±2)GET /Volume?mute=<0|1>&tell_slaves=1
- Grouping:
GET /AddSlave?slave=<ip>&port=<port>[&group=<name>]GET /RemoveSlave?slave=<ip>&port=<port>GET /Playlist[?start=<n>&end=<n>]GET /ClearGET /Delete?id=<n>GET /Move?old=<n>&new=<n>GET /Save?name=<playlist_name>GET /PresetsGET /Preset?id=<n>GET /Playlists[?service=<name>&category=<...>&expr=<...>]GET /Browse?key=<key>[&q=<query>][&withContextMenuItems=1]GET /RadioBrowse?service=CaptureGET /Sleep
--device <id|name|alias>:host[:port], discovery name, or alias from config.--json: JSON output (stable for scripting).--timeout <dur>: HTTP timeout.--dry-run: block mutating endpoints (still allows reads); use for safe verification.--trace-http: printhttp: GET …for each request.--discover/--discover=false: allow discovery fallback.--discover-timeout <dur>: discovery window.--config <path>: optional config override.
--deviceBLU_DEVICE- config
default_device - discovery cache (only if exactly 1 device)
- live discovery (only if exactly 1 device)
If multiple devices: error + ask user to pick --device.
blu completions bash|zshblu versionblu devices: discover + print devices; refreshes cache.blu status: current player status.blu now: condensed now-playing (alias forstatushuman output)blu watch status|sync: long-poll and print changesblu play|pause|stop|next|prev: playback control.blu shuffle on|offblu repeat off|track|queueblu volume get|set <0-100>|up|downblu mute on|off|toggleblu group status|add <slave> [--name <group>]|remove <slave>blu queue list|clear|delete <id>|move <old> <new>|save <name>blu presets list|load <id>blu browse --key <key> [--q <query>] [--context]blu playlists [--service <name>] [--category <cat>] [--expr <search>]blu inputs(akaradiobrowse Capture)blu tunein search|play [--pick <n>] [--id <id>] <query>blu spotify login|logout|open|devices|search|playblu sleep(cycles sleep timer)blu diag/blu doctorblu raw <path> [--param k=v ...] [--write](power tool;--writeblocked by--dry-run)
Path: $(userConfigDir)/blu/config.json (macOS: ~/Library/Application Support/blu/config.json)
Schema:
{
"default_device": "192.168.1.100:11000",
"aliases": {
"kitchen": "192.168.1.100:11000",
"office": "192.168.1.120:11000"
}
}Path: $(userCacheDir)/blu/discovery.json
Contains updated_at + last discovered devices (id/host/port).
cmd/blu: entrypointinternal/app: CLI parsing + command routing (testableRun)internal/bluos: HTTP client + XML models (typed; ignores unknown attrs)internal/discovery: mDNS discovery (zeroconf)internal/config: config + cache + device parsinginternal/output: printer (human + JSON)
internal/bluos: httptest server asserts request URLs; parses XML fixtures.internal/app: run commands viaRun(ctx, args, stdout, stderr); assert output/exit codes.internal/config: config and device parsing tests.internal/discovery: unit tests for TXT parsing + entry conversion (no network).
go test ./...(+ race on linux)golangci-lint(includes gofmt/goimports/gofumpt checks)govulncheck ./...- build matrix: linux/mac/windows (compile sanity)
- Same core endpoints for playback/volume/group/queue.
- Handy XML examples + parameter naming conventions.
- Same core endpoints (
/Status,/SyncStatus,/Volume,/Play…). - Good reference for queue XML parsing +
/RadioBrowse?service=Capture“inputs”.
- Confirms discovery details:
- mDNS browse types
_musc._tcp,_musp._tcp,_musz._tcp,_mush._tcp - LSDP UDP broadcast query on port
11430(useful fallback when mDNS is flaky).
- mDNS browse types
- Device discovery: works on Peter LAN (LSDP + mDNS merge).
- Verified end-to-end against real players on Peter LAN (status/group/queue/presets/browse/inputs/diag/watch + dry-run tracing for mutating calls).