From 0cfa045190ac67ce36977d302ee1ab3b0d39637e Mon Sep 17 00:00:00 2001 From: James Mackay Date: Sun, 11 Oct 2026 06:49:44 +0100 Subject: [PATCH] docs(readme): the release write-back and the command scripts (t44 3, R2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Releases: the section described the manifest/date fallback only, and pointed at `.github/workflows/release.yml`, which does not exist. It now covers the same-day `vYYYY.MM.DD.N` suffix (lowest free N, gaps reused), the manifest write-back commit and WHY it exists (HACS compares against the default-branch manifest, so a tag-only release is offered to nobody — v2026.10.09 shipped that way), what `[skip ci]` means and what it does not (release commits only; Gitea's own handling can still skip an ordinary commit whose body mentions the marker), the refusal to publish when the default branch disagrees, and the branch-protection failure path. t44 R2, both halves of the tag's identity: with a write-back the tag names the bot commit, so `git describe --tags` on a developer commit reports the previous release (expected, not a missing tag); hand-bumping the manifest keeps the tag on the developer's own commit, because the workflow then has nothing to write back. Command scripts: a new section under Options, with the command/variable table (`level` for volume_set, `is_volume_muted` for volume_mute, `app_name` for select_source), the "instead of, never both" rule, the fact that an unset selector means the native path, and a copy-pasteable script block covering the three cases the user asked for — a speaker switch owning the volume, an AVR owning the input, a mute that hits the switch too. It says plainly that scripts are set on the Configure page and not in the YAML seed, which stays address + transport only (the seed example is unchanged and still the only `nvidia_shield:` block). Verified: 414 passed / 2 skipped, 415 / 1 with androidtvremote2, test_config_flow_ux 61, test_standalone_setup + test_config_flow_protocol 58, ruff clean, drift engine exit 0 under real HA, and every fenced YAML block in the README parses with pyyaml. --- README.md | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fbb6739..934451f 100644 --- a/README.md +++ b/README.md @@ -188,12 +188,34 @@ full retirement mapping this section summarises. ## Releases -- **Date-based versioning**: `vYYYY.MM.DD` (e.g. `v2026.10.08`). +- **Date-based versioning**: `vYYYY.MM.DD`, and `vYYYY.MM.DD.N` (N from 2) for a + second release in the same UTC day. - Pushes to `master` on [Gitea](https://git.notexpectedyet.com/notexpectedyet/ha-nvidia-shield) - run the release workflow (`.github/workflows/release.yml`): - - A plain push publishes the current day's release (same-day pushes are deduped). - - Bumping `version` in `custom_components/nvidia_shield/manifest.json` forces that - specific version's release before the date fallback kicks in. + run the release workflow (`.gitea/workflows/release.yml`): + - A plain push publishes today's release. If that date is already tagged it takes + the lowest free `.N`, so a gap left by an earlier failure is reused rather than + skipped. + - Setting `version` in `custom_components/nvidia_shield/manifest.json` to an + untagged value releases exactly that version instead. +- **The release commit.** When the resolved version differs from the manifest, the + workflow writes it back, commits `chore(release): v [skip ci]`, pushes it + to `master`, and tags *that* commit. It has to do this: HACS compares the installed + version against the manifest on the default branch, so a release that only ever + existed as a tag is offered to nobody (v2026.10.09 shipped that way). + - Because the tag names the bot commit, `git describe --tags` on a *developer* + commit reports the **previous** release. That is expected, not a missing tag. + - Bump it by hand instead and the tag lands on your own commit: set `version` in + the manifest, commit, push. The workflow then has nothing to write back (it sees + an untagged manifest version and tags your commit). +- **`[skip ci]`**: the bot commit carries it, and the workflow's job additionally + checks the commit *subject* (`chore(release): …`), so a write-back cannot start a + second release. Both only recognise release commits — a normal commit whose message + body merely mentions `[skip ci]` can still be skipped by Gitea's own handling, so + keep the marker out of ordinary commit messages. +- The workflow refuses to publish unless the default branch's manifest already carries + the version it is about to tag, and fails loudly (with a "bump the manifest by hand" + instruction and nothing tagged) if the write-back push is rejected, for example by + branch protection. - Releases land on Gitea with the tag and a `nvidia_shield.zip` asset for manual installs. (No automatic mirroring — see the HACS note below.) @@ -264,6 +286,73 @@ 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 (per-command overrides) + +Some commands need hardware this integration deliberately does not drive: a speaker +switch that owns the volume, an AVR input that has to move with the Shield, a mute +that has to hit two devices. For those, the Configure page takes a **script per +command**: + +| Command | Runs when | Variables passed | +| --- | --- | --- | +| `turn_on`, `turn_off` | turn on/off, from the media player *or* the remote | — | +| `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` | + +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. + +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 +has to hit the switch as well. + +```yaml +script: + shield_volume: # select as "Volume set script" + alias: Shield volume (speaker switch) + sequence: + - action: media_player.volume_set + target: + entity_id: media_player.living_room_speaker + data: + volume_level: "{{ level }}" + shield_source: # select as "Select source script" + alias: Shield source (AVR) + sequence: + - action: media_player.select_source + target: + entity_id: media_player.avr + data: + source: "{{ app_name }}" + shield_mute: # select as "Mute script" + alias: Shield mute (speaker switch) + sequence: + - choose: + - conditions: "{{ is_volume_muted }}" + sequence: + - action: switch.turn_off + target: + entity_id: switch.living_room_speaker + default: + - action: switch.turn_on + target: + entity_id: switch.living_room_speaker +``` + +Then set `script.shield_volume` as **Volume set script**, `script.shield_source` as +**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. ## Entities