2026-04-17 17:04:30 -05:00
2026-04-17 17:04:30 -05:00
2026-04-17 17:04:30 -05:00

NVIDIA Shield

Home Assistant integration for the NVIDIA Shield TV — ADB media session tracking for now-playing state from any app, plus Google remote-protocol commands, Jellyfin rich metadata, and power monitoring.

This is a Shield-only fork of ltomes/ha-nvidia-shield (early 0.1.x), rebuilt around a different architecture: one integration, one device, ADB as the state source, and no dependency on separate androidtv / androidtv_remote integrations.

Status: early fork, actively rebuilt. Releases are date-based (v2026.10.08). Expect rough edges until the MVP phases land. The build-out plan is in docs/MVP_PLAN.md; device research is in docs/RESEARCH_SHIELD.md.

What we're building (the fork's goals)

The fork exists to make a single integration own the Shield end-to-end:

  1. Event-driven ADB media session tracking as the primary playback state source — dumpsys media_session over a persistent connection, not screen capture, not 10-second polling.
  2. Fallback state for apps without media sessions — e.g. TiviMate publishes none at all; foreground-app detection (mResumedActivity) + last-known-state
    • the "audio playback" hint from the same session dump close that gap.
  3. All-in-one device — absorb the Google remote protocol (androidtvremote2): a remote entity alongside the media player, play_media with media_content_type: channel for TiviMate channel-by-number, absolute volume, focused-app push events. ADB stays the playback-state source; ADB pairing stays intact.
  4. Retire the surrounding glue — the mediasession2mqtt bridge, universal media player templates, and external remote entity reading all become native integration behavior.

Phases 0–4 with files, risks, and definitions of done live in docs/MVP_PLAN.md.

Current features (inherited from upstream 0.1.x)

  • ADB Media Session — now-playing from any app that publishes a session (Spotify, Netflix, Plex, Wholphin, …)
  • Jellyfin rich metadata — title, series/episode, position, stream details (resolution, codec, HDR, audio format) via the HA Jellyfin integration's cache
  • Power monitoring — smart plug for reliable on/off with auto-discovered sibling sensors (watts, voltage, current)
  • Intent tracking — commands reflect in HA state immediately, self-heal against actual device state
  • Privacy mode — show only app name without media details for non-Jellyfin apps
  • Media DAG attributes — media_dag_* attributes for media-zone style orchestration

Changes inherited from upstream still apply in 0.1.x code: polling is timer-based, the ADB connection lives inside the media player entity, and fallback app state arrives in Phase 3.

State ladder and fallback semantics (Phase 3)

The media player state is decided top-down; each tier serves only when the ones above it are silent. Both transports live in the entry's ShieldConnectionManager (connection.py) — ADB is the state source, the remote protocol is the fast push/command path:

  1. Plug off — the power switch says off → off.
  2. Protocol power push (is_on) — False → off; None falls through.
  3. ADB wake (exact mWakefulness parse) — asleep → idle (standby).
  4. Intent — a just-sent command's target state, self-healing against reality within 30 s (retry goes over the transport that served it).
  5. Jellyfin — playing / paused while a Jellyfin session is active.
  6. ADB media session — playing / paused for apps that publish a session (foreground app preferred).
  7. Foreground fallback — an app is open (current_app protocol push, then the ADB mResumedActivity parse) but publishes no media session → on. Its identity comes from the TTL-capped last-known-state cache, the last channel entered, or the zero-cost "audio playback" hint (media recently active) — never resurrecting playing past the 60 s state TTL.
  8. On — powered, awake, nothing more specific.

TiviMate (ar.tvplayer.tv) is the documented no-session case: it publishes no MediaSession on Shield Android 11 (RESEARCH_SHIELD R2), so tiers 6–7 plus the audio hint are what keep it visible.

Standalone setup accepts any one of the three state sources — the Android TV Remote entity, the Nvidia Remote Protocol (PIN pairing), or ADB media-session mode (ADB enabled + host). The no_state_source form error fires only when none of the three is present; ADB-only setups are valid.

Channel entry options

The options flow exposes the §7 channel behavior the integration replicates: the inter-digit delay (0.05–1.0 s, default 0.1) and the optional ENTER press after the digits (off by default — TiviMate's validated behavior). Both are consumed by play_media with media_content_type: channel.

YAML seeding (optional, seed-only)

An optional nvidia_shield: block in configuration.yaml seeds an interactive setup flow — nothing is created without the user walking the standard flow, and pairing stays interactive exactly as with UI setup:

nvidia_shield:
  - name: Living Room
    adb_host: 10.0.0.31      # or protocol_host for the PIN-pairing hint
    adb_port: 5555

protocol_host is a prefill hint only (the PIN pairing still happens in the flow). Blocks without protocol/adb hosts are ignored with a warning. Duplicate guards: a block matching an existing entry's host (or its virtual yaml:… unique_id) aborts and never re-seeds a UI-configured host. Removing the YAML block creates or deletes nothing — entries live in the storage registry and require user consent to remove. Deleting/re- adding is the only way to move an imported entry between hosts; the UI is the contractual path (YAML is the seed, the flow is the truth).

Raw adb_command service (opt-in)

When enabled in the integration's options (adb_command_enabled, default off), the integration registers the nvidia_shield.adb_command service: send a raw ADB shell command to the entry's persistent connection ({"command": "your-command", "entity_id": "media_player.<yours>"} — the entity is optional when exactly one entry provides the service). This is the same security posture as the Home Assistant core androidtv integration: a documented, opt-in escape hatch for debugging and automation, registered at setup and unregistered when the last participating entity unloads. It rejects (logs a warning, sends nothing) when the ADB connection is down.

Migration from mediasession2mqtt + universal-player glue

The mediasession2mqtt bridge sensors and the universal media player YAML templates the fork replaces are retired by the ladder above: the media player entity already carries the DAG attributes (media_dag_running_apps with foreground/last-known enrichment, media_dag_now_playing with its source tag) that media-zone orchestration consumed from those sensors. See the appendix in docs/RESEARCH_SHIELD.md for the retirement mapping and the universal-player glue it replaces.

Releases

  • Date-based versioning: vYYYY.MM.DD (e.g. v2026.10.08).
  • Pushes to master on Gitea run the release workflow (.github/workflows/release.yml):
    • A plain push publishes the current day's release (same-day pushes are deduped).
    • Bumping version in custom_components/nvidia_shield/manifest.json forces that specific version's release before the date fallback kicks in.
  • Releases land on Gitea with the tag and a nvidia_shield.zip asset for manual installs. (No automatic mirroring — see the HACS note below.)

Installation

HACS

HACS only works with public GitHub repositories, so the Gitea repo cannot be added to HACS directly. If you set up a public GitHub mirror of the releases, add it as a custom repository:

  1. In HACS, open the overflow menu (⋮) → Custom repositories.
  2. Paste https://github.com/notexpectedyet/ha-nvidia-shield, pick type Integration, click Add.
  3. Find NVIDIA Shield in the HACS list and click Download.
  4. Restart Home Assistant.

Manual

Copy custom_components/nvidia_shield to your HA config/custom_components/ directory, restart, then add the integration from Settings → Devices & Services.

Configuration

  1. Settings > Devices & Services > Add Integration > "NVIDIA Shield"
  2. Select the Android TV Remote entity for your Shield
  3. Enable ADB if you want now-playing from all apps
  4. If ADB is enabled, the next step will pair — approve the prompt on the Shield
  5. Optionally configure Jellyfin, receiver, power switch

ADB Pairing

ADB is paired during setup via a dedicated config flow step. If you need to re-pair later (e.g. after factory reset), use the "Pair ADB" button on the device page in HA.

Requirements: Settings > Developer options > Network debugging enabled on the Shield.

Options

  • Jellyfin Media Player — auto-detected or manually selected
  • Receiver + Input — for media-zone coordination
  • Power Switch — smart plug entity
  • ADB Host/Port — override auto-detection if needed

Entities

Entity Type Description
Media Player media_player Main entity with playback controls and metadata
Current app (sensor.current_app) sensor Currently running app
Now playing (sensor.now_playing) sensor Formatted now-playing string
Pair ADB button Initiate or re-pair ADB connection

Development

  • Repository: Gitea — git.notexpectedyet.com/notexpectedyet/ha-nvidia-shield, branch master
  • CI/CD: Gitea Actions; every push to master releases (see .gitea/workflows/release.yml). Auth uses the job-provided GITEA_TOKEN — no extra secrets required.
  • Shared ADB helpers are vendored at custom_components/nvidia_shield/ha_adb_common/ (trimmed in this fork); dev extras in pyproject.toml (uv sync --extra dev).

License

MIT

S
Description
Home Assistant integration for NVIDIA Shield TV: ADB media session tracking, Jellyfin stream metadata, smart-plug power monitoring
Readme MIT
1.2 MiB
0 Stars 2 Watchers 0 Forks
v2026.10.12.2
Latest
2026-10-12 14:29:02 +01:00
Languages
Python 99.6%
Shell 0.4%