The t59 F2 ruling, implemented. The ladder said ON was for "no source reported
anything at all" but tested for something looser — no transport data — so a
source that ANSWERED "not playing" still fell through to ON. A source that
answers is not a source that is silent, and `on` is the "we cannot know" state.
The ladder now tracks an explicit `reported` flag, set by every source that gave
us a power or playback fact, and the last tier is `IDLE if reported else ON`.
Each case the ruling names now lands on IDLE:
* a configured Jellyfin entity in ANY state — idle, off, standby, unavailable,
or even absent from the state machine. It reports "not playing" without an
early return, so a silent Jellyfin still cannot mask a live media session
below it (both directions pinned);
* a live media session that carries no playback flag — the app publishes a
session but is not playing, paused or buffering;
* a bare foreground app: either transport's answer counts (the protocol's
current_app push or the ADB mResumedActivity parse);
* a configured power entity, whatever it currently says;
* protocol is_on / ADB awake, plugged or not.
`on` survives only for an entry where none of those exists. The t55 rules are
untouched: an off power entity still outranks a playing session, and no source
may mask playing/paused/buffering.
The module docstring's tier 6, the `state` docstring and the README's ladder
(moving "no data" to the precise wording) all say the same thing now, and the
README's one-line state table was aligned with it.
RED PROOF (the new tests against the parent tree): five of the reporting cases
read `on` where they must read `idle` — `assert 'on' == 'idle'` — plus the
"ON is unreachable with any source configured" shape test.
SIMULATED vs EXECUTED: no real Shield was available, so these are the documented
source shapes, not captures; the state=6 → buffering mapping stays marked
inferred. No browser exists either, so the README pins are textual and the
production behaviour is exercised against real HA's state machine in
tests/test_ha_api_drift.py.
The README now answers the question a migrator actually has: what do I delete,
and what covers it afterwards.
* a new "Replacing androidtv / androidtv_remote + the glue" section: the
back-up → remove-the-platform-YAML → remove-mediasession2mqtt → delete the old
integrations → add this one → update automations checklist, plus a table
mapping every retired piece to what serves it now. It notes that Wholphin
publishes no MediaSession of its own and gets its now-playing from the ADB
media session, so no bridge is needed for it either;
* the setup steps describe the real flow (name + one address, both transports on
by default, ADB pairing then PIN pairing, each with a skip);
* the YAML seed section carries a complete, copy-pasteable block. It documents
ONLY the keys `_yaml_seed_blocks` honours (name, host, adb_port, adb_enabled,
and the still-accepted protocol_host/adb_host) and says explicitly that the
rest lives on the Configure page — an example that lists ignored keys is worse
than no example;
* the state-ladder section, the discovery bullet and the options list follow the
two-transport, one-address reality.
`tests/test_readme.py` pins the doc/code contract: the example's keys are a
subset of what the seed reads, the example uses the current `host` key, and the
guide keeps naming the retired integrations and glues.
Verified: 326 passed / 2 skipped, ruff clean.