{"id":34377186,"url":"https://github.com/steipete/sonoscli","last_synced_at":"2026-01-11T21:59:32.583Z","repository":{"id":328563539,"uuid":"1115489431","full_name":"steipete/sonoscli","owner":"steipete","description":"Control SONOS speakers from your terminal.","archived":false,"fork":false,"pushed_at":"2026-01-04T05:25:49.000Z","size":287,"stargazers_count":40,"open_issues_count":1,"forks_count":5,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-06T15:06:31.489Z","etag":null,"topics":["cli","go","sonos"],"latest_commit_sha":null,"homepage":"https://sonoscli.sh","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/steipete.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-13T00:37:32.000Z","updated_at":"2026-01-05T21:48:30.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/steipete/sonoscli","commit_stats":null,"previous_names":["steipete/sonoscli"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/steipete/sonoscli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fsonoscli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fsonoscli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fsonoscli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fsonoscli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/steipete","download_url":"https://codeload.github.com/steipete/sonoscli/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/steipete%2Fsonoscli/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28324764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T18:42:50.174Z","status":"ssl_error","status_checked_at":"2026-01-11T18:39:13.842Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","go","sonos"],"created_at":"2025-12-18T13:00:41.945Z","updated_at":"2026-01-11T21:59:32.578Z","avatar_url":"https://github.com/steipete.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# 🔊 sonoscli — Discover, group, and control Sonos\n\n`sonoscli` is a modern Go CLI to control Sonos speakers over your local network (UPnP/SOAP).\n\n## Features\n\n- **Reliable discovery**: SSDP + topology (`ZoneGroupTopology.GetZoneGroupState`) with subnet scan fallback.\n- **Coordinator-aware control**: target any room; commands go to the group coordinator automatically.\n- **Playback controls**: play/pause/stop/next/prev, plus `play-uri`, `linein`, and `tv`.\n- **Grouping**: inspect groups, join/unjoin, party mode, dissolve groups, and **solo** a room.\n- **Queue**: list/play/remove/clear queue entries.\n- **Favorites**: list and play Sonos Favorites by index or title.\n- **Scenes**: save/apply presets (grouping + per-room volume/mute).\n- **Spotify**:\n  - Enqueue/play Spotify share links or canonical `spotify:\u003ctype\u003e:\u003cid\u003e` URIs (no Spotify credentials required).\n  - Search Spotify via **SMAPI** (Sonos Music API; uses your linked service in Sonos).\n  - Optional Spotify Web API search (client credentials) if you want it.\n- **Live events**: `watch` subscribes to AVTransport + RenderingControl and prints changes.\n- **Scriptable output**: `--format plain|json|tsv` plus `--debug` tracing.\n\nThis is not an official Sonos project.\n\n## Requirements\n\n- Your machine must be on the same network as your Sonos system.\n- Speakers must be reachable on TCP port `1400` (e.g. `http://\u003cspeaker-ip\u003e:1400/`).\n\nSpotify:\n- Spotify must already be linked in the Sonos app.\n- This CLI does not authenticate with Spotify; it enqueues Sonos “Spotify” URIs/metadata.\n\nSpotify search (recommended, no Spotify Web API credentials):\n- `sonos smapi search` uses Sonos SMAPI to search linked services (e.g. Spotify).\n- Some services require a one-time DeviceLink/AppLink flow: `sonos auth smapi begin|complete`.\n\nSpotify search via Spotify Web API (optional):\n- If you want `sonos search spotify`, you’ll need a Spotify Web API app (client credentials).\n  Set `SPOTIFY_CLIENT_ID` and `SPOTIFY_CLIENT_SECRET` (or pass `--client-id/--client-secret`).\n\n## Install / build\n\nInstall (Homebrew, single line):\n\n```bash\nbrew install steipete/tap/sonoscli\n```\n\nThis installs the `sonos` binary.\n\nUpgrade later:\n\n```bash\nbrew upgrade steipete/tap/sonoscli\n```\n\nInstall from source (Go):\n\n```bash\ngo install github.com/steipete/sonoscli/cmd/sonos@latest\nsonos --version\n```\n\nBuild a local binary:\n\n```bash\ngo build -o sonos ./cmd/sonos\n./sonos --version\n./sonos --help\n```\n\n## Quick start\n\nNote: if you installed via Homebrew or `go install`, replace `./sonos` with `sonos`.\n\nDiscover speakers:\n\n```bash\n./sonos discover\n./sonos discover --format json\n./sonos discover --all # include invisible/bonded devices (advanced)\n```\n\nShow status (text or JSON):\n\n```bash\n./sonos status --name \"Kitchen\"\n./sonos now --name \"Kitchen\"\n./sonos status --name \"Kitchen\" --format json\n```\n\nPlayback:\n\n```bash\n./sonos play --name \"Kitchen\"\n./sonos pause --name \"Kitchen\"\n./sonos stop --name \"Kitchen\"\n./sonos next --name \"Kitchen\"\n./sonos prev --name \"Kitchen\"\n```\n\nWatch live events (track/volume changes):\n\n```bash\n./sonos watch --name \"Kitchen\"\n./sonos watch --name \"Kitchen\" --format json\n./sonos watch --name \"Kitchen\" --format tsv\n```\n\nNote: this starts a local callback server for UPnP events; your OS firewall may prompt to allow incoming connections.\n\n## Command overview\n\nRun `sonos --help` for the full list. Most commonly used:\n\n- Discovery \u0026 status: `discover`, `status`/`now`, `watch`\n- Playback: `play`, `pause`, `stop`, `next`, `prev`, `open`, `enqueue`, `play-uri`, `linein`, `tv`\n- Grouping: `group status`, `group join`, `group unjoin`, `group solo`, `group party`, `group dissolve`\n- Queue: `queue list`, `queue play`, `queue remove`, `queue clear`\n- Favorites: `favorites list`, `favorites open`\n- Scenes: `scene save`, `scene apply`, `scene list`, `scene delete`\n- Spotify search: `smapi search` (recommended), optional `search spotify` (Spotify Web API)\n\n## Queue\n\nList the queue:\n\n```bash\n./sonos queue list --name \"Kitchen\"\n./sonos queue list --name \"Kitchen\" --format json\n```\n\nPlay or remove a queue entry (positions are 1-based):\n\n```bash\n./sonos queue play --name \"Kitchen\" 1\n./sonos queue remove --name \"Kitchen\" 3\n```\n\nClear the queue:\n\n```bash\n./sonos queue clear --name \"Kitchen\"\n```\n\n## Scenes (presets)\n\nSave a scene (grouping + per-room volume/mute):\n\n```bash\n./sonos scene save \"Evening\"\n```\n\nApply a scene later:\n\n```bash\n./sonos scene apply \"Evening\"\n```\n\nList / delete scenes:\n\n```bash\n./sonos scene list\n./sonos scene delete \"Evening\"\n```\n\nScenes are stored in your user config dir as `sonoscli/scenes.json` (e.g. `~/.config/sonoscli/scenes.json` on macOS/Linux).\n\n## Favorites\n\nList Sonos Favorites:\n\n```bash\n./sonos favorites list --name \"Kitchen\"\n./sonos favorites list --name \"Kitchen\" --format json\n```\n\nPlay by index (from the list):\n\n```bash\n./sonos favorites open --name \"Kitchen\" --index 1\n```\n\nOr play by title (case-insensitive exact match):\n\n```bash\n./sonos favorites open --name \"Kitchen\" \"BBC Radio 6 Music\"\n```\n\n## Other sources\n\nPlay an arbitrary URI:\n\n```bash\n./sonos play-uri --name \"Kitchen\" \"https://example.com/stream.mp3\"\n```\n\nForce radio-style playback (useful for station-like streams):\n\n```bash\n./sonos play-uri --name \"Kitchen\" --radio --title \"My Stream\" \"https://example.com/live.mp3\"\n```\n\nSwitch to line-in (optionally from another speaker):\n\n```bash\n./sonos linein --name \"Kitchen\" --from \"Living Room\"\n```\n\nSwitch to TV input (soundbar):\n\n```bash\n./sonos tv --name \"Living Room\"\n```\n\n## Grouping\n\nShow current groups:\n\n```bash\n./sonos group status\n./sonos group status --all # include invisible/bonded devices (advanced)\n```\n\nJoin `Bedroom` into `Living Room`’s group:\n\n```bash\n./sonos group join --name \"Bedroom\" --to \"Living Room\"\n```\n\nRoom targeting supports fuzzy substring matching (and will suggest matches on ambiguity):\n\n```bash\n./sonos group join --name \"Off\" --to \"Bar\"     # \"Office\" joins \"Bar\"\n./sonos group join --name \"Bed\" --to \"Liv\"     # \"Bedroom\" joins \"Living Room\"\n```\n\nUngroup a speaker (make it standalone):\n\n```bash\n./sonos group unjoin --name \"Bedroom\"\n```\n\nSolo a speaker (ungroup its current group so it plays alone):\n\n```bash\n./sonos group solo --name \"Office\"\n```\n\nParty mode (join all visible speakers to a target group):\n\n```bash\n./sonos group party --to \"Bar\"\n```\n\nDissolve a group (ungroup all members of the group):\n\n```bash\n./sonos group dissolve --name \"Living Room\"\n```\n\nUngroup Office and play on Office only:\n\n```bash\n./sonos group solo --name \"Office\"\n./sonos open --name \"Office\" \"https://open.spotify.com/album/\u003cid\u003e\"\n```\n\nRe-join a speaker back into another group:\n\n```bash\n./sonos group join --name \"Office\" --to \"Bar\"\n```\n\nGroup volume / mute (affects the whole group):\n\n```bash\n./sonos group volume get --name \"Living Room\"\n./sonos group volume set --name \"Living Room\" 25\n\n./sonos group mute get --name \"Living Room\"\n./sonos group mute toggle --name \"Living Room\"\n```\n\nVolume / mute:\n\n```bash\n./sonos volume get --name \"Kitchen\"\n./sonos volume set --name \"Kitchen\" 25\n\n./sonos mute get --name \"Kitchen\"\n./sonos mute toggle --name \"Kitchen\"\n```\n\n## Targeting and groups\n\nTarget a speaker by:\n- `--name \"Kitchen\"` (Sonos room name)\n- `--ip 192.168.0.250` (speaker IP)\n\nMost commands must be sent to the *group coordinator* (the device that owns transport state for the group). `sonoscli` resolves the coordinator automatically so commands behave like the Sonos app.\n\n## Spotify\n\nSearch via Sonos (SMAPI; no Spotify Web API credentials):\n\n```bash\n./sonos smapi services\n./sonos smapi categories --service \"Spotify\"\n./sonos smapi browse --service \"Spotify\" --id root\n./sonos auth smapi begin --service \"Spotify\"\n# open the printed URL in a browser, link your account, then:\n./sonos auth smapi complete --service \"Spotify\" --code \u003clinkCode\u003e --wait 5m\n\n./sonos smapi search --service \"Spotify\" --category tracks \"gareth emery\"\n./sonos smapi search --service \"Spotify\" --category tracks --open --name \"Office\" \"gareth emery\"\n```\n\nPlay from a search query (shortcut for SMAPI search + open):\n\n```bash\n./sonos play spotify --name \"Office\" \"gareth emery\"\n./sonos play spotify --name \"Office\" --category albums \"gareth emery\"\n```\n\nSMAPI tokens are stored under your user config dir as `sonoscli/smapi_tokens.json` (e.g. `~/.config/sonoscli/smapi_tokens.json` on macOS/Linux).\n\nSearch via Spotify Web API (prints playable URIs):\n\n```bash\nexport SPOTIFY_CLIENT_ID=\"...\"\nexport SPOTIFY_CLIENT_SECRET=\"...\"\n\n./sonos search spotify --type track --limit 5 \"daft punk harder better\"\n./sonos search spotify --type playlist \"focus\"\n```\n\nOpen the first search result directly on Sonos:\n\n```bash\n./sonos search spotify --open --name \"Kitchen\" \"miles davis so what\"\n```\n\nEnqueue + play:\n\n```bash\n./sonos open --name \"Kitchen\" spotify:track:6NmXV4o6bmp704aPGyTVVG\n./sonos open --name \"Kitchen\" https://open.spotify.com/track/6NmXV4o6bmp704aPGyTVVG\n```\n\nEnqueue only:\n\n```bash\n./sonos enqueue --name \"Kitchen\" spotify:playlist:37i9dQZF1DXcBWIGoYBM5M\n```\n\nNotes:\n- The enqueue implementation tries Spotify Sonos service numbers `2311` and `3079` for compatibility.\n- Use `--title` to override the queue display title for some entries.\n\n## Dev workflow\n\n### Makefile\n\n```bash\nmake fmt\nmake test\nmake build\nmake lint\n```\n\n`make lint` requires `golangci-lint`:\n\n```bash\nbrew install golangci-lint\n# or:\ngo install github.com/golangci/golangci-lint/cmd/golangci-lint@latest\n```\n\n### pnpm helper scripts\n\nThis repo includes a minimal `package.json` so you can drive the workflow with `pnpm` (no Node dependencies required):\n\n```bash\npnpm build\npnpm test\npnpm format\npnpm lint\n\npnpm sonos -- discover\npnpm sonos -- status --name \"Kitchen\"\npnpm sonos -- open --name \"Kitchen\" spotify:track:6NmXV4o6bmp704aPGyTVVG\npnpm sonos -- search spotify \"miles davis so what\"\npnpm sonos -- group status\npnpm sonos -- queue list --name \"Kitchen\"\n```\n\nCI runs: `gofmt` check, `go vet`, `go test`, and `golangci-lint`.\n\n## Global flags\n\n- `--ip \u003cip\u003e`: target by IP\n- `--name \u003cname\u003e`: target by speaker name (defaults to `sonos config defaultRoom` if set)\n- `--timeout \u003cduration\u003e`: discovery/network timeout (default `5s`)\n- `--format plain|json|tsv`: output format (defaults to `sonos config format` if set)\n- `--json`: deprecated alias for `--format json`\n- `--debug`: enable detailed trace logs (SSDP/topology/SOAP timings)\n\n## Config (defaults)\n\nPersist small local defaults:\n\n```bash\n./sonos config get\n./sonos config set defaultRoom \"Office\"\n./sonos config set format json\n./sonos config unset defaultRoom\n```\n\n## Troubleshooting\n\n- `discover` is empty:\n  - Some networks block multicast/SSDP; `sonoscli` falls back to scanning local /24 subnets for port `1400` and then uses Sonos topology to list all rooms.\n  - Ensure Wi‑Fi client isolation is off and you’re on the same LAN/subnet.\n- Discovery is slow or flaky:\n  - Run `sonos --debug discover` to see whether SSDP multicast is timing out and whether topology calls are slow.\n- Discovery / SOAP calls hang or time out on your network:\n  - `sonoscli` retries local Sonos HTTP/SOAP calls via `curl` as a workaround for some network/firmware quirks.\n- Commands fail with UPnP/SOAP errors:\n  - Verify you can reach `http://\u003cspeaker-ip\u003e:1400/` from this machine.\n  - Try targeting by `--name` (it resolves the coordinator).\n- Spotify enqueue fails:\n  - Confirm Spotify is linked and playable in the Sonos app.\n  - Some systems behave differently per firmware/service configuration.\n\n## Inspiration / references\n\nThis project was informed by the Sonos control ecosystem and the SoCo Python library:\n\n```text\nhttps://github.com/SoCo/SoCo\n```\n\n## Design doc\n\nSee [`docs/spec.md`](docs/spec.md).\n\n## License\n\nMIT License. See [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteipete%2Fsonoscli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsteipete%2Fsonoscli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsteipete%2Fsonoscli/lists"}