The user reads attributes as YAML — "These could do with being separate
attributes rather than an object in one?" — so the nested
`media_dag_now_playing` object is gone and every field it carried is its own
top-level attribute:
now_playing_via, now_playing_state, now_playing_title, now_playing_artist,
now_playing_album, now_playing_media_type, now_playing_series,
now_playing_season, now_playing_episode, now_playing_duration,
now_playing_position, now_playing_position_updated_at, now_playing_thumbnail,
now_playing_media_info
Rules the surface now holds to:
- an unknown field is OMITTED, never published as None (`title: null` inside the
object was exactly the noise this removes); 0 and False are real values
- a device reading `off` publishes no now-playing row at all — a Jellyfin session
that has gone stale must not put a title beside a device that is off — and `on`
(nothing reported) publishes none either, because there is no reading
- `now_playing_state` IS the entity's own state, so the ladder stays the single
answer even while the foreground tier's last-known bridge serves a title
- the tier indicator is `now_playing_via` (`jellyfin` / `media_session` / `app`),
NOT `source`: on a media_player `source`/`source_list` mean the selected app
input (`Netflix`, `TiviMate`, …) and they still do — a tier named `source` is
what made the reading look like media metadata from somewhere else. The internal
reading key is renamed `via` as well, and the Jellyfin tier now names itself too
- every published name is declared unrecorded (G12): these change every poll
COMPATIBILITY: `media_dag_now_playing` is removed, not deprecated, and no alias is
kept — a nested copy would double every poll's payload. README documents the exact
names, these rules, the sensor's own flat set and a one-line template migration.
sensor.py: the Jellyfin now-playing sensor follows the same no-None rule (`title`
and `media_type` used to be published as null there).
Tests: 17 new cases pin the flattening, the tier rename, the five states, the
off/on gate, the playing->idle transition, the last-known bridge, privacy mode and
agreement with the Current App sensor; the tier-dict tests follow `via`.