fix(availability): F3 — any usable transport keeps the entities up (t40)

Reported from a live install: with ADB enabled but unreachable and a paired,
healthy protocol transport, the media player went unavailable even though the
protocol still served power/app/volume.

The old rule was ``manager.connected or manager.awake is not None or not
manager.available`` — i.e. "a configured ADB manager that is down ⇒ the whole
entity is down" (t5-F2), which ignored transport B entirely. The ladder is now:

* protocol transport wired and not reported unreachable — usable;
* or ADB connected — usable;
* or fresh wake data (the poller has spoken) — usable;
* or a passive manager (no ADB configured at all) — usable, because nothing to
  transport is not a broken transport.

Unavailable only when a CONFIGURED transport is actively down and no usable data
exists. One implementation, ``entity_available`` (media_player.py), is used by the
media player, the remote (whose ladder previously duplicated a weaker version of
this rule) and the diagnostics payload.

Diagnostics grew ``transport.entity_available`` plus ``adb.configured`` so a
support dump answers "why is my entity unavailable?" — the ladder inputs and its
verdict are now in the same place.

Pinned by tests: the exact user case (ADB configured+down, protocol up ⇒
available), all-down (⇒ unavailable), wake-data rescue, connected ADB without a
protocol, the passive manager, a configured-but-down transport with no data, the
remote following the same ladder, and the diagnostics verdict.

Verified: 310 passed / 2 skipped, 311 passed / 1 skipped (androidtvremote2),
16 passed in the HA drift suite, ruff clean.
This commit is contained in:
James Mackay committed 2026-10-11 06:02:46 +01:00
1 parent dd23d01a88
commit 60d884b76a
4 files changed
+144 -17

No files matched your search

@@ -19,6 +19,7 @@ from homeassistant.core import HomeAssistant
from homeassistant.helpers.redact import async_redact_data
from .connection import ShieldConnectionManager
from .media_player import entity_available
_REDACT_KEY_MARKERS = ("pairing", "pin", "secret", "password", "token", "key_material")
_REDACTED = "**REDACTED**"
@@ -81,7 +82,14 @@ async def async_get_config_entry_diagnostics(
if isinstance(manager, ShieldConnectionManager):
payload["store"] = manager.store_snapshot()
payload["transport"] = {
"adb": {"connected": manager.connected, "awake": manager.awake},
# t40: the ladder's verdict, from the same helper the entities use,
# so a support dump answers "why is my entity unavailable?".
"entity_available": entity_available(manager),
"adb": {
"configured": manager.available,
"connected": manager.connected,
"awake": manager.awake,
},
"protocol": {
"active": manager.protocol_active,
"available": manager.is_available,