diff --git a/custom_components/nvidia_shield/connection.py b/custom_components/nvidia_shield/connection.py index ac42aaa..c153ed3 100644 --- a/custom_components/nvidia_shield/connection.py +++ b/custom_components/nvidia_shield/connection.py @@ -347,6 +347,15 @@ class ShieldConnectionManager: """Visible recent-task apps, ordered by recency.""" return self.store.get(FIELD_RUNNING_APPS, []) + @property + def protocol_active(self) -> bool: + """True when the remote-protocol transport is wired for this entry. + + Entities use this instead of reaching into ``_protocol``: a passive + manager means "no transport", not "broken transport". + """ + return self._protocol is not None + @property def is_on(self) -> bool | None: """Protocol power push (device reports on/off state).""" diff --git a/custom_components/nvidia_shield/media_player.py b/custom_components/nvidia_shield/media_player.py index 066535a..6b0980d 100644 --- a/custom_components/nvidia_shield/media_player.py +++ b/custom_components/nvidia_shield/media_player.py @@ -1,19 +1,23 @@ """Media player entity for Nvidia Shield. -State architecture: - - Power/availability: androidtv_remote (local push, fast) - - Playback state: Jellyfin (polling, slow 10-30s) - - Metadata: Jellyfin attributes - - ALL commands: androidtv_remote (Jellyfin is read-only) +Phase 2 architecture (docs/MVP_PLAN.md §2): the entry's ShieldConnectionManager +(connection.py) owns both transports — ADB (adb_shell, state source) and the +absorbed remote protocol (androidtvremote2, local push) — plus their StateStore; +this entity is a thin callback-view over it. -ADB connection/polling lives in the entry's ShieldConnectionManager -(connection.py); this entity is a thin callback-view over its StateStore. +State architecture: + - Power ladder: protocol is_on push → ADB wake (exact mWakefulness) → plug off + - Playback state: Jellyfin (polling, slow 10-30s) or ADB media session + - Metadata: Jellyfin attributes + - Commands: protocol key push → ADB keyevent → external androidtv_remote + (the last tier is transitional; removed in Phase 4) Intent tracking bridges the gap between instant commands and slow Jellyfin polling: 1. User presses pause → intent recorded → UI shows paused immediately - 2. Command sent to androidtv_remote → Shield pauses on screen + 2. Command sent over the serving transport → Shield pauses on screen 3. 10-30s later Jellyfin confirms paused → intent cleared - 4. If Jellyfin doesn't confirm in 30s → retry once → if still no → surrender to truth + 4. If Jellyfin doesn't confirm in 30s → retry once on the serving + transport (protocol/ADB stamped on the intent) → surrender to truth """ import asyncio diff --git a/custom_components/nvidia_shield/remote.py b/custom_components/nvidia_shield/remote.py index cd25a31..6f7d8b3 100644 --- a/custom_components/nvidia_shield/remote.py +++ b/custom_components/nvidia_shield/remote.py @@ -101,7 +101,7 @@ class NvidiaShieldRemoteEntity(RemoteEntity): manager = self._manager_of() if not isinstance(manager, ShieldConnectionManager): return True - if manager._protocol is None: + if not manager.protocol_active: return True return manager.is_available is not False