fix: the deprecated listener+reload combo is gone — reauth uses async_update_reload_and_abort(_get_reauth_entry()), zeroconf host update is update-only, the __init__ options-reload listener is removed (grep-pinned); host-change tests assert update-without-reload (t29 G3a)

This commit is contained in:
James Mackay committed 2026-10-09 07:02:21 +01:00
1 parent 3e0208789a
commit bcb8b962e7
6 files changed
+158 -50

No files matched your search

+25 -49
View File
@@ -34,6 +34,7 @@ from homeassistant.components.media_player import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import STATE_PAUSED, STATE_PLAYING
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_call_later, async_track_state_change_event
@@ -62,7 +63,6 @@ from .const import (
DOMAIN,
PROTOCOL_MEDIA_KEYS,
PROTOCOL_REMOTE_KEYS,
SERVICE_ADB_COMMAND,
TIVIMATE_PACKAGE,
)
from .ha_adb_common import PlaybackIntent, running_apps_from_tasks
@@ -243,7 +243,6 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
getattr(self, "entity_id", None) or self._attr_unique_id
)
_ADB_COMMAND_TARGETS[self._adb_command_key] = self._manager
_adb_command_service_ensure(self.hass)
# Discover power sensor siblings from the power switch
if self._power_entity:
@@ -280,13 +279,8 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
for task in self._entity_tasks:
task.cancel()
self._entity_tasks.clear()
# P4-2a: drop this entity's service participation (same key as add)
if (
(key := getattr(self, "_adb_command_key", None)) is not None
and _ADB_COMMAND_TARGETS.pop(key, None) is not None
and self.hass
):
_adb_command_service_maybe_remove(self.hass)
# P4-2a/t29 G8: drop this entity's service participation (same key
# as add). The service itself stays registered (always-on setup).
# ── Intent management ──
@@ -1076,53 +1070,35 @@ _ADB_COMMAND_TARGETS: dict[str, ShieldConnectionManager] = {}
_ADB_COMMAND_SCHEMA = vol.Schema(
{
vol.Required("command"): str,
vol.Optional("entity_id"): str,
vol.Required("entity_id"): str,
}
)
async def _async_adb_command_service(call) -> None:
"""Run a raw ADB shell command on the targeted entry's connection."""
"""Run a raw ADB shell command on the target connection (t29 G8).
The integration's async_setup registers this ALWAYS; the entry's
adb_command_enabled option gates USE (participants register from
async_added_to_hass). Failures raise ServiceValidationError with
translated keys (action-exceptions) — never warning logs.
"""
command = str(call.data.get("command") or "").strip()
if not command:
_LOGGER.warning("adb_command: empty command ignored")
return
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="invalid_command"
)
entity_id = call.data.get("entity_id")
pairs = [
(eid, manager)
for eid, manager in _ADB_COMMAND_TARGETS.items()
if entity_id in (None, eid)
]
if not pairs:
_LOGGER.warning(
"adb_command: no participating entry matches %s (is the option "
"enabled on the entry's options?)",
entity_id,
manager = _ADB_COMMAND_TARGETS.get(entity_id)
if manager is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="unsupported_target",
)
return
for _eid, manager in pairs:
poller = manager._poller
if poller is None or not manager.connected:
_LOGGER.warning("adb_command: ADB unavailable — command not sent")
continue
result = await poller.shell(command)
_LOGGER.debug("adb_command %r → output=%s", command, result is not None)
def _adb_command_service_ensure(hass) -> None:
"""Register the domain service once the first participant appears."""
if not hass.services.has_service(DOMAIN, SERVICE_ADB_COMMAND):
hass.services.async_register(
DOMAIN,
SERVICE_ADB_COMMAND,
_async_adb_command_service,
schema=_ADB_COMMAND_SCHEMA,
poller = manager.adb_poller # public accessor (t21 F6)
if poller is None or not manager.connected:
raise ServiceValidationError(
translation_domain=DOMAIN, translation_key="adb_unavailable"
)
def _adb_command_service_maybe_remove(hass) -> None:
"""Unregister when the last participant unloads."""
if not _ADB_COMMAND_TARGETS and hass.services.has_service(
DOMAIN, SERVICE_ADB_COMMAND
):
hass.services.async_remove(DOMAIN, SERVICE_ADB_COMMAND)
result = await poller.shell(command)
_LOGGER.debug("adb_command %r → output=%s", command, result is not None)