fix(media_player,config_flow): close the batch review's O2 and O3 (t17)
Both observations from the t8-t12 batch review, settled by execution rather than
by argument.
O2 — WHICH STATES MAY CARRY A NOW-PLAYING ROW. The review read
`extra_state_attributes` and argued the row publishes the entity state verbatim,
so `now_playing_state: on` is reachable, while the README documents only
playing/paused/buffering/idle; t10's author had reported that `on` and `off`
publish no row at all. The probe settles both halves:
* the t10 account is true on the NATURAL ladder — `on` means no source reported a
power or playback fact, and every tier that can produce a reading sets
`reported = True` in the ladder, so the two cannot meet;
* the review is right about the RULE. It was "publish unless the state is off", an
assumption nothing enforced: forcing the ladder to `on` (and to `standby`, which
the README does not even mention) published `now_playing_state: on`/`standby`
beside a real title.
So the rule is now explicit — `NOW_PLAYING_STATE_VALUES = ("playing", "paused",
"buffering", "idle")` — and a reading is dropped whole when the entity state is
anything else, exactly as `off` always dropped it. Nothing legitimate is lost: a
tier only ever answers in one of those four (`idle` for "reported, nothing
playing", which is where the foreground tier's remembered-playback bridge lands).
The README says the same thing in the table row and in prose, and
tests/test_now_playing_publication.py walks EVERY `MediaPlayerState` member with a
reading present (the four publish themselves; on/off/standby publish nothing),
then reads the four values back OUT OF THE README and compares them to the
constant, so code and docs cannot drift again.
O3 — A REJECTED APP PASTE NAMES ITS BAD LINES. The bulk paste was all-or-nothing
and reported a generic `invalid_app_map`, so a 30-row paste with one typo had to
be bisected by hand. `_app_map_parse` now returns `(map | None, problems)` where
each problem carries the 1-based line NUMBER, the line as the user typed it, and
why (empty/space-bearing package, empty name, space in the icon), and the
rejection puts them in the form's `description_placeholders["invalid_lines"]`,
which the error copy interpolates — capped at 4 named lines with the remaining
count, so the message stays readable. The map is still None when anything is
wrong (never half-stored) and the detail is cleared as soon as a paste parses.
The single-row editor is untouched and keeps its own `invalid_app_row`; a test
asserts the new per-line report and `_valid_app_row` accept and refuse the same
rows, so a paste and a typed row cannot disagree.
Verify: 794 passed / 4 skipped, 795 passed / 3 skipped (with androidtvremote2),
ruff clean.
This commit is contained in:
1 parent
91d9fb3883
commit
2819ee5a49
7 files changed
+489
-33
No files matched your search
@@ -221,6 +221,22 @@ NOW_PLAYING_READING_KEYS = (
|
||||
"media_info",
|
||||
)
|
||||
|
||||
#: The state values a now-playing ROW may carry, and the whole documented set
|
||||
#: (t17/O2). A reading comes from a tier that answered, and a tier answers in one
|
||||
#: of these four: ``playing``/``paused``/``buffering`` from a live session or
|
||||
#: Jellyfin, ``idle`` for a source that reported "nothing is playing" (including
|
||||
#: the foreground tier's remembered-playback bridge, which is why the ROW's state
|
||||
#: is the LADDER's answer and not the tier's own label).
|
||||
#:
|
||||
#: Anything else is deliberately NOT publishable. ``off`` means the device is off
|
||||
#: (a stale session must not put a title beside it), ``on`` means nothing anywhere
|
||||
#: reported a power or playback fact — so there is no reading to describe — and
|
||||
#: ``standby`` is deprecated and never leaves the ladder. The gate used to be
|
||||
#: "not off", which published whatever the entity state said; the README documents
|
||||
#: four values, so the code now publishes exactly those four and a test walks
|
||||
#: EVERY ``MediaPlayerState`` member against the README to keep the two in step.
|
||||
NOW_PLAYING_STATE_VALUES = ("playing", "paused", "buffering", "idle")
|
||||
|
||||
|
||||
def now_playing_attribute_names() -> frozenset[str]:
|
||||
"""The flat now-playing attribute names — the whole published surface (t65)."""
|
||||
@@ -1365,6 +1381,12 @@ class NvidiaShieldMediaPlayer(MediaPlayerEntity):
|
||||
if self.state != MediaPlayerState.OFF:
|
||||
state = self.state.value
|
||||
reading = self._now_playing()
|
||||
# t17/O2: only the four states a READING can carry are published, so
|
||||
# a state outside the documented set can never leak into
|
||||
# ``now_playing_state`` (see NOW_PLAYING_STATE_VALUES). The reading is
|
||||
# dropped whole, exactly as it is for a device that reads off.
|
||||
if state not in NOW_PLAYING_STATE_VALUES:
|
||||
reading = None
|
||||
if reading:
|
||||
# The published state is the ENTITY's own state, so a dashboard
|
||||
# never reads two answers: the tier can briefly serve a
|
||||
|
||||
Reference in new issue
Block a user