Number keys: the keyboard's digits work like the remote's; a key can hold one song

The IR remote's digits and the kiosk keyboard's number keys now both emit
PlayRemoteSlotRequested, and one reaction resolves the slot for either (a podcast
show to its newest episode, an album to the album). POST /api/remote/press/{digit}
is the keyboard's way in. Mid-search a digit is still part of the query.

A remote slot can start one song instead of a whole album: `track_index` on an
album slot plays from that track, then the rest of the album. Shift+A on the play
screen arms "this song" where A arms "this album", and the assign popup offers
both. A series slot cannot name a song, and an index past the album's end is
rejected when saving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 19:41:34 +02:00
parent 92305bece0
commit 459599d2ba
22 changed files with 449 additions and 75 deletions

View File

@@ -96,6 +96,24 @@ def test_remote_mapping_rejects_an_unknown_target_kind(config_dir: Path) -> None
assert "remote" in message
def test_a_remote_slot_can_name_a_song(config_dir: Path) -> None:
data = copy.deepcopy(VALID_CONFIG)
data["remote"] = {
"1": {"target_kind": "album", "target": "abc123", "track_index": 4},
"2": {"target_kind": "album", "target": "abc123"},
}
config = load_config(write_config(config_dir, data))
assert config.remote["1"].track_index == 4
assert config.remote["2"].track_index == 0
def test_a_series_slot_cannot_name_a_song(config_dir: Path) -> None:
data = copy.deepcopy(VALID_CONFIG)
data["remote"] = {"7": {"target_kind": "series", "target": "Wissen", "track_index": 1}}
message = _error(config_dir, data)
assert "track_index" in message
def test_ha_device_and_scene_name_is_optional(config_dir: Path) -> None:
data = _config(
ha={