Initial release v0.1.0
Home Assistant custom integration for NVIDIA Shield TV that composes the Android TV Remote, Jellyfin, smart plug, and receiver entities into a single unified media player with ADB-based now-playing from any app. Part of the Media Zone ecosystem. Entities: - media_player: power (with smart plug fallback), transport, input, volume routed to receiver, rich stream info overlay when Jellyfin is active - sensor: DAG attributes and stream info - button: Pair ADB Config: - Two-step flow: basic setup + optional ADB pairing on the Shield - Configurable ADB port (default 5555) - Pick an existing Jellyfin media_player entity — stream info is read from the HA Jellyfin integration's own DataUpdateCoordinator, so no server URL / API key / extra config needed here - Privacy mode (adb_store_app_info=False) suppresses non-Jellyfin media details, showing only the app name Ecosystem: - Exposes media_dag_* attributes for ha-media-zone orchestration - Exposes media_dag_running_apps and async_bring_to_foreground for the Media Zone card's app switcher Shared ADB code lives in ltomes/ha-adb-common and is vendored at custom_components/nvidia_shield/ha_adb_common/ for HACS installs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
0ad51879ae
24 files changed
+2538
No files matched your search
@@ -0,0 +1,72 @@
|
||||
"""Build the running apps list from recent tasks + media sessions.
|
||||
|
||||
Produces the media_dag_running_apps attribute value that media_zone reads.
|
||||
Recent tasks (from dumpsys activity recents) provide the list of running apps.
|
||||
Media sessions provide playback state for apps that have active sessions.
|
||||
"""
|
||||
|
||||
from .media_session import MediaSessionInfo
|
||||
|
||||
|
||||
def running_apps_from_tasks(
|
||||
recent_tasks: list[dict],
|
||||
media_sessions: list[MediaSessionInfo] | None = None,
|
||||
foreground_package: str | None = None,
|
||||
) -> list[dict] | None:
|
||||
"""Build a list of running app dicts from recent tasks + media sessions.
|
||||
|
||||
Args:
|
||||
recent_tasks: List of {"package": str} from ADB recent tasks.
|
||||
media_sessions: Optional media sessions for playback state enrichment.
|
||||
foreground_package: The current foreground app package.
|
||||
|
||||
Returns None if there are fewer than 2 apps (no point showing a switcher
|
||||
with only one option).
|
||||
"""
|
||||
if not recent_tasks or len(recent_tasks) < 2:
|
||||
return None
|
||||
|
||||
# Index media sessions by package for quick lookup
|
||||
session_by_pkg: dict[str, MediaSessionInfo] = {}
|
||||
if media_sessions:
|
||||
for s in media_sessions:
|
||||
if s.package:
|
||||
session_by_pkg[s.package] = s
|
||||
|
||||
apps = []
|
||||
for task in recent_tasks:
|
||||
pkg = task["package"]
|
||||
app: dict = {"package": pkg}
|
||||
|
||||
# Mark foreground app
|
||||
if pkg == foreground_package:
|
||||
app["foreground"] = True
|
||||
|
||||
# Enrich with media session data if available
|
||||
session = session_by_pkg.get(pkg)
|
||||
if session:
|
||||
if session.playing:
|
||||
app["state"] = "playing"
|
||||
elif session.paused:
|
||||
app["state"] = "paused"
|
||||
else:
|
||||
app["state"] = "idle"
|
||||
if session.title:
|
||||
app["title"] = session.title
|
||||
if session.artist:
|
||||
app["artist"] = session.artist
|
||||
else:
|
||||
app["state"] = "running"
|
||||
|
||||
apps.append(app)
|
||||
|
||||
return apps if len(apps) >= 2 else None
|
||||
|
||||
|
||||
# Keep backward compat for any code still using this
|
||||
def running_apps_from_sessions(sessions: list[MediaSessionInfo]) -> list[dict] | None:
|
||||
"""Build running apps from media sessions only (legacy)."""
|
||||
if not sessions:
|
||||
return None
|
||||
tasks = [{"package": s.package} for s in sessions if s.package]
|
||||
return running_apps_from_tasks(tasks, sessions)
|
||||
Reference in new issue
Block a user