feat(media_player): app_id/app_name prefer the androidtv entity, fall back to the protocol current_app push when absent/unavailable; manifest androidtvremote2 floor aligned to >=0.2.0 (P3-1b,c)
This commit is contained in:
1 parent
d35b61078d
commit
34d09e9f70
3 files changed
+59
-6
No files matched your search
@@ -11,7 +11,7 @@
|
||||
"issue_tracker": "https://git.notexpectedyet.com/notexpectedyet/ha-nvidia-shield/issues",
|
||||
"requirements": [
|
||||
"adb-shell>=0.4.0",
|
||||
"androidtvremote2>=0.1.0"
|
||||
"androidtvremote2>=0.2.0"
|
||||
],
|
||||
"version": "2026.10.08"
|
||||
}
|
||||
@@ -110,7 +110,7 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
|
||||
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
self.hass = hass
|
||||
self._entry = entry
|
||||
self._androidtv_entity = entry.data[CONF_ANDROIDTV_ENTITY]
|
||||
self._androidtv_entity = entry.data.get(CONF_ANDROIDTV_ENTITY, "")
|
||||
self._attr_unique_id = f"{DOMAIN}_{entry.entry_id}_media_player"
|
||||
self._unsub_listeners: list = []
|
||||
|
||||
@@ -489,13 +489,28 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
|
||||
|
||||
@property
|
||||
def app_id(self):
|
||||
atv = self._read(self._androidtv_entity)
|
||||
return atv.attributes.get("app_id") if atv else None
|
||||
"""Android TV entity first; standalone setups fall back to the
|
||||
protocol current-app push (raw package — both stamp the store)."""
|
||||
atv = self._read(self._androidtv_entity) if self._androidtv_entity else None
|
||||
if atv is not None:
|
||||
app_id = atv.attributes.get("app_id")
|
||||
if app_id:
|
||||
return app_id
|
||||
if self._manager is not None:
|
||||
return self._manager.current_app
|
||||
return None
|
||||
|
||||
@property
|
||||
def app_name(self):
|
||||
atv = self._read(self._androidtv_entity)
|
||||
return atv.attributes.get("app_name") if atv else None
|
||||
atv = self._read(self._androidtv_entity) if self._androidtv_entity else None
|
||||
if atv is not None:
|
||||
app_name = atv.attributes.get("app_name")
|
||||
if app_name:
|
||||
return app_name
|
||||
if self._manager is not None:
|
||||
# Name resolution is deferred app-map work; the package is truth.
|
||||
return self._manager.current_app
|
||||
return None
|
||||
|
||||
@property
|
||||
def media_content_type(self):
|
||||
|
||||
@@ -342,6 +342,44 @@ class TestRemoteEntity:
|
||||
assert protocol.key_calls == []
|
||||
|
||||
|
||||
class TestAppFallback:
|
||||
async def test_app_id_prefers_the_androidtv_entity(self, modules):
|
||||
player, _s, _i = make_player(modules)
|
||||
player._androidtv_entity = "media_player.atv"
|
||||
from types import SimpleNamespace as FakeState
|
||||
|
||||
state = FakeState(attributes={"app_id": "com.plex.hdx", "app_name": "Plex"})
|
||||
states = FakeState(states=FakeState(
|
||||
get=lambda eid: state if eid == "media_player.atv" else None
|
||||
))
|
||||
player.hass = states
|
||||
player._read = lambda eid: state if eid == "media_player.atv" else None
|
||||
|
||||
assert player.app_id == "com.plex.hdx"
|
||||
assert player.app_name == "Plex"
|
||||
|
||||
async def test_app_falls_back_to_protocol_current_app(self, modules):
|
||||
player, _s, _i = make_player(modules)
|
||||
player._androidtv_entity = "" # standalone setup (P3-1)
|
||||
player._manager._on_current_app("com.netflix.ninja")
|
||||
await asyncio.sleep(0.05) # drain the call_soon_threadsafe push
|
||||
|
||||
assert player.app_id == "com.netflix.ninja"
|
||||
assert player.app_name == "com.netflix.ninja" # package is truth; map later
|
||||
|
||||
async def test_app_unavailable_entity_falls_back(self, modules):
|
||||
player, _s, _i = make_player(modules)
|
||||
player._androidtv_entity = "media_player.atv"
|
||||
from types import SimpleNamespace as FakeState
|
||||
|
||||
player.hass = FakeState(states=FakeState(get=lambda eid: None)) # entity gone
|
||||
player._read = lambda eid: None
|
||||
player._manager._on_current_app("com.google.android.youtube.tv")
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
assert player.app_id == "com.google.android.youtube.tv"
|
||||
|
||||
|
||||
class TestVolumeAndPowerLadder:
|
||||
async def test_volume_readout_from_protocol_push(self, modules):
|
||||
player, served, _intents = make_player(modules)
|
||||
|
||||
Reference in new issue
Block a user