refactor: centralize ADB host/key resolution in helpers.py; drop the three duplicated resolvers
This commit is contained in:
1 parent
d3afda0a25
commit
6b4227cb40
4 files changed
+80
-74
No files matched your search
@@ -5,7 +5,6 @@ import logging
|
||||
from homeassistant.components.button import ButtonEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
@@ -18,6 +17,7 @@ from .const import (
|
||||
DEFAULT_ADB_PORT,
|
||||
DOMAIN,
|
||||
)
|
||||
from .helpers import discover_adb_host
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -52,29 +52,11 @@ class NvidiaShieldPairAdbButton(ButtonEntity):
|
||||
model="Shield TV",
|
||||
)
|
||||
|
||||
def _resolve_host(self) -> str | None:
|
||||
"""Resolve ADB host from config or Android TV entity."""
|
||||
host = self._entry.options.get(CONF_ADB_HOST)
|
||||
if host:
|
||||
return host
|
||||
|
||||
atv_entity = self._entry.data.get(CONF_ANDROIDTV_ENTITY)
|
||||
if not atv_entity:
|
||||
return None
|
||||
|
||||
registry = er.async_get(self.hass)
|
||||
entity_entry = registry.async_get(atv_entity)
|
||||
if not entity_entry or not entity_entry.config_entry_id:
|
||||
return None
|
||||
|
||||
config_entry = self.hass.config_entries.async_get_entry(entity_entry.config_entry_id)
|
||||
if config_entry:
|
||||
return config_entry.data.get("host")
|
||||
return None
|
||||
|
||||
async def async_press(self) -> None:
|
||||
"""Attempt ADB connection. The Shield will show a pairing prompt."""
|
||||
host = self._resolve_host()
|
||||
host = self._entry.options.get(CONF_ADB_HOST) or discover_adb_host(
|
||||
self.hass, self._entry.data.get(CONF_ANDROIDTV_ENTITY)
|
||||
)
|
||||
port = self._entry.options.get(CONF_ADB_PORT, DEFAULT_ADB_PORT)
|
||||
|
||||
if not host:
|
||||
|
||||
@@ -24,6 +24,7 @@ from .const import (
|
||||
DEFAULT_NAME,
|
||||
DOMAIN,
|
||||
)
|
||||
from .helpers import discover_adb_host
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -187,23 +188,9 @@ class NvidiaShieldConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
def _resolve_adb_host(self) -> str | None:
|
||||
"""Resolve ADB host from explicit config or the Android TV entity."""
|
||||
host = self._user_data.get(CONF_ADB_HOST)
|
||||
if host:
|
||||
return host
|
||||
|
||||
atv_entity = self._user_data.get(CONF_ANDROIDTV_ENTITY)
|
||||
if not atv_entity:
|
||||
return None
|
||||
|
||||
registry = er.async_get(self.hass)
|
||||
entity_entry = registry.async_get(atv_entity)
|
||||
if not entity_entry or not entity_entry.config_entry_id:
|
||||
return None
|
||||
|
||||
config_entry = self.hass.config_entries.async_get_entry(entity_entry.config_entry_id)
|
||||
if config_entry:
|
||||
return config_entry.data.get("host")
|
||||
return None
|
||||
return self._user_data.get(CONF_ADB_HOST) or discover_adb_host(
|
||||
self.hass, self._user_data.get(CONF_ANDROIDTV_ENTITY)
|
||||
)
|
||||
|
||||
async def async_step_adb_pair(self, user_input=None):
|
||||
"""Step 2: ADB pairing. User clicks Submit after approving on the Shield."""
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Shared config helpers for the NVIDIA Shield integration."""
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .const import (
|
||||
CONF_ADB_HOST,
|
||||
CONF_ADB_PORT,
|
||||
CONF_ANDROIDTV_ENTITY,
|
||||
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 discover_adb_host(
|
||||
hass: HomeAssistant, androidtv_entity_id: str | None
|
||||
) -> str | None:
|
||||
"""Best-effort host for an AndroidTV entity, from its owning config entry."""
|
||||
if not androidtv_entity_id:
|
||||
return None
|
||||
registry = er.async_get(hass)
|
||||
entity_entry = registry.async_get(androidtv_entity_id)
|
||||
if not entity_entry or not entity_entry.config_entry_id:
|
||||
return None
|
||||
config_entry = hass.config_entries.async_get_entry(entity_entry.config_entry_id)
|
||||
if config_entry:
|
||||
return config_entry.data.get("host")
|
||||
return None
|
||||
|
||||
|
||||
def resolve_adb_config(hass: HomeAssistant, entry) -> AdbConfig | None:
|
||||
"""Resolve host/port/key_path for the entry's ADB connection, or None.
|
||||
|
||||
Priority: explicit ADB host/port options > host from the AndroidTV
|
||||
entity's own config entry.
|
||||
"""
|
||||
host = entry.options.get(CONF_ADB_HOST) or discover_adb_host(
|
||||
hass, entry.data.get(CONF_ANDROIDTV_ENTITY)
|
||||
)
|
||||
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))
|
||||
@@ -14,6 +14,7 @@ Intent tracking bridges the gap between instant commands and slow Jellyfin polli
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
from homeassistant.components.media_player import (
|
||||
@@ -40,18 +41,16 @@ from .const import (
|
||||
ATTR_MEDIA_DAG_ROLE,
|
||||
ATTR_MEDIA_DAG_RUNNING_APPS,
|
||||
CONF_ADB_ENABLED,
|
||||
CONF_ADB_HOST,
|
||||
CONF_ADB_PORT,
|
||||
CONF_ADB_STORE_APP_INFO,
|
||||
CONF_ANDROIDTV_ENTITY,
|
||||
CONF_JELLYFIN_ENTITY,
|
||||
CONF_POWER_ENTITY,
|
||||
CONF_RECEIVER_ENTITY,
|
||||
CONF_RECEIVER_INPUT,
|
||||
DEFAULT_ADB_PORT,
|
||||
DOMAIN,
|
||||
)
|
||||
from .ha_adb_common import PlaybackIntent, running_apps_from_tasks
|
||||
from .helpers import adb_key_path, resolve_adb_config
|
||||
from .jellyfin import fetch_session_media_info
|
||||
from .power import discover_power_siblings
|
||||
|
||||
@@ -336,60 +335,38 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
|
||||
return None
|
||||
return self.hass.states.get(entity_id)
|
||||
|
||||
def _discover_adb_host(self) -> str | None:
|
||||
"""Auto-detect ADB host from the androidtv_remote config entry for our entity."""
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
registry = er.async_get(self.hass)
|
||||
entity_entry = registry.async_get(self._androidtv_entity)
|
||||
if not entity_entry or not entity_entry.config_entry_id:
|
||||
return None
|
||||
|
||||
config_entry = self.hass.config_entries.async_get_entry(entity_entry.config_entry_id)
|
||||
if config_entry:
|
||||
return config_entry.data.get("host")
|
||||
return None
|
||||
|
||||
async def _setup_adb(self) -> None:
|
||||
"""Initialize ADB connection and start polling."""
|
||||
try:
|
||||
# Only auto-connect if ADB was previously paired (key exists)
|
||||
import os
|
||||
key_dir = os.path.join(self.hass.config.config_dir, ".storage", DOMAIN)
|
||||
key_file = os.path.join(key_dir, "adbkey")
|
||||
if not await self.hass.async_add_executor_job(os.path.exists, key_file):
|
||||
key_path = adb_key_path(self.hass)
|
||||
if not await self.hass.async_add_executor_job(os.path.exists, key_path):
|
||||
_LOGGER.info(
|
||||
"ADB key not found — skipping auto-connect. "
|
||||
"Use the Pair ADB button or reconfigure the integration."
|
||||
)
|
||||
return
|
||||
|
||||
# Get device IP: explicit config > auto-detect from androidtv_remote
|
||||
host = self._entry.options.get(CONF_ADB_HOST)
|
||||
port = self._entry.options.get(CONF_ADB_PORT, DEFAULT_ADB_PORT)
|
||||
|
||||
if not host:
|
||||
host = self._discover_adb_host()
|
||||
|
||||
if not host:
|
||||
_LOGGER.warning("Could not determine ADB host. Set it in integration options.")
|
||||
adb = resolve_adb_config(self.hass, self._entry)
|
||||
if adb is None:
|
||||
_LOGGER.warning(
|
||||
"Could not determine ADB host. Set it in integration options."
|
||||
)
|
||||
return
|
||||
|
||||
key_path = key_file
|
||||
self._adb_poller = AdbMediaSessionPoller(host, port, key_path)
|
||||
self._adb_poller = AdbMediaSessionPoller(adb.host, adb.port, adb.key_path)
|
||||
|
||||
if await self._adb_poller.connect():
|
||||
# Start periodic polling
|
||||
self._adb_poll_unsub = async_call_later(
|
||||
self.hass, ADB_POLL_INTERVAL, self._adb_poll_callback
|
||||
)
|
||||
_LOGGER.info("ADB media session polling started for %s", host)
|
||||
_LOGGER.info("ADB media session polling started for %s", adb.host)
|
||||
else:
|
||||
_LOGGER.warning(
|
||||
"ADB connection to %s failed. If this is the first connection, "
|
||||
"approve the ADB debugging prompt on the device. "
|
||||
"Retrying in %d seconds.",
|
||||
host, ADB_RETRY_INTERVAL,
|
||||
adb.host, ADB_RETRY_INTERVAL,
|
||||
)
|
||||
self._adb_poll_unsub = async_call_later(
|
||||
self.hass, ADB_RETRY_INTERVAL, self._adb_poll_callback
|
||||
|
||||
Reference in new issue
Block a user