From d57c4eaa408b27c7fbed3b8319a31fcb2dba17f0 Mon Sep 17 00:00:00 2001 From: James Mackay Date: Thu, 8 Oct 2026 09:33:19 +0100 Subject: [PATCH] feat: protocol_settings wiring + InvalidAuth reauth start + entry migration to v2; PLATFORMS gains REMOTE (placeholder platform until t10) --- custom_components/nvidia_shield/__init__.py | 69 +++++++++++++++++++-- custom_components/nvidia_shield/remote.py | 19 ++++++ 2 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 custom_components/nvidia_shield/remote.py diff --git a/custom_components/nvidia_shield/__init__.py b/custom_components/nvidia_shield/__init__.py index c68380e..59b82ed 100644 --- a/custom_components/nvidia_shield/__init__.py +++ b/custom_components/nvidia_shield/__init__.py @@ -19,12 +19,18 @@ from homeassistant.core import HomeAssistant from .adb_session import AdbMediaSessionPoller from .connection import ShieldConnectionManager -from .const import CONF_ADB_ENABLED, DOMAIN -from .helpers import resolve_adb_config +from .const import ( + CONF_ADB_ENABLED, + CONF_ANDROIDTV_ENTITY, + CONF_PROTOCOL_ENABLED, + CONF_PROTOCOL_HOST, + DOMAIN, +) +from .helpers import discover_adb_host, protocol_cert_paths, resolve_adb_config _LOGGER = logging.getLogger(__name__) -PLATFORMS = [Platform.MEDIA_PLAYER, Platform.SENSOR, Platform.BUTTON] +PLATFORMS = [Platform.MEDIA_PLAYER, Platform.SENSOR, Platform.REMOTE, Platform.BUTTON] async def _build_poller_factory(hass: HomeAssistant, entry: ConfigEntry): @@ -69,7 +75,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data[DOMAIN][entry.entry_id] = entry manager = ShieldConnectionManager( - hass, entry, poller_factory=await _build_poller_factory(hass, entry) + hass, + entry, + poller_factory=await _build_poller_factory(hass, entry), + protocol_settings=await _build_protocol_settings(hass, entry), + invalid_auth_callback=lambda: entry.async_start_reauth(hass), ) entry.runtime_data = manager await manager.async_start() @@ -89,6 +99,57 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return True +async def _build_protocol_settings(hass: HomeAssistant, entry: ConfigEntry): + """Manager protocol_settings when the transport is enabled & resolvable. + + Falls back to the androidtv-remote host discovery (the ADB host fallback) + until the user pairs, after which the explicit protocol host persists. + """ + if not entry.options.get(CONF_PROTOCOL_ENABLED, False): + return None + + host = entry.options.get(CONF_PROTOCOL_HOST) or discover_adb_host( + hass, entry.data.get(CONF_ANDROIDTV_ENTITY) + ) + if not host: + _LOGGER.info( + "Protocol enabled for entry %s but no host resolvable — continuing " + "without the remote-protocol transport. Pair via the flow.", + entry.title, + ) + return None + + certfile, keyfile = await hass.async_add_executor_job(protocol_cert_paths, hass) + return { + "host": host, + "client_name": entry.title or "Home Assistant", + "certfile": certfile, + "keyfile": keyfile, + } + + +async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Migrate v1 entries to v2 (adds the remote-protocol fields). + + ADB options carry over untouched; the ADB-host discovery fallback keeps + serving as the protocol host until pairing populates it. Downgrades + (version > 2) are rejected so HA offers a rollback. + """ + if config_entry.version > 2: + return False + if config_entry.version < 2: + options = {**config_entry.options} + options.setdefault(CONF_PROTOCOL_ENABLED, False) + options.setdefault(CONF_PROTOCOL_HOST, "") + hass.config_entries.async_update_entry( + config_entry, options=options, version=2 + ) + _LOGGER.info( + "Migrated %s config entry to version 2", config_entry.title + ) + return True + + async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/custom_components/nvidia_shield/remote.py b/custom_components/nvidia_shield/remote.py new file mode 100644 index 0000000..7f6c9af --- /dev/null +++ b/custom_components/nvidia_shield/remote.py @@ -0,0 +1,19 @@ +"""Remote entities for NVIDIA Shield — Phase 2 step 3 (t10). + +Remotely controlling the Shield runs over the absorbed remote-protocol +transport (androidtvremote2) managed by ShieldConnectionManager. This +placeholder keeps the Platform.REMOTE forward from failing while t10 +lands the real entity on top. +""" + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Remote entities arrive with the Phase 2 command-routing unit (t10)."""