feat(media-player): flatten now-playing into separate attributes and name the tier
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`.
This commit is contained in:
1 parent
46738063aa
commit
a1ce28996e
4 files changed
+543
-47
No files matched your search
@@ -634,11 +634,67 @@ exactly as before.
|
||||
|
||||
| Entity | Type | Description |
|
||||
|--------|------|-------------|
|
||||
| Media Player | `media_player` | Main entity with playback controls and metadata |
|
||||
| Media Player | `media_player` | Main entity with playback controls, metadata and the flat `now_playing_*` attributes (below) |
|
||||
| Current app (`sensor.current_app`) | `sensor` | Currently running app |
|
||||
| Now playing (`sensor.now_playing`) | `sensor` | Formatted now-playing string |
|
||||
| Now playing (`sensor.now_playing`) | `sensor` | Formatted now-playing string, with its own flat attributes (below) |
|
||||
| Pair ADB | `button` | Initiate or re-pair ADB connection |
|
||||
|
||||
### Now-playing attributes
|
||||
|
||||
**Since t65 the media player's now-playing reading is a set of separate
|
||||
TOP-LEVEL attributes**, not one nested object — that object read badly in YAML:
|
||||
|
||||
| Attribute | What it holds |
|
||||
| --- | --- |
|
||||
| `now_playing_via` | which TIER produced the reading: `jellyfin`, `media_session` or `app` |
|
||||
| `now_playing_state` | the reading's own state: `playing` / `paused` / `buffering` / `idle` |
|
||||
| `now_playing_title` | the title — or, in privacy mode, the app identity |
|
||||
| `now_playing_artist`, `now_playing_album` | the session's artist and album, when it has them |
|
||||
| `now_playing_media_type` | the content type the source reported |
|
||||
| `now_playing_series`, `now_playing_season`, `now_playing_episode` | series metadata, when the source reports it |
|
||||
| `now_playing_duration`, `now_playing_position`, `now_playing_position_updated_at` | playback timing |
|
||||
| `now_playing_thumbnail` | the artwork URL (or the Jellyfin entity picture) |
|
||||
| `now_playing_media_info` | the Jellyfin media-info dict, when that entity has one |
|
||||
|
||||
The rules that keep the set predictable:
|
||||
|
||||
- **Only what the tier answered is published.** An unknown field is OMITTED, never
|
||||
`null`, so nothing ever reads as `now_playing_title: null`.
|
||||
- **A device that reads `off` publishes none of them.** A Jellyfin session that has
|
||||
gone stale must not put a title beside a device that is off, and the state
|
||||
already says nothing is playing. `on` (no source reported a power or playback
|
||||
fact) publishes none either — there is no reading to publish.
|
||||
- **`now_playing_via` is deliberately not `source`.** On a media player, `source`
|
||||
and `source_list` mean the selected app input (`Netflix`, `TiviMate`, …) and they
|
||||
still do; naming the tier `source` is exactly what made the reading look like
|
||||
media metadata from somewhere else. `media_dag_role` keeps its own separate
|
||||
meaning (that this entity is the "source" role of the media graph).
|
||||
- **`now_playing_state` IS the entity's own state**, so a dashboard never reads two
|
||||
different answers: the state ladder is the authority, and the foreground tier's
|
||||
last-known-playback bridge can still publish a title while the state is already
|
||||
`idle`.
|
||||
|
||||
**Compatibility: `media_dag_now_playing` is GONE** — not deprecated, removed. It
|
||||
was one object; its contents are the attributes above. No alias is kept: a nested
|
||||
copy would double every poll's payload, and these attributes are declared
|
||||
*unrecorded* for exactly that reason (they must not bloat the recorder database). A
|
||||
template that read the object needs one rename:
|
||||
|
||||
```jinja
|
||||
{# before #}
|
||||
{{ state_attr('media_player.shield', 'media_dag_now_playing').title }}
|
||||
{# after #}
|
||||
{{ state_attr('media_player.shield', 'now_playing_title') }}
|
||||
```
|
||||
|
||||
The Jellyfin-backed `sensor.<entry>_now_playing` keeps its own flat set
|
||||
(`playback_state`, `title`, `media_type`, `series`, `season`, `episode`,
|
||||
`duration`, `position`, `thumbnail`), and it follows the same rule: a field the
|
||||
Jellyfin entity did not answer is omitted, so there are no `title: null` rows
|
||||
there either. Its `playback_state` reads `idle` whenever that session is not
|
||||
playing. `sensor.<entry>_current_app` publishes `app_id`/`app_name`, the same
|
||||
identity the media player carries beside its now-playing attributes.
|
||||
|
||||
## Development
|
||||
|
||||
- **Repository:** [Gitea — git.notexpectedyet.com/notexpectedyet/ha-nvidia-shield](https://git.notexpectedyet.com/notexpectedyet/ha-nvidia-shield), branch `master`
|
||||
|
||||
Reference in new issue
Block a user