macOS CLI that controls Apple Music playback and routes audio to HomePods.
Homebrew (recommended):
brew tap agisilaos/tap
brew install homepodctlFrom source:
make build
./homepodctl --help- macOS with the Music app
osascript(built-in)shortcuts(built-in, optional for thenativebackend)- Go toolchain to build (if building from source)
On first use, macOS may prompt you to allow your terminal (or the built binary) to control:
- Music (via Apple Events)
- Shortcuts (if you use the
nativebackend)
--backend airplay: selects Music.app AirPlay output device(s) and plays a playlist (the Mac is the sender).--backend native: runs a Shortcuts automation you map inconfig.json(can be set up so HomePod plays natively).
- “Rooms” = AirPlay device names as seen by Music.app (HomePods, Apple TVs, speakers, etc).
out setchanges Music.app’s current AirPlay outputs (it does not edit your config).playplays a Music.app user playlist (by fuzzy search or by ID).config.jsonis only for defaults and aliases (so you don’t have to type--roomevery time).
Bootstrap local config + diagnostics:
homepodctl setup --room "Bedroom"List available AirPlay outputs (these names are what you pass as “rooms”):
homepodctl devicesPick outputs to play through (sets Music.app’s current outputs):
homepodctl out set --room "Bedroom"Play a playlist by fuzzy query:
homepodctl play chillIf the playlist name has spaces, quote it:
homepodctl play "Songs I've been obsessed recently pt. 2"If multiple playlists match, auto-picks the best match; to pick interactively:
homepodctl play autumn --chooseSee status (playback + outputs/route + backend connectivity/auth):
homepodctl statusShortcut for status:
homepodctl nowWatch changes:
homepodctl status --watch 1sSearch playlists (for IDs / debugging):
homepodctl playlists --query chillIf a playlist name is ambiguous or tricky to match (emoji/whitespace), use IDs:
homepodctl playlists --query autumn
homepodctl play --playlist-id <PERSISTENT_ID>Set volume (if rooms are omitted, uses defaults.rooms; if that’s empty, uses the currently selected outputs in Music.app):
homepodctl vol 50
homepodctl volume 35 "Living Room"Create a starter config:
homepodctl config-initThis writes config.json under your macOS user config dir (typically ~/Library/Application Support/homepodctl/config.json).
Defaults are used when flags are omitted. For example, if you set:
defaults.backend = "airplay"defaults.rooms = ["Bedroom"]
…then you can just run:
homepodctl play chillList configured aliases:
homepodctl aliasesRun an alias from your config:
homepodctl run bed-exampleEdit config.json, map room -> playlist -> shortcut name, and run:
homepodctl play --backend native --room "Bedroom" --playlist "Example Playlist"CLI help:
homepodctl --help
homepodctl --version
homepodctl --verbose status
homepodctl --quiet out set --room "Bedroom" --dry-run
homepodctl help playVerbose diagnostics can also be enabled via HOMEPODCTL_VERBOSE=1.
Run built-in diagnostics:
homepodctl doctor
homepodctl doctor --jsonGenerate shell completions:
homepodctl completion zsh
homepodctl completion bash
homepodctl completion fishInstall shell completions:
# auto-target default path for shell
homepodctl completion install zsh
homepodctl completion install bash
homepodctl completion install fishManual load snippets:
# zsh (~/.zshrc)
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit
# bash (~/.bashrc)
source ~/.local/share/bash-completion/completions/homepodctl
# fish
# file is auto-loaded from ~/.config/fish/completions/homepodctl.fishInspect and update config values:
homepodctl config validate --json
homepodctl config get defaults.backend
homepodctl config set defaults.backend airplay
homepodctl config set defaults.rooms "Bedroom" "Living Room"Dry-run mutating commands without side effects:
homepodctl play chill --dry-run --json
homepodctl out set --room "Bedroom" --dry-run --json
homepodctl volume 30 --dry-run --json
homepodctl run bed --dry-run --json0: success2: usage/flag/validation error3: config or automation validation error4: backend command error (osascript/shortcuts)1: other runtime failures
homepodctl devices/homepodctl out list: list AirPlay deviceshomepodctl out set --room <name> ... [--json|--plain|--dry-run]: select Music.app outputshomepodctl play <query> [--json|--plain|--dry-run]/homepodctl play --playlist-id <id>: play a playlisthomepodctl playlists --query <text> [--json|--plain]: search playlistshomepodctl status [--json|--plain]/homepodctl now/homepodctl status --watch 1s: playback, route, and connectivity statushomepodctl pause|stop|next|prev [--json|--plain]: transport controlshomepodctl volume <0-100> [room ...] [--json|--plain|--dry-run]/homepodctl vol ...: output volumehomepodctl aliases [--json|--plain]/homepodctl run <alias> [--json|--plain|--dry-run]: config shortcutshomepodctl native-run --shortcut <name> [--json|--dry-run]: run a Shortcut directlyhomepodctl config validate|get|set ...: validate and edit config values (defaults.*)homepodctl config-init: create starter confighomepodctl setup [--backend ...] [--room ...]: bootstrap config + diagnostics + device discoveryhomepodctl doctor: diagnostics checklisthomepodctl completion <bash|zsh|fish>: generate completion scripthomepodctl plan <command> ...: preview resolved dry-run execution for core actionshomepodctl schema [<name>] [--json]: inspect JSON output contractshomepodctl automation validate|plan|run|init ...: routine workflows (non-interactive by default; add--dry-runto preview)homepodctl version: version info
- You built it but it still behaves “old”: if you run
make build, the binary is./homepodctl. Runninghomepodctl ...might be a different binary on your PATH. - Rooms are not flags: use
--room "Bedroom"(repeatable), not--bedroom/--Bedroom. out setdoesn’t edit config: it only changes Music.app’s current outputs. Useconfig-init+ editdefaults.roomsif you want persistent defaults.
This tool is macOS-only (it relies on osascript + Music.app, and optionally shortcuts).
- Release preflight (recommended):
make release-check VERSION=vX.Y.Zvalidates changelog/test/vet/docs and produces a version-stamped local binary. - Release dry run:
make release-dry-run VERSION=vX.Y.Zbuilds release artifacts only (no changelog/tag/push/release/tap writes). - Prebuilt binaries:
make release VERSION=vX.Y.Zpublishes a GitHub Release and updates the Homebrew formula inagisilaos/homebrew-tap. - Release scripts:
scripts/release-check.shandscripts/release.sh go install(after publishing):go install github.com/agisilaos/homepodctl/cmd/homepodctl@latest
Automation commands are being designed for routine playback flows and agent usage.
Design docs:
- CLI spec:
docs/automation-v1-cli-spec.md - User quickstart:
docs/automation/quickstart-user.md - Agent quickstart:
docs/automation/quickstart-agent.md - Troubleshooting matrix:
docs/automation/troubleshooting.md - Preset templates:
docs/automation/presets/
Canonical presets included:
docs/automation/presets/morning.yamldocs/automation/presets/focus.yamldocs/automation/presets/winddown.yamldocs/automation/presets/party.yamldocs/automation/presets/reset.yaml
This project is not affiliated with Apple.
