diff --git a/README.md b/README.md index bb57b1e..8b46f3b 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,14 @@ TV Remote entity, the Nvidia Remote Protocol (PIN pairing), or ADB media-session mode (ADB enabled + host). The `no_state_source` form error fires only when none of the three is present; ADB-only setups are valid. +### Channel entry options + +The options flow exposes the §7 channel behavior the integration replicates: +the inter-digit **delay** (0.05–1.0 s, default 0.1) and the optional +**ENTER press** after the digits (off by default — TiviMate's validated +behavior). Both are consumed by `play_media` with +`media_content_type: channel`. + ### Raw `adb_command` service (opt-in) When enabled in the integration's options (`adb_command_enabled`, **default diff --git a/custom_components/nvidia_shield/config_flow.py b/custom_components/nvidia_shield/config_flow.py index f2851a7..8ebc4f4 100644 --- a/custom_components/nvidia_shield/config_flow.py +++ b/custom_components/nvidia_shield/config_flow.py @@ -13,6 +13,7 @@ from .adb_session import ensure_adb_key from .connection import ShieldConnectionManager from .const import ( ATTR_MEDIA_DAG_INPUT_MAP, + CHANNEL_DIGIT_DELAY, CONF_ADB_COMMAND_ENABLED, CONF_ADB_ENABLED, CONF_ADB_HOST, @@ -20,6 +21,8 @@ from .const import ( CONF_ADB_STORE_APP_INFO, CONF_ANDROIDTV_ENTITY, CONF_APP_NAME_MAP, + CONF_CHANNEL_DIGIT_DELAY, + CONF_CHANNEL_SEND_ENTER, CONF_JELLYFIN_ENTITY, CONF_PAIRING_CODE, CONF_POWER_ENTITY, @@ -497,6 +500,16 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlow): """Single-page options.""" errors = {} if user_input is not None: + # P5-2: validate the digit delay (0.05–1.0 s, §7 window) + delay = user_input.pop(CONF_CHANNEL_DIGIT_DELAY, None) + if delay is not None: + try: + delay = float(delay) + except (TypeError, ValueError): + errors["base"] = "invalid_digit_delay" + delay = None + if delay is not None and not (0.05 <= delay <= 1.0): + errors["base"] = "invalid_digit_delay" app_map_text = user_input.pop(CONF_APP_NAME_MAP, None) if app_map_text is None: app_map = self._options.get(CONF_APP_NAME_MAP, {}) @@ -507,6 +520,8 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlow): if app_map is None: errors["base"] = "invalid_app_map" if not errors: + if delay is not None: + self._options[CONF_CHANNEL_DIGIT_DELAY] = delay self._options.update(user_input) self._options[CONF_APP_NAME_MAP] = app_map return self.async_create_entry(title="", data=self._options) @@ -537,6 +552,12 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlow): recv_key = vol.Optional(CONF_RECEIVER_ENTITY, default=recv) schema_dict[recv_key] = sel["receiver"] + # P5-2: channel digit behavior (the user-validated §7 flow) + current_delay = self._options.get(CONF_CHANNEL_DIGIT_DELAY, CHANNEL_DIGIT_DELAY) + schema_dict[vol.Optional(CONF_CHANNEL_DIGIT_DELAY, default=current_delay)] = str + current_enter = self._options.get(CONF_CHANNEL_SEND_ENTER, False) + schema_dict[vol.Optional(CONF_CHANNEL_SEND_ENTER, default=current_enter)] = bool + # P4-2a: the raw adb_command service is opt-in (default off) current_flag = self._options.get(CONF_ADB_COMMAND_ENABLED, False) schema_dict[vol.Optional(CONF_ADB_COMMAND_ENABLED, default=current_flag)] = bool diff --git a/custom_components/nvidia_shield/strings.json b/custom_components/nvidia_shield/strings.json index 4624fb6..f01a742 100644 --- a/custom_components/nvidia_shield/strings.json +++ b/custom_components/nvidia_shield/strings.json @@ -55,7 +55,8 @@ "protocol_invalid_code": "Pairing has not started or the code is empty. Click Submit to retry.", "protocol_pair_failed": "The Shield rejected that pairing code. Click Submit to try again.", "no_state_source": "No state source selected. Provide at least one of: the Android TV Remote entity, the Nvidia Remote Protocol pairing, or ADB media-session mode (enable ADB and set the ADB Host).", - "invalid_app_map": "Each line must be package=name with a non-empty package (no spaces) and a non-empty name \u2014 e.g. ar.tvplayer.tv=TiviMate." + "invalid_app_map": "Each line must be package=name with a non-empty package (no spaces) and a non-empty name \u2014 e.g. ar.tvplayer.tv=TiviMate.", + "invalid_digit_delay": "The digit delay must be between 0.05 and 1.0 seconds." }, "abort": { "already_configured": "This Shield entity is already configured.", @@ -79,7 +80,9 @@ "adb_host": "ADB Host", "adb_port": "ADB Port", "app_name_map": "Apps name/icon map", - "adb_command_enabled": "Enable raw ADB shell service" + "adb_command_enabled": "Enable raw ADB shell service", + "channel_digit_delay": "Channel digit delay (seconds)", + "channel_send_enter": "Send ENTER after channel digits" }, "data_description": { "jellyfin_entity": "The Jellyfin media_player for this device. Session stream info is read from the HA Jellyfin integration's existing coordinator \u2014 no extra config needed.", @@ -91,7 +94,9 @@ "adb_host": "IP address for ADB connection. Leave blank to auto-detect from the Android TV Remote integration.", "adb_port": "ADB port (default 5555).", "app_name_map": "One entry per line, package=name (e.g. ar.tvplayer.tv=TiviMate). The friendly name resolves after the Android TV Remote entity and the protocol push; icons follow the media image lookup by name. Leave blank to clear.", - "adb_command_enabled": "Adds the nvidia_shield.adb_command service (raw ADB shell over the persistent connection). Security-relevant: leave off unless you need it \u2014 it is the same opt-in posture as the core androidtv integration." + "adb_command_enabled": "Adds the nvidia_shield.adb_command service (raw ADB shell over the persistent connection). Security-relevant: leave off unless you need it \u2014 it is the same opt-in posture as the core androidtv integration.", + "channel_digit_delay": "Pause between the channel digits entered via play_media(channel) \u2014 0.05 to 1.0 seconds (default 0.1, matching the validated behavior).", + "channel_send_enter": "Optionally presses ENTER after the last digit (protocol key, then the ADB keyevent fallback). Off by default \u2014 matches TiviMate's validated behavior." } } } diff --git a/custom_components/nvidia_shield/translations/en.json b/custom_components/nvidia_shield/translations/en.json index 4624fb6..f01a742 100644 --- a/custom_components/nvidia_shield/translations/en.json +++ b/custom_components/nvidia_shield/translations/en.json @@ -55,7 +55,8 @@ "protocol_invalid_code": "Pairing has not started or the code is empty. Click Submit to retry.", "protocol_pair_failed": "The Shield rejected that pairing code. Click Submit to try again.", "no_state_source": "No state source selected. Provide at least one of: the Android TV Remote entity, the Nvidia Remote Protocol pairing, or ADB media-session mode (enable ADB and set the ADB Host).", - "invalid_app_map": "Each line must be package=name with a non-empty package (no spaces) and a non-empty name \u2014 e.g. ar.tvplayer.tv=TiviMate." + "invalid_app_map": "Each line must be package=name with a non-empty package (no spaces) and a non-empty name \u2014 e.g. ar.tvplayer.tv=TiviMate.", + "invalid_digit_delay": "The digit delay must be between 0.05 and 1.0 seconds." }, "abort": { "already_configured": "This Shield entity is already configured.", @@ -79,7 +80,9 @@ "adb_host": "ADB Host", "adb_port": "ADB Port", "app_name_map": "Apps name/icon map", - "adb_command_enabled": "Enable raw ADB shell service" + "adb_command_enabled": "Enable raw ADB shell service", + "channel_digit_delay": "Channel digit delay (seconds)", + "channel_send_enter": "Send ENTER after channel digits" }, "data_description": { "jellyfin_entity": "The Jellyfin media_player for this device. Session stream info is read from the HA Jellyfin integration's existing coordinator \u2014 no extra config needed.", @@ -91,7 +94,9 @@ "adb_host": "IP address for ADB connection. Leave blank to auto-detect from the Android TV Remote integration.", "adb_port": "ADB port (default 5555).", "app_name_map": "One entry per line, package=name (e.g. ar.tvplayer.tv=TiviMate). The friendly name resolves after the Android TV Remote entity and the protocol push; icons follow the media image lookup by name. Leave blank to clear.", - "adb_command_enabled": "Adds the nvidia_shield.adb_command service (raw ADB shell over the persistent connection). Security-relevant: leave off unless you need it \u2014 it is the same opt-in posture as the core androidtv integration." + "adb_command_enabled": "Adds the nvidia_shield.adb_command service (raw ADB shell over the persistent connection). Security-relevant: leave off unless you need it \u2014 it is the same opt-in posture as the core androidtv integration.", + "channel_digit_delay": "Pause between the channel digits entered via play_media(channel) \u2014 0.05 to 1.0 seconds (default 0.1, matching the validated behavior).", + "channel_send_enter": "Optionally presses ENTER after the last digit (protocol key, then the ADB keyevent fallback). Off by default \u2014 matches TiviMate's validated behavior." } } }