fix(sensor): F2 — the Current App sensor shows the resolved app name (t40)
Reported from a live install: the sensor showed ``ar.tvplayer.tv`` while the media player showed "TiviMate" — the same foreground app, two identities. The sensor's ``native_value`` returned ``manager.foreground_app`` (the raw package push) or ``running_apps[0]["package"]`` as-is, while the media player resolved names through apps map → cached manifest label → androidtv entity tier. Both now call ONE function, ``resolve_app_name`` (media_player.py), so the two entities cannot disagree again; the sensor keeps the raw package as its ``app_id`` attribute and ``app_name`` carries the shown name. Per the contract: * apps map wins, then the cached manifest label, then the androidtv entity's ``app_name``, then the package text itself; * a bare or unknown package falls back to the package text, NOT None (it used to be possible for the value to vanish while the app was plainly running); * pinned by tests: map tier, label tier, entity tier, the ADB-recents source, the unknown-package fallback, and sensor == media player for the same inputs. Verified: 302 passed / 2 skipped, ruff clean.
This commit is contained in:
1 parent
be3abae3e7
commit
dd23d01a88
3 files changed
+177
-34
No files matched your search
@@ -13,9 +13,11 @@ from homeassistant.helpers.event import async_track_state_change_event
|
||||
from .connection import ShieldConnectionManager
|
||||
from .const import (
|
||||
CONF_ANDROIDTV_ENTITY,
|
||||
CONF_APP_NAME_MAP,
|
||||
CONF_JELLYFIN_ENTITY,
|
||||
DOMAIN,
|
||||
)
|
||||
from .media_player import resolve_app_name
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -101,14 +103,23 @@ class NvidiaShieldCurrentAppSensor(NvidiaShieldSensorBase):
|
||||
def _handle_store_update(self) -> None:
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def native_value(self) -> str | None:
|
||||
state = self.hass.states.get(self._androidtv_entity)
|
||||
if state:
|
||||
return state.attributes.get("app_name") or state.attributes.get("app_id")
|
||||
def _androidtv_state(self):
|
||||
"""The configured androidtv entity's state object, or None."""
|
||||
if not self._androidtv_entity:
|
||||
return None
|
||||
return self.hass.states.get(self._androidtv_entity)
|
||||
|
||||
# androidtv entity unavailable — StateStore priority chain (P3-3c):
|
||||
# protocol current_app push → ADB mResumedActivity → ADB recents
|
||||
def _package(self) -> str | None:
|
||||
"""The raw foreground package (t40): entity tier, then the store.
|
||||
|
||||
androidtv entity unavailable — StateStore priority chain (P3-3c):
|
||||
protocol current_app push → ADB mResumedActivity → ADB recents.
|
||||
"""
|
||||
state = self._androidtv_state()
|
||||
if state is not None:
|
||||
package = state.attributes.get("app_id")
|
||||
if package:
|
||||
return package
|
||||
manager = self._manager()
|
||||
if manager is not None:
|
||||
foreground = manager.foreground_app
|
||||
@@ -120,19 +131,34 @@ class NvidiaShieldCurrentAppSensor(NvidiaShieldSensorBase):
|
||||
return None
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict:
|
||||
state = self.hass.states.get(self._androidtv_entity)
|
||||
if state:
|
||||
return {
|
||||
"app_id": state.attributes.get("app_id"),
|
||||
"app_name": state.attributes.get("app_name"),
|
||||
}
|
||||
def native_value(self) -> str | None:
|
||||
"""The resolved friendly name (t40), not the raw package id.
|
||||
|
||||
Exactly the ``media_player.app_name`` chain via the shared
|
||||
``resolve_app_name``: apps map → cached manifest label → androidtv
|
||||
entity tier → the package text itself. A bare or unknown package shows
|
||||
its package text rather than None, like the media player's app_name.
|
||||
"""
|
||||
manager = self._manager()
|
||||
if manager is not None:
|
||||
running = manager.running_apps
|
||||
if running:
|
||||
return {"app_id": running[0].get("package")}
|
||||
return {}
|
||||
state = self._androidtv_state()
|
||||
return resolve_app_name(
|
||||
self._package(),
|
||||
dict(self._entry.options.get(CONF_APP_NAME_MAP) or {}),
|
||||
manager.app_label if manager is not None else None,
|
||||
state.attributes.get("app_name") if state is not None else None,
|
||||
)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict:
|
||||
"""The raw package stays visible here (t40) next to the shown name."""
|
||||
package = self._package()
|
||||
attrs: dict = {}
|
||||
if package:
|
||||
attrs["app_id"] = package
|
||||
name = self.native_value
|
||||
if name:
|
||||
attrs["app_name"] = name
|
||||
return attrs
|
||||
|
||||
|
||||
class NvidiaShieldNowPlayingSensor(NvidiaShieldSensorBase):
|
||||
|
||||
Reference in new issue
Block a user