Named-room media playback
Chen's watch loop remains Plex-first and acquisition-gated. When a title is already in Plex and the operator names an allowlisted room, Luna hands a library reference plus place intent to Earthshaker. Earthshaker owns physical target resolution, adapter control, and bounded world observation.
This is not acquisition. Kernel Io (app/io_tethers.py) is unrelated. Home
Assistant lights remain a separate Earthshaker lane. Chen/media_manager must
not instantiate a Plex-shaped physical room controller.
Boundary
Human
-> Luna intent (`play` + title + Living Room)
-> Chen/media availability (`already_present` + rating key)
-> Earthshaker `play_available_title(place, media_reference)`
-> Apple TV / Plex adapters beneath Earthshaker
-> Plex session observation
-> Luna
Luna does not need Apple TV IP, port 32500, Companion credentials,
com.plexapp.plex, or Plex client HTTP details.
Physical entity
The first Earthshaker playback entity is the living-room Apple TV:
- entity id:
living_room.apple_tv(canonical; not an IP address) - place:
living_room - role:
media_player - capability:
play_available_title - Plex client identifier:
823A4A33-9C7F-4D41-AAEE-3A18EFF3311F - last-known address hint:
192.168.1.112(DHCP hint only) - display: Samsung UN75J6300 (recovery fact, not the player)
Unknown rooms fail closed. Multiple matching Plex clients fail closed. A
missing Plex client fails closed with plex_client_unavailable after an
optional Companion launch.
Basement is the first dynamic exact-place binding. Its Apple TV identity is not
seeded in source. Earthshaker performs bounded Companion/AirPlay observation,
requires exactly one eligible candidate and a human-created off-git pairing,
then uses current address only as a control hint. After the first matching
playback session is observed, Earthshaker persists stable Apple and Plex client
identifiers in /data/earthshaker/physical_bindings.json. Later requests probe
the hint and revalidate stable identity before actuation; they rediscover only
to find the same pinned identity after an address change.
When that unique candidate is not paired, Luna creates a durable pairing
challenge and starts the bounded Companion protocol session. The challenge
holds the original play operation, Chen's resolved library reference, the
Basement place, stable candidate evidence, operator/context binding, and an
expiry. It does not hold the PIN or Companion credential. Luna accepts a
four-digit response only for exactly one matching live challenge; arbitrary
numeric messages, expired challenges, and ambiguous challenges are not treated
as pairing input.
Wrong codes and expired live sessions fail closed and require try again,
which generates a new TV code. I don't see a code stops the current session
before offering that retry, and cancel terminates the suspended operation.
Successful pairing writes credentials only to the mode-0600 off-git binding,
revalidates authority and physical readiness, and resumes the immutable
original request without requiring the operator to repeat it. The normal Plex
session observation remains the only source of an observed playback claim.
Operator phrases
These are the same media-resolution phrases as watch, plus an optional room:
I want to watch Interstellar in the living roomPlay Interstellar on the Living Room TVPlay Interstellar in the Basement
Without a room suffix, behavior is unchanged: resolve or propose acquisition, no playback.
Capability
play_library_title_on_named_room is auto-execute for the exact allowlisted
places living_room and basement only. Provider:
EarthshakerMediaPlaybackService.play_available_title.
It cannot target arbitrary clients, refresh Plex, move files, or call Home
Assistant. Observation does not grant new execution authority.
The Plex server address advertised to the Apple TV is PLEX_ADVERTISED_ADDRESS
when the configured PLEX_BASE_URL is loopback or host.docker.internal.
The household default is ser6buzz at 192.168.1.137.
Playback is sent to the Apple TV Plex companion port (host:32500) when that
client is reachable. HTTP 200 from playMedia is not success. Earthshaker
reports:
blocked— command rejected or failed before a trustworthy dispatchdispatched_unverified— command dispatched, no matching Plex sessionobserved— matching Plex session for that client and title
App launch
Launching Plex from Earthshaker uses the Companion protocol. The living-room
Apple TV is paired; credentials live in ~/.config/luna-appletv/living_room.json
and are not in git. If Plex is not already a reachable client, playback launches
the already-bound device with Home when Companion reports it asleep, requires a
bounded awake state, launches com.plexapp.plex, and then polls for the unique
Plex client before sending playMedia. A client listed in /clients is still
not actuation: if playMedia fails, Earthshaker performs the same Companion
wake once and retries the identical command. A successful Companion launch call is
not itself readiness or playback evidence. After dispatch, Earthshaker uses a
bounded twelve-attempt, one-second session-observation window so normal Apple
TV startup and Plex session-publication delay do not weaken the requirement for
a matching client, title, and playback state.