From 1c6c223f27468e8f947beb16a5c498ff4750b457 Mon Sep 17 00:00:00 2001 From: James Mackay Date: Sun, 11 Oct 2026 07:07:36 +0100 Subject: [PATCH] docs(readme): the app list as sources, the remote command script, current versioning (t47 D) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit t46 F2: the status line still cited `v2026.10.08`. It now describes the scheme (`vYYYY.MM.DD`, plus `vYYYY.MM.DD.N` for a second release in the same day) and links to the Releases section, so it cannot go stale again. The other dated reference stays: "v2026.10.09 shipped that way" is the historical example that motivated the write-back, and it is accurate. New "Sources (the app list in a dropdown)" section: source_list from the Apps map (with the built-in fallback named, so a card is never empty), `source` resolved exactly like the Current App sensor, and `media_player.select_source` by name — accepting a raw package id and any case, erroring on an unknown name — with a copy-pasteable service call. It also points at the Select source script as the override for the "more than launching an app" case. New "Remote command script" subsection: the four variables in a table (command, num_repeats, delay_secs, hold_secs) with their meanings and defaults, and a copy-pasteable script that forwards the call to an IR bridge. It states that all four are always present including defaults, and that unset changes nothing. Corrected the two claims UNIT A invalidated: the command table no longer says select_source has no native path, and the paragraph no longer says it is offered only while a script is configured. Every fenced YAML block in the README still parses with a real parser (5 now), and tests/test_readme.py is green. --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 934451f..15350ff 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,9 @@ This is a **Shield-only fork** of [ltomes/ha-nvidia-shield](https://github.com/l ADB as the state source, and no dependency on separate `androidtv` / `androidtv_remote` integrations. -> **Status:** early fork, actively rebuilt. Releases are date-based (`v2026.10.08`). +> **Status:** early fork, actively rebuilt. Releases are date-based +> (`vYYYY.MM.DD`, plus `vYYYY.MM.DD.N` for a second release in the same day) — +> see [Releases](#releases) for how a push becomes one. > Expect rough edges until the MVP phases land. The build-out plan is in > [`docs/MVP_PLAN.md`](docs/MVP_PLAN.md); device research is in > [`docs/RESEARCH_SHIELD.md`](docs/RESEARCH_SHIELD.md). @@ -286,7 +288,8 @@ helper the flow calls is a coroutine and its dedup only runs when awaited. transport on or off - **Shield address / ADB port** — the Shield's IP, and the ADB port if you changed it from 5555 -- **Command scripts** — one optional script per command; see below +- **Command scripts** — one optional script per command, plus one for the remote + entity; see below ### Command scripts (per-command overrides) @@ -301,13 +304,14 @@ command**: | `volume_up`, `volume_down` | volume step | — | | `volume_set` | absolute volume (there is no native path — the protocol has VOLUME_UP/DOWN/MUTE only) | `level` | | `volume_mute` | mute/unmute | `is_volume_muted` | -| `select_source` | source selection (no native path) | `app_name` | +| `select_source` | source selection (the app list drives it natively; a script overrides) | `app_name` | A command with a script calls the script **instead of** the protocol/ADB path, never both. Left unset — the default — every command behaves exactly as it did before, and -clearing a selector on the Configure page restores that. `volume_set` and -`select_source` only appear as services while their script is configured, because -there is nothing native to fall back on. +clearing a selector on the Configure page restores that. Two of the seven appear as +services only when there is something to do: `volume_set`, which has no native path at +all, and `select_source`, which is offered once the integration has sources to offer +(the app list below) — or a script. The scripts are ordinary Home Assistant scripts. This block covers all three awkward cases: a speaker switch owning the volume, an AVR owning the input, and a mute that @@ -350,9 +354,77 @@ Then set `script.shield_volume` as **Volume set script**, `script.shield_source` **Select source script** and so on on the Configure page. Scripts are set there, not in the YAML seed above — the seed only carries the address and the transport choice. -A script that fails surfaces as an error on the service call that triggered it (the -command is not retried over protocol/ADB), because a silent no-op is the one outcome -that leaves you guessing. +A script that fails surfaces as a translated error on the service call that triggered +it (the command is not retried over protocol/ADB), because a silent no-op is the one +outcome that leaves you guessing. + +### Sources (the app list in a dropdown) + +The media player's source list is your **Apps** map — the same map the Browse tree and +app-name resolution use. With no map configured it falls back to a small built-in list +(TiviMate, Wholphin, Netflix, YouTube), so a universal-media-player card always has +something to show: + +```yaml +# a media-player card, or anything that reads source_list +type: media-control +entity: media_player.living_room_shield +``` + +- **`source_list`** — the app names. They come from the map (in the order it lists + them), so editing the map renames the dropdown entries. +- **`source`** — the app currently on screen, resolved exactly like the Current App + sensor: your map first, then the cached device label, then the raw package. +- **`media_player.select_source`** — picks one **by name** and launches it over the + protocol, the same path `play_media` uses for apps. A raw package id works too (that + is what the Browse tree publishes), names match case-insensitively, and a name that + is not in the list is an error rather than a silent no-op: + +```yaml +action: media_player.select_source +target: + entity_id: media_player.living_room_shield +data: + source: TiviMate +``` + +Selecting a source is sometimes more than launching an app — an AVR input, a speaker +switch. That is what the **Select source script** above is for: with one configured it +replaces the native launch entirely. + +### Remote command script + +`remote.send_command` sends protocol keys: repeats, long presses (`START_LONG`/ +`END_LONG`) and inter-repeat delays. Some setups need that call to go somewhere else — +an IR bridge, a macro, a second device — so one optional **Remote command script** +replaces the whole key path when it is set. It receives the service's own parameters, +named as they are on the service: + +| Variable | Meaning | +| --- | --- | +| `command` | the list of key commands as submitted, e.g. `["HOME", "KEYCODE_DPAD_CENTER"]` | +| `num_repeats` | how many times to send them (default `1`) | +| `delay_secs` | the delay between repeats in seconds (default `0.0`) | +| `hold_secs` | the requested long-press duration (default `0.0`) — the script owns the key semantics, so it decides what "hold" means | + +```yaml +script: + shield_remote: # select as "Remote command script" + alias: Shield remote (IR bridge) + sequence: + - action: remote.send_command + target: + entity_id: remote.living_room_bridge + data: + command: "{{ command }}" # a list: pass it through + num_repeats: "{{ num_repeats }}" + delay_secs: "{{ delay_secs }}" + hold_secs: "{{ hold_secs }}" +``` + +All four variables are always present, including their defaults, so a script can rely +on the shape. Unset — the default — nothing changes: keys go out over the protocol +exactly as before. ## Entities