Programmable home audio.
sonosctl is a terminal-first, local-first CLI for controlling Sonos speakers and Spotify from scripts, cron jobs, shortcuts, and AI agents.
It is built for people who want home audio to be automatable, composable, and easy to integrate into local workflows.
sonosctlis an independent open source project by Andres Lee. It is not affiliated with, endorsed by, or sponsored by Sonos or Spotify.
Command the vibe.
$ sonosctl devices
Living Room | 192.168.68.110 | Sonos One
Office | 192.168.68.106 | Sonos One
$ sonosctl play "nujabes feather" --speaker "Living Room"
Now playing on Living Room: Feather - Nujabes
$ sonosctl status --speaker "Living Room"
Speaker: Living Room
State: PLAYING
Track: Feather
Artist: Nujabes
Album: Modal Soul
Position: 0:01:23 / 0:04:47
Volume: 25
Source: x-sonos-spotify:spotify%3atrack%3a...
$ sonosctl queue add "khruangbin evan finds the third room" --speaker "Living Room"
Added to queue on Living Room: Evan Finds the Third Room - Khruangbin (position 12)
$ sonosctl group --coordinator "Living Room" --members "Office"
Grouped with coordinator Living Room: OfficeMost Sonos control flows are built for apps and taps.
sonosctl is built for automation.
Use it when you want to:
- start music from a shell script
- trigger playback from cron
- wire Sonos into Raycast, Shortcuts, Stream Deck, or Alfred
- expose local speaker control to AI agents
- inspect playback state as JSON
- manage speakers from a terminal without opening the Sonos app
- Discover Sonos speakers on your LAN
- Play tracks and playlists from Spotify through Sonos
- Read playback state, queue contents, and group topology
- Persist recently observed playback history for automation
- Group and ungroup rooms
- Toggle shuffle, repeat, and crossfade
- Output JSON on automation-oriented commands
- Run locally with no Spotify developer API keys
pipx install sonosctlOr with pip:
pip install sonosctlFrom source:
git clone https://github.com/andresleecom/sonosctl.git
cd sonosctl
python -m venv .venv
source .venv/bin/activate
pip install -e .sonosctl devicesSpotify auth is usually a one-time setup per Sonos household.
sonosctl auth-spotify --speaker "Living Room"Open the link, approve access, then press Enter to complete linking.
sonosctl play "bohemian rhapsody" --speaker "Living Room"sonosctl status --speaker "Living Room" --json | jq '.track'
sonosctl queue --speaker "Living Room" --json | jq '.items[].title'
sonosctl history --speaker "Living Room" --json | jq '.entries[-5:]'Playback history is observation-based. New entries are written when you call status, or continuously if you keep monitor running.
Run a lightweight monitor to keep ~/.sonosctl/history.jsonl updated automatically:
sonosctl monitor --speaker "Living Room" --interval 15# Morning playlist at 8 AM
0 8 * * * sonosctl play-playlist "Morning Chill" --speaker "Living Room" --shuffle
# Lower volume at 10 PM
0 22 * * * sonosctl volume 10 --speaker "Living Room"#!/usr/bin/env bash
set -euo pipefail
current_track="$(sonosctl status --speaker "Living Room" --json | jq -r '.track')"
echo "Now playing: $current_track"sonosctl works well as a thin control layer for:
- local AI agents
- Raycast scripts
- Stream Deck actions
- Apple Shortcuts
- shell aliases and desktop automations
See docs/AI_AGENT_INTEGRATION.md.
sonosctl can keep a lightweight playback history for agents and automations.
sonosctl history --speaker "Living Room"
sonosctl history --speaker "Living Room" --json
sonosctl monitor --speaker "Living Room" --interval 15Use this when you want an agent to avoid repeating the same tracks or artists too often.
A useful agent loop is:
sonosctl status --speaker "Living Room" --json
sonosctl queue --speaker "Living Room" --json
sonosctl history --speaker "Living Room" --jsonThen decide whether to:
- do nothing if the current playback is healthy
queue addif you want to introduce variety without interruptingplay-playlistorplayonly when the room is idle or the automation truly owns the mood
| Command | Description |
|---|---|
play <query> |
Search and play the first Spotify track match |
play-playlist <name-or-id> |
Play a Spotify playlist by name or item ID |
pause |
Pause playback |
resume |
Resume playback |
next |
Skip to next track |
prev |
Go to previous track |
volume [level] |
Get or set volume (0-100) |
status |
Show current playback state and metadata |
| Command | Description |
|---|---|
queue |
Show playback queue |
queue add <query> |
Add a Spotify track to the queue |
queue clear |
Clear the queue |
| Command | Description |
|---|---|
history |
Show recently observed playback history |
monitor |
Continuously observe playback and append history |
| Command | Description |
|---|---|
search <query> |
Search Spotify tracks |
playlists [query] |
List or filter playlists |
playlist-info <name-or-id> |
Show playlist metadata and tracks |
favorites [all|playlists|tracks] [query] |
List Sonos favorites |
| Command | Description |
|---|---|
shuffle [on|off] |
Show or set shuffle mode |
repeat [off|one|all] |
Show or set repeat mode |
crossfade [on|off] |
Show or set crossfade mode |
| Command | Description |
|---|---|
devices |
Discover speakers on your network |
group |
Group speakers under a coordinator |
ungroup |
Remove a speaker from its group |
groups |
Show current Sonos group topology |
doctor status |
Analyze captured status --json --raw diagnostics |
auth-spotify |
Run Spotify linking flow through Sonos |
Run sonosctl <command> --help for details.
Create ~/.sonosctl/config.toml to set defaults:
[defaults]
speaker = "Living Room"
timeout = 5
search_limit = 8
json = trueWith a default speaker configured, you can omit --speaker from commands that target a specific device.
These commands support --json for automation workflows:
devicessearchplaylistsfavoritesplaylist-infoshufflerepeatcrossfadequeuehistorystatusgroups
Example:
sonosctl status --speaker "Living Room" --json
sonosctl devices --json
sonosctl history --speaker "Living Room" --jsonAuthTokenExpired
Re-run:
sonosctl auth-spotify --speaker "Living Room"No speakers found
Verify the speaker is online, on the same LAN/VLAN, and that a VPN is not interfering.
Try:
sonosctl devices --timeout 15Auth link not working
Run auth-spotify again and use the new code immediately.
Group or ungroup looks stuck
Verify actual topology and use wait confirmation:
sonosctl group --coordinator "Living Room" --members "Office" --wait 3
sonosctl ungroup --speaker "Office" --wait 3
sonosctl groupsstatus returns Unknown intermittently
Capture raw payloads and run diagnostics:
for i in $(seq 1 60); do
python -m sonosctl status --speaker "Living Room" --json --raw
sleep 2
done > status-debug.jsonl
python -m sonosctl doctor status --input status-debug.jsonl --samples 10- Python 3.10+
- Sonos speakers on the same local network
- Spotify linked in the Sonos app
- CONTRIBUTING.md
- CODE_OF_CONDUCT.md
- SECURITY.md
- CHANGELOG.md
- docs/ARCHITECTURE.md
- docs/AI_AGENT_INTEGRATION.md
- docs/HARDENING_CHECKLIST.md
sonosctl is completely independent from Sonos and Spotify.
All product and company names, including Sonos and Spotify, are trademarks of their respective owners.
MIT. See LICENSE.
