ZoneFoundry Local Sonos
Use this skill when an agent, local bot, or automation wants to control Sonos through the local zf CLI.
This skill is written English-first so global users can scan it quickly. Keep user-facing replies in the user's language. Chinese examples and China-specific readiness notes are included where they help.
Use this skill when
- the user wants to connect Sonos for the first time
- the user wants to check whether local Sonos control is ready
- the user wants to play, pause, skip, change volume, or inspect status
- the user wants to add songs to the current queue without interrupting playback
- the user wants to inspect or recover queue / transport problems
- the user wants to check music-service readiness or continue a pending local link flow
Do not use this skill for
- Sonos account creation or billing flows
- cloud relay, hosted bot subscription, or multi-tenant product logic
- arbitrary chat unrelated to Sonos control
Core model
Treat zf as the execution layer.
bot / agent / web onboarding -> zf -> Sonos
- the bot or agent translates intent and explains results
zf handles discovery, playback, queueing, diagnostics, readiness checks, and recovery
Alternative control methods
In addition to agent / CLI usage, ZoneFoundry supports a Telegram Bot as an alternative control surface. Users can send voice or text messages to the bot for hands-free Sonos control from their phone.
Hard rules (MUST follow)
-
Always obey nextCommand: If zf setup --format json returns a nextCommand field, execute that command immediately.
-
Always obey nextAction: If zf service list --format json returns a nextAction field, follow it. If nextAction=ready, proceed to play.
-
Do not block playback on service linking status. QQ Music, Apple Music, and most services work without separate CLI-side linking — users bind services through the Sonos mobile app. If state is unclear, try playing directly first.
-
Do not expose internal implementation details, premium-only routing, or speculative workarounds in public-facing explanations. Describe user-visible outcomes and the next safe action instead.
-
Keep update paths separate:
clawhub update zonefoundry-local-sonos # refresh this skill
zf update self --check --format json # check local runtime update
Language rule
- keep user-facing replies in the user's language
- keep literal room names and service names exactly as the user sees them on Sonos
- use English examples by default, then add Chinese examples only when they improve clarity
First-run quickstart
When the user mentions Sonos for the first time, do not immediately say "not configured".
At the start of every new session, check runtime updates first:
zf update self --check --format json
If it returns status=update_available, update before doing deeper work:
zf update self --format json
Then run the one-shot readiness flow:
zf setup --format json
zf setup should be the default first move because it checks:
- speaker discovery
- default room
- service list and local readiness state
- default service
- a final summary with suggested next actions
If zf setup is unavailable on an older runtime, do this fallback preflight:
zf doctor --format json
zf discover --format json
zf config get defaultRoom
zf service list --format json
zf config get defaultService
If rooms are found but there is no default room, ask the user to choose exactly one visible room and then set it once:
zf config set defaultRoom "Office"
If there is no default service, ask once and set it:
zf config set defaultService "Spotify"
Environment gate
Before promising persistent local bot control, confirm there is an always-on device in the same LAN as Sonos.
Valid local nodes:
- Mac or Windows PC
- NAS
- mini PC
- Raspberry Pi
- Docker host
- Home Assistant host
If the user only has a phone:
- explain that Sonos itself can still be used normally
- guide service add/login through the official Sonos iOS / Android app
- treat Sonos Web App as a supplementary control surface, not the main onboarding path
- do not promise persistent local bot control or always-on automation
Short rule:
- Sonos itself does not require a desktop app
- persistent ZoneFoundry agent or bot flows do require an always-on local node
Minimum safe commands
Prefer JSON output by default.
zf setup --format json
zf doctor --format json
zf discover --format json
zf status --name "<room>" --format json
zf queue list --name "<room>" --format json
If the user already specified a room, prefer --name "<room>".
Safe command mapping
For user-facing explanations, prefer direct CLI command names.
Examples:
zf status --name "Office" --format json
zf pause --name "Office"
zf next --name "Office"
zf volume set 20 --name "Office"
Playback rules
Prefer the unified play music command for normal playback.
Common service examples:
zf play music "Miles Davis" --format json
zf play music "Taylor Swift" --service Spotify --format json
zf play music "Adele" --service "Apple Music" --format json
zf play music "黎明 夏日傾情" --service "Apple Music" --format json
zf play music "黎明" --enqueue --service "Apple Music" --limit 5 --format json
zf queue list --name "Office" --format json
zf queue remove 3 --name "Office"
zf say "Dinner is ready" --name "Kitchen" --mode queue-insert --format json
Chinese examples:
zf play music "周杰伦" --service "网易云音乐" --format json
zf play music "郑秀文" --enqueue --service "QQ音乐" --limit 5 --format json
zf play music "郑秀文 舍不得你" --service "QQ音乐" --format json
zf ncm lucky --name "客厅" "郑秀文" --format json
zf smapi search --name "客厅" --service "QQ音乐" --category tracks --open --index 1 --format json "周杰伦"
Default service selection:
- Chinese content (Chinese artist/song names, user speaks Chinese): use
--service "QQ音乐". No fallback — QQ has the best Chinese catalog coverage.
- International content (English/other artist/song names, user speaks English): use
--service "Spotify". If Spotify is unavailable, fallback to --service "Apple Music".
- If the user explicitly names a service, always honour that.
- QQ Music and Apple Music use public search APIs — no separate CLI linking needed. Just play directly.
- Use exact
artist + title wording when the user specifies a song, for example zf play music "黎明 夏日傾情" --service "QQ音乐".
Important distinction:
- if the user says "play X", use
zf play music "X" and expect a replace-style action
- if the user says "add X", "queue X", "append X", or "play X after this", use
zf play music "X" --enqueue
- if the wording is ambiguous and something is already playing,
--enqueue is usually the safer default
Natural-language examples:
- "play Taylor Swift" ->
zf play music "Taylor Swift"
- "add five Adele songs" ->
zf play music "Adele" --enqueue --limit 5
- "播郑秀文" ->
zf play music "郑秀文"
- "再加一首陈奕迅" ->
zf play music "陈奕迅" --enqueue
Lyrics rule
When the user asks about lyrics for the current song, use:
zf lyric --name "<room>" --format json
This fetches lyrics for the currently playing track when supported by the runtime. For raw LRC with timestamps, add --raw.
QQ Music lyrics now work reliably (MID resolution fixed). Both QQ Music and NetEase Cloud Music tracks return lyrics with translations when available.
Announcement rule
If the user asks for a short spoken interruption such as:
- "read a one-minute news brief"
- "announce the meeting starts in 5 minutes"
- "播一分钟新闻"
- "20 分钟后提醒我开会"
default to a short TTS or reminder path, not radio search.
Current stable announcement path (direct mode, default):
zf say "<text>" --name "<room>" --format json
TTS language options:
zh (普通话) — default, no flag needed
yue (粤语/Cantonese) — pass --lang yue
Example with Cantonese:
zf say "今日新闻" --name "<room>" --lang yue --format json
Important announcement rules:
- Always use direct mode (the default). Do NOT pass
--mode queue-insert.
Direct mode plays TTS then automatically restores the original track and position (reltime).
- Concatenate multiple items into one
zf say call. If the user asks for
"5 news headlines", compose all 5 headlines into a single text block and call
zf say once. Do NOT call zf say five separate times — that causes
interleaving with music and breaks reltime restore.
- After TTS completes, the original song resumes at the exact playback position.
Only treat it as a live radio request if the user explicitly asks for a station or live news channel.
Queue hygiene rule
After adding songs via zf play music with --enqueue or after any queue-related playback,
run an automatic queue prune to remove copyright-blocked tracks:
zf queue prune --name "<room>" --format json
This removes greyed-out tracks (e.g. "应版权方要求暂不能播放") from the queue.
Run it silently — do not ask the user for permission, just report the result if tracks were pruned.
Service readiness rule
Most music services work without separate CLI-side linking:
- QQ Music: uses public search API → always ready, no linking needed
- Apple Music: uses public search API → always ready, no linking needed
- Spotify: depends on household setup; try playing directly first
- NetEase Cloud Music (网易云音乐): uses SMAPI; may need initial linking via
zf setup
Users bind music services through the Sonos mobile app — there is no desktop binding.
Do not tell users "service not linked" if zf play music works. Try playing first, diagnose only on failure.
If you need a reliable probe, prefer:
zf doctor service --service "Spotify" --query "Miles Davis" --format json
New-session rule
If a new session starts and the user says things like:
- "done"
- "complete"
- "I logged in already"
- "已经绑好啦"
- "我登录好了"
do not force the whole onboarding conversation again.
Prefer this order:
- try playing the user's request directly
- fall back to
zf setup --format json only if playback fails and context is unclear
Failure routing
Read structured JSON errors first:
error.code
error.message
error.details
Do not classify every playback failure as auth or copyright.
Known patterns:
- if the same song plays in a helper room but not in the target room, suspect room-local queue or transport pollution first
- if you see
TRANSITIONING or partial queue failure, do not loop infinite retries
- if queue pollution is suspected, prefer explicit queue inspection and recovery commands
- if deeper room-local pollution is confirmed,
zf group rebuild --name "<target>" --via "<helper>" is the stronger recovery path
Boundaries
- Sonos official mobile apps remain the default path for adding and logging in to content services
- Sonos Web App is a supplementary control surface, not the primary onboarding path
group rebuild is a recovery tool, not proof that the original defect is fixed
- exact
RelTime restoration is not a guaranteed stable capability
- OpenClaw skill updates and local
zf runtime updates may require a new session to fully refresh
When to ask the user something
Ask only when required:
- choose a default room
- confirm a preferred music service
- confirm helper-room usage for
group rebuild
Do not ask the user to learn repo internals or memorize command names.
User-facing tone
Good examples:
- "I'll first check whether this machine can discover your Sonos speakers on the LAN."
- "I found these rooms: Office, Kitchen. Which one should be the default?"
- "我先检查这台机器能不能发现你局域网里的 Sonos。"
- "我找到这些房间:客厅、浴室。你想默认控制哪一个?"
Avoid:
- "Please install the Sonos controller" as a blanket answer
- "Learn the zf CLI first"
- "Not configured" before running preflight
Read these references when needed
Bundled reference files (same repo, references/ subdirectory):
onboarding-boundary.md — product scope and onboarding guidance
command-map.md — command map and recovery rules
china-service-linking.md — China service-linking notes