The two address fields were the same Shield spelled twice, and every bug in the
zeroconf/IP-change path came from keeping them in sync. One field now:
* const.CONF_HOST replaces CONF_ADB_HOST / CONF_PROTOCOL_HOST in the setup form,
the options page, the pairing forms, the reconfigure/reauth seeding and the
description copy. ADB port 5555 stays the only port field;
* helpers.entry_host(entry) is the single resolver (options first, then data);
resolve_adb_config and the protocol settings both go through it, so both
transports read the same value at call time (the t32 F1 factory re-read keeps
working: the NEXT built poller sees the new address);
* helpers.seed_host / seed_adb_enabled own the YAML seed's shape rule. A block
may use `host` or the legacy protocol_host/adb_host keys — t41 keeps the old
spelling working — and t32 F4's outcome is unchanged: a protocol-only block
leaves ADB off, an address (or an explicit adb_enabled) turns it on;
* the zeroconf IP-change path collapses the t21-F2 per-host dance into one rule:
the discovery address IS the entry's address, so the entry is updated when it
differs and left alone when it matches. The blank-self-heal and
deliberately-different-ADB-host cases are gone with the duplication — a
second address field cannot express them anymore, and the tests now pin that
(previously "an explicit override is never clobbered");
* the options page exposes the protocol toggle it never had (the page could turn
ADB off but not the protocol);
* button.py (granted to this task) reads the entry's own address instead of
another integration's entity.
The v3 entry migration that maps existing adb_host/protocol_host onto CONF_HOST
and drops the androidtv key lands in the next commit; HA runs migrations before
setup, so an existing entry is never read with the old shape.
Verified: 311 passed / 2 skipped, 312 / 1 with androidtvremote2, ruff clean,
drift engine exit 0 on HEAD. The UI-copy contract tests (every rendered field
has a label in strings.json AND translations/en.json, key trees identical, host
errors name the field) drove the string edits.
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.