Yeelight Cinema Director
When to use
Use this Skill when a user asks for a film-themed light show, soundtrack
visualisation, or a private screening with Yeelight lights. It is a local,
single-user experience; it never starts a public server, performs a cloud
login, or asks the user to run a shell command.
Host workflow
The AI host owns service lifecycle. Execute the wrapper itself, parse its single
JSON-line response, reuse only a healthy instance with the requested mode and
Runtime context, and open the returned openUrl with the host browser tool.
Never tell the user to run a command, paste localhost, choose a port, or edit
an environment variable. The following are host-internal actions, not user
instructions:
# mock preview (the default for "预览/模拟/先看看")
sh scripts/invoke.sh start
# explicit live preflight (only after the user names a protected context)
sh scripts/invoke.sh start --mode live --profile <profile> --region <cn|sg|us|eu> --house-id <house-id>
# prefer the household gateway when protected Runtime metadata provides it
sh scripts/invoke.sh start --mode live --profile <profile> --region <cn|sg|us|eu> --house-id <house-id> --control-mode local-preferred --gateway-ip <private-gateway-ip>
The wrapper emits one JSON line with status, serviceStatus, mode,
openUrl, and healthUrl. PowerShell hosts use scripts/invoke.ps1 with the
same actions. If a running service has a different mode or live context, the
host runs stop and then the requested start; it does not silently reuse the
wrong instance. YEELIGHT_CINEMA_MODE=live and the active/default profile are
never enough to enable live mode.
Classify natural-language requests before starting:
- Mock preview: requests such as “预览、模拟、先看看效果” use the default
mock start. It never calls the Yeelight Runtime and may be opened
immediately.
- Live preflight: requests such as “真实、实体、EU 家庭” require an
explicit profile, region, and house selected by the host from protected
local Runtime metadata. The host must verify the requested region/profile,
discover the complete device list, and show the user the exact candidate
names, room, count, online/capability evidence, and read-only pre-state.
- Live execution: never begin a physical write from an ambiguous request.
Present the exact target set and recovery plan, then require the user to
confirm the scoped test in the conversation. After the exact confirmation,
the host uses the internal host-validation wrapper to prepare and consume a
one-time grant. The page proof alone cannot prepare or run a physical test.
A missing, stale, duplicated, offline, or capability-unknown target is a
hard stop.
The host must keep the returned URL and service state in its own tool context;
the user only interacts with the opened page. status and stop are host
actions for health/recovery, not commands to copy into chat.
The first page runs in deterministic mock mode, so catalog, audio, lighting,
stop, and error states are safe to explore without hardware. A live run is
opt-in and fails closed unless the installed yeelight-home Runtime passes a
read-only preflight with an explicit profile/region/house. The only production
path is:
browser -> loopback service -> semantic Runtime request -> yeelight-home invoke --stdin
The browser does not receive Runtime identities, credentials, request payloads,
headers, house/profile/region values, or artwork URLs. A session receives
opaque display handles for the user's current device selection. Handles are
bound to one immutable target snapshot and cannot be reused after stop,
replacement, timeout, or service restart.
Directing a screening
- Search for a film and choose an official poster and exactly one soundtrack.
Apple Music is the primary playback entry when the catalog returns an
official track link; the selected catalog track is enough to prepare.
- Select one or more discovered lights. Every selected light is assigned once
to
Accent or Ambient; those names describe musical roles, not bulb
counts. A single light still receives the complete composite track.
- Use
Open Apple Music window to open the selected track in one small,
resizable top-level player; after selecting a different soundtrack, clicking
the button reuses that window and navigates it to the new track instead of
opening another window.
The page also exposes a matching draggable Apple Music companion in the
lower-right corner. It stays available while the Cinema Director page is
used, and its Open player, Load selected track, and Bring player forward actions reuse the same top-level window as the panel button.
This avoids third-party iframe preview limits. The initial open uses the
companion's current position as a best-effort anchor. After the window has
loaded, the companion remains an independent, non-disruptive control surface:
browsers block web pages from moving an already-loaded cross-origin player,
and the page never closes or restarts playback just to follow a drag. The
page cannot force the player to remain always-on-top above Cinema Director.
YouTube appears only as a secondary fallback when the selected track has no
official Apple Music link. If a browser player should drive the lights, use
the native audio-share picker to share the tab or window playing the
soundtrack and enable its audio; the page cannot choose a source on the
user's behalf. Local audio remains the fallback.
- Prepare the console to capture a read-only target snapshot, then start it. In
live mode, Prepare remains read-only before host validation; Start stays blocked
until the bounded physical validation succeeds. The console shows live spectrum, conservative lyric cues, target
roles, dispatch acknowledgements, and per-target results.
- Pause, replace, clear, or stop at any time. Stop invalidates the generation
first, fades the frozen target snapshot, briefly powers it off, restores the
recorded live pre-state, and reads back each target. A partial, timed-out,
or restore-mismatched result is reported as
uncertain.
The browser sends only one live frame request at a time. A busy or cadence
skip means no new physical write was accepted and does not create a recovery
queue. Every live frame covers the complete frozen selected set, regardless of
whether the user selected one, four, eighteen, or another supported number of
lights. Runtime writes use a bounded pool of twelve workers, so a large home is
parallel without creating an unbounded process burst. The recovery journal is
persisted once for the complete frame before any worker writes; a fatal error or
cancellation stops new work and drains already-started workers before Stop or
restore begins. Live Stop queries the frozen target set once before writing to
select targets with in-session write evidence, then reuses that selection for
fade/off/restore. It does not query again before restore; one final readback is
the physical result, and mismatches remain recoverable.
A live partial or uncertain result is recorded and the next frame continues
when every failed row is retryable: either an exact target-bound verification
mismatch or a Runtime response explicitly marked safeToRetry. This also
applies when every selected light in the frame fails, so a flaky light or a
short-lived backend issue cannot strand the remaining frame loop. The page
labels the failure class, and affected targets remain pending in the screening
journal until Stop verifies their pre-state. Consecutive all-failed frames have
a bounded 300-frame grace budget (about 3 minutes at the default cadence); when
it is exhausted, playback stops and keeps the touched scope recoverable.
Explicit Runtime timeout, unavailable, process-exit, or malformed-response
failures are recorded as failed rows and continue with the next frame within
that budget. They may represent a write whose result is unknown, so they never
claim physical verification and Stop/readback remains authoritative. A
cancellation, journal/recovery, validation, session, or unknown/unbound failure
remains terminal. The current POST is never replayed. A browser request with no
HTTP response is treated as a lost connection: the page waits and sends the
next frame so a completed server-side tick does not strand the session. Parsed
HTTP errors remain terminal.
Flow receipts mean that a semantic request was acknowledged. They do not claim
that a physical light has changed. lighting.design.apply is reserved for
low-frequency initialisation and termination because Runtime applies and reads
back its attributes serially. High-frequency frames use a capability-gated
single-target Flow call or a bounded twelve-worker compatibility pool over the
complete selected set.
When protected Runtime metadata includes a household gateway, pass
controlMode=local-preferred with either gatewayIp or a validated
lanEndpoint. The service strips ambient LAN settings, then supplies only the
explicit context to the Runtime child. local-preferred lets yeelight-home
fall back according to its own policy; local-only is available when cloud
fallback is not acceptable. LAN endpoints must be local/private or link-local
HTTP(S) /mcp URLs without credentials, queries, or fragments. The browser
never selects or receives this endpoint.
Boundaries
- The service listens only on
127.0.0.1, rejects foreign Host/Origin and
cross-site POSTs, uses a short-lived page proof, and returns no CORS grant.
- Requests are JSON with bounded size and closed fields/enums. User text is
rendered as text, not executable markup. The parent page has a fixed local
script policy; YouTube is limited to a validated in-page iframe and explicit
browser audio capture. Apple Music opens only from a server-projected,
validated
music.apple.com link in a user-triggered top-level window. The
page requests an initial companion-anchored position, while the browser
controls any later native window movement, popup sizing, and audio-sharing
permission; the Skill never closes a playing cross-origin window to fake
repositioning.
- Artwork is fetched only from the server's signed opaque handle and exact
HTTPS host allowlist. Redirects, private DNS, oversized responses, and raw
upstream errors are rejected.
- No QR login, token profile, raw URL, raw header, MCP session, or arbitrary
Runtime intent is part of this Skill. A YouTube Data API key is optional and
remains process-local. Without a key, the service uses a fixed server-side
YouTube Web JSON search fallback; it never scrapes HTML or sends browser
cookies/login state. This fallback is an undocumented public web contract and
may return no candidates when YouTube changes or blocks it; local audio remains
the reliable fallback.
- The Skill does not call OpenAI, Anthropic, Gemini, or the host model from the
browser. AI capability comes from the host AI reading this Skill, classifying
the user's natural language, running the local wrapper, opening the page, and
presenting Runtime results. The page itself uses browser
AudioContext,
fixed catalog adapters, deterministic hue/lyric rules, and loopback HTTP
only. Deterministic catalog fixtures exist only in tests and are never
loaded by the production launcher.
- Live context is host-owned and explicit:
profile, region, house-id, and
optional local gateway fields are passed only to the local Runtime process.
They are never accepted from browser JSON, returned as page data, or placed
in logs. Live startup rejects dev, missing context, ambient API-base
overrides, and gateway endpoints that are not local/private /mcp URLs.
- The initial defensive limit is 160 selected lights per screening. It is a
resource guard, not a topology or room assumption; 161 is rejected before
any Runtime call. Every frame still covers all selected lights under this
limit.
Real-light validation gate
The first physical run is a separate, bounded verification rather than an
ordinary high-frequency screening. The host must:
- Run read-only
auth status, home list, entity.list, and the dedicated
exact-target state.batch.query preflight through the protected Runtime
context. Use single-target reads only for failed/uncertain write readback or
explicit recovery; the final validation restore uses one batch read.
Do not change the active profile or write credentials.
- Bind the exact upcoming screening scope by display name/room and Runtime
evidence (18 lights here, or any other explicitly selected N). Choose four
distinct lights from that bound scope as the physical sample. The number
four is a test scope, not a product limit; never infer “the first four
devices” when discovery returns more.
- Record a redacted, per-target pre-state containing power, brightness, color,
color temperature, online status, and capability evidence. If any field is
missing or a target is not independently confirmed as a light, stop.
- Ask for the explicit conversational confirmation
“确认执行上述 4 盏灯短时验证”. Before asking, use the host-only
validation wrapper with the four opaque handles and keep its short-lived
ASCII grant in host context. Only after the user confirms, call the host-only
run action with that grant. The server caps brightness at 10% and runs one
bounded validation sequence per target in series. A sequence may use
separate semantic brightness and power writes; it never replays the design
step, and a power-only fallback is limited to one additional direct power
call after trusted readback. Successful receipts skip an immediate readback;
failed or uncertain writes still pay for a single-target readback. After a
verified fade/off receipt, restore writes the recorded state directly without
a pre-restore state query, then one final batch read confirms the result. The
host wrapper receives both the four sample handles and the full
scopeHandles set; success authorizes only that exact set. The
conversational Chinese phrase is checked by the host wrapper and is never
copied into a request header. Do not use the ordinary high-frequency page Start
control for this first run; live Start stays blocked until validation succeeds.
- Report each target's write receipt, readback, restore result, and any
partial/uncertain state. A failed restore is not success; stop further
writes and keep the remaining recovery list visible to the host. A formally
bound partial Runtime receipt is accepted only after exact verified
readback; a power-only mismatch may use one direct power fallback only
after the phase brightness is confirmed, and never retries a design write.
See references/lighting-model.md, references/runtime-execution.md, and
references/setup.md and references/live-validation.md for the detailed
contracts.
1---2name: yeelight-cinema-director3description: Direct a local private cinema by pairing a selected film and soundtrack with browser audio analysis, lyrics-aware choreography, and explicitly selected Yeelight lights.4---56# Yeelight Cinema Director78## When to use910Use this Skill when a user asks for a film-themed light show, soundtrack11visualisation, or a private screening with Yeelight lights. It is a local,12single-user experience; it never starts a public server, performs a cloud13login, or asks the user to run a shell command.1415## Host workflow1617The AI host owns service lifecycle. Execute the wrapper itself, parse its single18JSON-line response, reuse only a healthy instance with the requested mode and19Runtime context, and open the returned `openUrl` with the host browser tool.20Never tell the user to run a command, paste `localhost`, choose a port, or edit21an environment variable. The following are host-internal actions, not user22instructions:2324```sh25# mock preview (the default for "预览/模拟/先看看")26sh scripts/invoke.sh start27# explicit live preflight (only after the user names a protected context)28sh scripts/invoke.sh start --mode live --profile <profile> --region <cn|sg|us|eu> --house-id <house-id>29# prefer the household gateway when protected Runtime metadata provides it30sh scripts/invoke.sh start --mode live --profile <profile> --region <cn|sg|us|eu> --house-id <house-id> --control-mode local-preferred --gateway-ip <private-gateway-ip>31```3233The wrapper emits one JSON line with `status`, `serviceStatus`, `mode`,34`openUrl`, and `healthUrl`. PowerShell hosts use `scripts/invoke.ps1` with the35same actions. If a running service has a different mode or live context, the36host runs `stop` and then the requested `start`; it does not silently reuse the37wrong instance. `YEELIGHT_CINEMA_MODE=live` and the active/default profile are38never enough to enable live mode.3940Classify natural-language requests before starting:41421. **Mock preview**: requests such as “预览、模拟、先看看效果” use the default43 mock start. It never calls the Yeelight Runtime and may be opened44 immediately.452. **Live preflight**: requests such as “真实、实体、EU 家庭” require an46 explicit profile, region, and house selected by the host from protected47 local Runtime metadata. The host must verify the requested region/profile,48 discover the complete device list, and show the user the exact candidate49 names, room, count, online/capability evidence, and read-only pre-state.503. **Live execution**: never begin a physical write from an ambiguous request.51 Present the exact target set and recovery plan, then require the user to52 confirm the scoped test in the conversation. After the exact confirmation,53 the host uses the internal host-validation wrapper to prepare and consume a54 one-time grant. The page proof alone cannot prepare or run a physical test.55 A missing, stale, duplicated, offline, or capability-unknown target is a56 hard stop.5758The host must keep the returned URL and service state in its own tool context;59the user only interacts with the opened page. `status` and `stop` are host60actions for health/recovery, not commands to copy into chat.6162The first page runs in deterministic mock mode, so catalog, audio, lighting,63stop, and error states are safe to explore without hardware. A live run is64opt-in and fails closed unless the installed `yeelight-home` Runtime passes a65read-only preflight with an explicit profile/region/house. The only production66path is:6768```text69browser -> loopback service -> semantic Runtime request -> yeelight-home invoke --stdin70```7172The browser does not receive Runtime identities, credentials, request payloads,73headers, house/profile/region values, or artwork URLs. A session receives74opaque display handles for the user's current device selection. Handles are75bound to one immutable target snapshot and cannot be reused after stop,76replacement, timeout, or service restart.7778## Directing a screening79801. Search for a film and choose an official poster and exactly one soundtrack.81 Apple Music is the primary playback entry when the catalog returns an82 official track link; the selected catalog track is enough to prepare.832. Select one or more discovered lights. Every selected light is assigned once84 to `Accent` or `Ambient`; those names describe musical roles, not bulb85 counts. A single light still receives the complete composite track.863. Use `Open Apple Music window` to open the selected track in one small,87 resizable top-level player; after selecting a different soundtrack, clicking88 the button reuses that window and navigates it to the new track instead of89 opening another window.90 The page also exposes a matching draggable Apple Music companion in the91 lower-right corner. It stays available while the Cinema Director page is92 used, and its `Open player`, `Load selected track`, and `Bring player93 forward` actions reuse the same top-level window as the panel button.94 This avoids third-party iframe preview limits. The initial open uses the95 companion's current position as a best-effort anchor. After the window has96 loaded, the companion remains an independent, non-disruptive control surface:97 browsers block web pages from moving an already-loaded cross-origin player,98 and the page never closes or restarts playback just to follow a drag. The99 page cannot force the player to remain always-on-top above Cinema Director.100 YouTube appears only as a secondary fallback when the selected track has no101 official Apple Music link. If a browser player should drive the lights, use102 the native audio-share picker to share the tab or window playing the103 soundtrack and enable its audio; the page cannot choose a source on the104 user's behalf. Local audio remains the fallback.1054. Prepare the console to capture a read-only target snapshot, then start it. In106 live mode, Prepare remains read-only before host validation; Start stays blocked107 until the bounded physical validation succeeds. The console shows live spectrum, conservative lyric cues, target108 roles, dispatch acknowledgements, and per-target results.1095. Pause, replace, clear, or stop at any time. Stop invalidates the generation110 first, fades the frozen target snapshot, briefly powers it off, restores the111 recorded live pre-state, and reads back each target. A partial, timed-out,112 or restore-mismatched result is reported as `uncertain`.113114The browser sends only one live frame request at a time. A `busy` or cadence115skip means no new physical write was accepted and does not create a recovery116queue. Every live frame covers the complete frozen selected set, regardless of117whether the user selected one, four, eighteen, or another supported number of118lights. Runtime writes use a bounded pool of twelve workers, so a large home is119parallel without creating an unbounded process burst. The recovery journal is120persisted once for the complete frame before any worker writes; a fatal error or121cancellation stops new work and drains already-started workers before Stop or122restore begins. Live Stop queries the frozen target set once before writing to123select targets with in-session write evidence, then reuses that selection for124fade/off/restore. It does not query again before restore; one final readback is125the physical result, and mismatches remain recoverable.126127A live `partial` or `uncertain` result is recorded and the next frame continues128when every failed row is retryable: either an exact target-bound verification129mismatch or a Runtime response explicitly marked `safeToRetry`. This also130applies when every selected light in the frame fails, so a flaky light or a131short-lived backend issue cannot strand the remaining frame loop. The page132labels the failure class, and affected targets remain pending in the screening133journal until Stop verifies their pre-state. Consecutive all-failed frames have134a bounded 300-frame grace budget (about 3 minutes at the default cadence); when135it is exhausted, playback stops and keeps the touched scope recoverable.136Explicit Runtime timeout, unavailable, process-exit, or malformed-response137failures are recorded as failed rows and continue with the next frame within138that budget. They may represent a write whose result is unknown, so they never139claim physical verification and Stop/readback remains authoritative. A140cancellation, journal/recovery, validation, session, or unknown/unbound failure141remains terminal. The current POST is never replayed. A browser request with no142HTTP response is treated as a lost connection: the page waits and sends the143next frame so a completed server-side tick does not strand the session. Parsed144HTTP errors remain terminal.145146Flow receipts mean that a semantic request was acknowledged. They do not claim147that a physical light has changed. `lighting.design.apply` is reserved for148low-frequency initialisation and termination because Runtime applies and reads149back its attributes serially. High-frequency frames use a capability-gated150single-target Flow call or a bounded twelve-worker compatibility pool over the151complete selected set.152153When protected Runtime metadata includes a household gateway, pass154`controlMode=local-preferred` with either `gatewayIp` or a validated155`lanEndpoint`. The service strips ambient LAN settings, then supplies only the156explicit context to the Runtime child. `local-preferred` lets `yeelight-home`157fall back according to its own policy; `local-only` is available when cloud158fallback is not acceptable. LAN endpoints must be local/private or link-local159HTTP(S) `/mcp` URLs without credentials, queries, or fragments. The browser160never selects or receives this endpoint.161162## Boundaries163164- The service listens only on `127.0.0.1`, rejects foreign Host/Origin and165 cross-site POSTs, uses a short-lived page proof, and returns no CORS grant.166- Requests are JSON with bounded size and closed fields/enums. User text is167 rendered as text, not executable markup. The parent page has a fixed local168 script policy; YouTube is limited to a validated in-page iframe and explicit169 browser audio capture. Apple Music opens only from a server-projected,170 validated `music.apple.com` link in a user-triggered top-level window. The171 page requests an initial companion-anchored position, while the browser172 controls any later native window movement, popup sizing, and audio-sharing173 permission; the Skill never closes a playing cross-origin window to fake174 repositioning.175- Artwork is fetched only from the server's signed opaque handle and exact176 HTTPS host allowlist. Redirects, private DNS, oversized responses, and raw177 upstream errors are rejected.178- No QR login, token profile, raw URL, raw header, MCP session, or arbitrary179 Runtime intent is part of this Skill. A YouTube Data API key is optional and180 remains process-local. Without a key, the service uses a fixed server-side181 YouTube Web JSON search fallback; it never scrapes HTML or sends browser182 cookies/login state. This fallback is an undocumented public web contract and183 may return no candidates when YouTube changes or blocks it; local audio remains184 the reliable fallback.185- The Skill does not call OpenAI, Anthropic, Gemini, or the host model from the186 browser. AI capability comes from the host AI reading this Skill, classifying187 the user's natural language, running the local wrapper, opening the page, and188 presenting Runtime results. The page itself uses browser `AudioContext`,189 fixed catalog adapters, deterministic hue/lyric rules, and loopback HTTP190 only. Deterministic catalog fixtures exist only in tests and are never191 loaded by the production launcher.192- Live context is host-owned and explicit: `profile`, `region`, `house-id`, and193 optional local gateway fields are passed only to the local Runtime process.194 They are never accepted from browser JSON, returned as page data, or placed195 in logs. Live startup rejects `dev`, missing context, ambient API-base196 overrides, and gateway endpoints that are not local/private `/mcp` URLs.197- The initial defensive limit is 160 selected lights per screening. It is a198 resource guard, not a topology or room assumption; 161 is rejected before199 any Runtime call. Every frame still covers all selected lights under this200 limit.201202## Real-light validation gate203204The first physical run is a separate, bounded verification rather than an205ordinary high-frequency screening. The host must:2062071. Run read-only `auth status`, `home list`, `entity.list`, and the dedicated208 exact-target `state.batch.query` preflight through the protected Runtime209 context. Use single-target reads only for failed/uncertain write readback or210 explicit recovery; the final validation restore uses one batch read.211 Do not change the active profile or write credentials.2122. Bind the exact upcoming screening scope by display name/room and Runtime213 evidence (18 lights here, or any other explicitly selected N). Choose four214 distinct lights from that bound scope as the physical sample. The number215 four is a test scope, not a product limit; never infer “the first four216 devices” when discovery returns more.2173. Record a redacted, per-target pre-state containing power, brightness, color,218 color temperature, online status, and capability evidence. If any field is219 missing or a target is not independently confirmed as a light, stop.2204. Ask for the explicit conversational confirmation221 **“确认执行上述 4 盏灯短时验证”**. Before asking, use the host-only222 validation wrapper with the four opaque handles and keep its short-lived223 ASCII grant in host context. Only after the user confirms, call the host-only224 run action with that grant. The server caps brightness at 10% and runs one225 bounded validation sequence per target in series. A sequence may use226 separate semantic brightness and power writes; it never replays the design227 step, and a power-only fallback is limited to one additional direct power228 call after trusted readback. Successful receipts skip an immediate readback;229 failed or uncertain writes still pay for a single-target readback. After a230 verified fade/off receipt, restore writes the recorded state directly without231 a pre-restore state query, then one final batch read confirms the result. The232 host wrapper receives both the four sample handles and the full233 `scopeHandles` set; success authorizes only that exact set. The234 conversational Chinese phrase is checked by the host wrapper and is never235 copied into a request header. Do not use the ordinary high-frequency page Start236 control for this first run; live Start stays blocked until validation succeeds.2375. Report each target's write receipt, readback, restore result, and any238 `partial`/`uncertain` state. A failed restore is not success; stop further239 writes and keep the remaining recovery list visible to the host. A formally240 bound partial Runtime receipt is accepted only after exact verified241 readback; a power-only mismatch may use one direct power fallback only242 after the phase brightness is confirmed, and never retries a design write.243244See `references/lighting-model.md`, `references/runtime-execution.md`, and245`references/setup.md` and `references/live-validation.md` for the detailed246contracts.