The user ruling: this integration REPLACES both old integrations, so nothing may depend on an androidtv/androidtv_remote entity anymore. Removed: * the setup-form field + its entity selector (CONF_ANDROIDTV_ENTITY is no longer offered, only read by the migration that drops it); * the entity-derived unique_id derivation (_unique_id_from_atv_device, t29 G2) and, with it, every registry lookup for another integration's devices — the user step now sets NO unique_id at all; the protocol MAC learned during pairing is the identity; * the Jellyfin auto-detect's second tier (the atv entity's friendly_name) — it now uses the entry name the user typed or the device name from discovery; * the app-name and app-id tiers (media_player.app_name/app_id + the shared resolve_app_name chain, the Current App sensor, the sensor's state-change listener and its `_androidtv_state` helper) — the manager's store is the only source; * the "transitional external entity" command tier in every path: `_cmd` and the pan-media_player service call are gone, and so is the fallback in _media_cmd, _remote_key_cmd, the §7 channel-digit sequence and the intent self-heal retry (an intent no transport served now surrenders to the truth instead of delegating); * the entity-derived power signal (`_on_source_change`'s "device off clears the intent" check no longer watches another integration's entity; the power LADDER — protocol is_on → ADB wake → power switch — is unchanged). turn_on/turn_off are native now: they send the protocol POWER key and, like the remote entity, send nothing when the known state already matches (no superfluous toggle). An ADB-only entry (protocol skipped) has no power write path — the manager owns the socket and exposes no device-keyevent, which is what the old delegation used to cover. helpers.discover_adb_host — the one helper that derived a host from another integration's config entry — is deleted; button.py (granted to this task) reads the entry's own address fields instead of the entity, so the Pair ADB button keeps working. Present-day entries that still carry the key are unaffected (nothing reads it); the v3 migration that drops it lands with the host unification. Verified: 309 passed / 2 skipped, 310 / 1 with androidtvremote2, ruff clean, drift engine exit 0 on HEAD (13 modules, 58 imports, 35 call arguments, 6 awaited HA calls) and 16 passed in the real-HA drift suite. The stub harness's legacy service recorder became a watchdog: any HA service call from the media player now fails the routing tests instead of being the expected outcome.
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""Shared config helpers for the NVIDIA Shield integration."""
|
|
|
|
import os
|
|
from dataclasses import dataclass
|
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from .const import (
|
|
CONF_ADB_HOST,
|
|
CONF_ADB_PORT,
|
|
CONF_PROTOCOL_HOST,
|
|
DEFAULT_ADB_PORT,
|
|
DOMAIN,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class AdbConfig:
|
|
"""Resolved ADB connection settings for a config entry."""
|
|
|
|
host: str
|
|
port: int
|
|
key_path: str
|
|
|
|
|
|
def adb_key_path(hass: HomeAssistant) -> str:
|
|
"""Path of the ADB private key for this integration (may not exist yet)."""
|
|
return os.path.join(hass.config.config_dir, ".storage", DOMAIN, "adbkey")
|
|
|
|
|
|
def protocol_cert_paths(hass: HomeAssistant) -> tuple[str, str]:
|
|
"""Local (certfile, keyfile) paths for the remote protocol client.
|
|
|
|
Mirrors core androidtv_remote's storage pattern: private material under
|
|
``.storage/<domain>/``. Shared between config-flow pairing and runtime —
|
|
an entry aborts duplicate setup by unique_id (= device MAC), so one pair
|
|
of files suffices per install.
|
|
"""
|
|
base = os.path.join(hass.config.config_dir, ".storage", DOMAIN)
|
|
os.makedirs(base, exist_ok=True)
|
|
return os.path.join(base, "client.pem"), os.path.join(base, "client.key")
|
|
|
|
|
|
def resolve_adb_config(hass: HomeAssistant, entry) -> AdbConfig | None:
|
|
"""Resolve host/port/key_path for the entry's ADB connection, or None.
|
|
|
|
Priority: the entry's own address fields (t41: no other integration is
|
|
consulted) — the ADB field first, then the protocol field, since both
|
|
describe the same Shield.
|
|
"""
|
|
host = entry.options.get(CONF_ADB_HOST) or entry.options.get(CONF_PROTOCOL_HOST)
|
|
if not host:
|
|
return None
|
|
port = int(entry.options.get(CONF_ADB_PORT, DEFAULT_ADB_PORT))
|
|
return AdbConfig(host=host, port=port, key_path=adb_key_path(hass))
|