fix(config-flow): the setup menu was unreachable under real HA (t57 UNIT 0a)
t61 F1 (BLOCKER) + F2 + F3 + F5, all reproduced against a real FlowManager
before fixing. The OPTIONS flow was verified good and is untouched by this
commit; the SETUP flow was broken in three independent ways.
F1 — `_show_setup_menu` passed `errors=` to `async_show_menu`, which has no
such parameter (HA 2026.10.1: step_id / menu_options / sort /
description_placeholders). Every path that went near the menu therefore
raised `TypeError` the moment the first form was submitted or ADB pairing
was skipped. The path map, driven end to end: FOUR of the five ordinary
paths died before create_entry ("ADB skip", "ADB paired + protocol off",
"ADB skip + protocol skip", "pair ADB"); only "protocol only" survived, and
it never showed the menu — so a new entry could only be created on that one
path and the sub-screens were unreachable.
F2 — the menu's rows broke the rule this repo documents: HA dispatches a row
to `async_step_<row id>` AND dispatches a submitted form by its OWN step_id,
so a row named `adb` with a handler called `async_step_adb_pair` is an
UnknownStep, and a row named `receiver` rendering `step_id="setup_receiver"`
is a form that cannot be submitted. The rows are now their handler names
(`receiver`, `connection`, `adb_pair`, `protocol_pair`, `finish`) and the
two sub-screens render their row id. There is also a `_paired_adb` guard
now, so a completed ADB pairing stops offering that row — the rule the
protocol row already followed.
F3 — both menus keyed their row labels under a bare `menu`, which core does
not read (56 core strings files use `menu_options`), so every row fell back
to its raw id in the browser. Renamed in both strings files.
F5 — the per-command script merge is field-wise now: a `script_<command>`
key that is present and empty removes that command, one with a value sets
it, and a key the submission does not carry is untouched — the same rule as
every other option. The wholesale rebuild depended on the page always being
complete, so a partial submission silently wiped the commands it omitted.
THE SYSTEMIC HALF (F1), closed as asked: `ha_api_drift.HA_CALL_OWNERS` now
covers the flow-display APIs (`async_show_menu`, `async_show_progress`,
`async_show_progress_done`, plus the existing form/abort/entry owners), so
the drift engine resolves their REAL signatures and flags an unknown
keyword. `tests/test_ha_api_drift.py` carries a pre-fix source using
`errors=` to prove the new owner is non-vacuous. Every harness stub now
mirrors the real signature (no `errors` on a menu) and a stub-level test
pins that parameter set, so a stub cannot go back to accepting anything.
On the pre-fix tree the drift engine reports exactly this:
config_flow.py:898: unknown-kwarg: async_show_menu() has no 'errors'
parameter; real signature: (self, *, step_id=None, menu_options, sort,
description_placeholders) -> _FlowResultT
TESTS: tests/test_config_flow_real_ha.py gains a SETUP section that drives
the real flow — the first form, the ADB skip, the pairing guard, EVERY menu
row (dispatch AND re-submittable step_id), an accepted sub-screen
submission, a full connection+receiver+finish walk merging into one entry,
the menu's pairing note, and the five-path map as a parametrised test.
11 of those fail on the pre-fix tree; the drift engine failure above is the
12th. The stub suite additionally pins the menu stub's signature and the
`menu_options` key in both strings files.
Also in this commit (t57 F4): the real-HA file claimed entity-valued pages
could not be driven there, which was false (it has always submitted
`switch.soundbar`). The claim is gone and the scripts page is driven through
SET (two scripts selected, real entity ids) → CLEAR (one selector emptied)
→ SAVE → REOPEN, so the SET half is pinned in the real flow and not only
against the selector objects.
This commit is contained in:
1 parent
5eefd0c6d0
commit
3a55519b74
10 files changed
+713
-208
No files matched your search
@@ -875,29 +875,66 @@ class NvidiaShieldConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._setup_extra_options = {}
|
||||
return self._setup_extra_options
|
||||
|
||||
def _show_setup_menu(self, errors=None):
|
||||
"""The setup menu: connection, receiver, and finishing the entry (t56).
|
||||
def _show_setup_menu(self):
|
||||
"""The setup menu: receiver, connection, pairing, finish (t56).
|
||||
|
||||
Home Assistant's standard menu step: it renders every option as a row
|
||||
and dispatches the choice to ``async_step_<option>``. Both transports
|
||||
stay optional (the pairing steps keep their skip), so the user can
|
||||
finish at any point. A transport that already paired is not offered
|
||||
again — that row would re-open a pairing session the Shield no longer
|
||||
needs.
|
||||
Home Assistant's standard menu step: it renders every option as a row and
|
||||
dispatches the choice to ``async_step_<row id>``.
|
||||
|
||||
TWO rules this menu has to obey, both learned the hard way:
|
||||
|
||||
* the row id must BE its handler name — ``receiver``,
|
||||
``connection``, ``adb_pair``, ``protocol_pair``, ``finish`` — because
|
||||
that is literally how HA dispatches (``_raise_if_step_does_not_exist``
|
||||
on ``async_step_<row id>``). A friendlier alias like ``adb`` is an
|
||||
UnknownStep.
|
||||
* a sub-screen's rendered ``step_id`` must be the SAME string, because a
|
||||
submitted form is dispatched by its own step_id. Rendering
|
||||
``setup_receiver`` from the ``receiver`` row makes the form
|
||||
unsubmittable.
|
||||
|
||||
``async_show_menu`` takes no ``errors`` (verified against HA 2026.10.1:
|
||||
``step_id``/``menu_options``/``sort``/``description_placeholders``
|
||||
only), which is why this method takes none either.
|
||||
|
||||
Both transports stay optional (the pairing steps keep their skip), so
|
||||
the user can finish at any point. A transport that already paired is not
|
||||
offered again — that row would re-open a pairing session the Shield no
|
||||
longer needs (file an entry option to change it).
|
||||
"""
|
||||
options = ["finish"]
|
||||
if self._user_data.get(CONF_ADB_ENABLED):
|
||||
options.insert(0, "adb")
|
||||
# built in one explicit order rather than a chain of insert(0), which
|
||||
# is how this read backwards until real HA showed the row order
|
||||
options = ["receiver", "connection"]
|
||||
if self._user_data.get(CONF_ADB_ENABLED) and not getattr(
|
||||
self, "_paired_adb", False
|
||||
):
|
||||
options.append("adb_pair")
|
||||
if self._user_data.get(CONF_PROTOCOL_ENABLED) and not getattr(
|
||||
self, "_paired_protocol", False
|
||||
):
|
||||
options.insert(0, "protocol")
|
||||
options.insert(0, "connection")
|
||||
options.insert(0, "receiver")
|
||||
options.append("protocol_pair")
|
||||
options.append("finish")
|
||||
return self.async_show_menu(
|
||||
step_id="menu", menu_options=options, errors=errors or {}
|
||||
step_id="menu",
|
||||
menu_options=options,
|
||||
description_placeholders=self._setup_menu_placeholders(),
|
||||
)
|
||||
|
||||
def _setup_menu_placeholders(self) -> dict:
|
||||
"""What the setup menu says about the transports it still offers."""
|
||||
pending = []
|
||||
if self._user_data.get(CONF_ADB_ENABLED) and not getattr(
|
||||
self, "_paired_adb", False
|
||||
):
|
||||
pending.append("ADB")
|
||||
if self._user_data.get(CONF_PROTOCOL_ENABLED) and not getattr(
|
||||
self, "_paired_protocol", False
|
||||
):
|
||||
pending.append("the remote protocol")
|
||||
if not pending:
|
||||
return {"pairing": "everything that needs pairing is paired"}
|
||||
return {"pairing": "still to pair: " + " and ".join(pending)}
|
||||
|
||||
async def async_step_menu(self, user_input=None):
|
||||
"""First visit: open the pending pairing; later visits: show the menu.
|
||||
|
||||
@@ -962,9 +999,9 @@ class NvidiaShieldConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
fields[vol.Optional(CONF_RECEIVER_INPUT, default=current_input)] = str
|
||||
else:
|
||||
fields[vol.Optional(CONF_RECEIVER_INPUT)] = str
|
||||
return self.async_show_form(
|
||||
step_id="setup_receiver", data_schema=vol.Schema(fields)
|
||||
)
|
||||
# the step_id IS the menu row id: HA dispatches a submitted form by its
|
||||
# own step_id, so anything else is an UnknownStep on submit
|
||||
return self.async_show_form(step_id="receiver", data_schema=vol.Schema(fields))
|
||||
|
||||
async def async_step_connection(self, user_input=None):
|
||||
"""Setup sub-screen: the address, the ADB port and the two transports.
|
||||
@@ -1003,9 +1040,7 @@ class NvidiaShieldConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
default=bool(self._user_data.get(CONF_PROTOCOL_ENABLED, True)),
|
||||
): bool,
|
||||
}
|
||||
return self.async_show_form(
|
||||
step_id="setup_connection", data_schema=vol.Schema(fields)
|
||||
)
|
||||
return self.async_show_form(step_id="connection", data_schema=vol.Schema(fields))
|
||||
|
||||
async def async_step_finish(self, user_input=None):
|
||||
"""The menu's finish row: create the entry from the first form + sub-screens."""
|
||||
@@ -1082,6 +1117,9 @@ class NvidiaShieldConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
host, port, key_path
|
||||
)
|
||||
if connected:
|
||||
# t57 F2: paired, so the menu must stop offering that row —
|
||||
# the same rule the protocol row already follows
|
||||
self._paired_adb = True
|
||||
return await self._async_after_pairing()
|
||||
else:
|
||||
errors["base"] = "adb_failed"
|
||||
@@ -1266,26 +1304,31 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlowWithReload):
|
||||
return False
|
||||
present[CONF_CHANNEL_DIGIT_DELAY] = delay
|
||||
|
||||
# ── the per-command scripts: the submitted fields ARE the dict ──
|
||||
# ── the per-command scripts: per FIELD, which is the unambiguous rule ──
|
||||
#
|
||||
# Rebuilding from the submitted fields is what makes removal persist: a
|
||||
# script configured for `turn_on` and then cleared arrives as '' for
|
||||
# that field, and a dict built only from the non-empty fields drops it
|
||||
# instead of leaving a stale key behind. A command the submission does
|
||||
# not carry is therefore cleared too — the Scripts screen renders every
|
||||
# field, so the only way a key goes missing is a hand-made partial
|
||||
# submission, and this keeps the old "a save without scripts clears
|
||||
# them" contract unambiguous rather than shape-dependent.
|
||||
if any(field.startswith(SCRIPT_FIELD_PREFIX) for field in present):
|
||||
present[CONF_COMMAND_SCRIPTS] = {
|
||||
command: str(present.get(f"{SCRIPT_FIELD_PREFIX}{command}") or "").strip()
|
||||
for command in SCRIPT_COMMANDS
|
||||
if str(present.get(f"{SCRIPT_FIELD_PREFIX}{command}") or "").strip()
|
||||
}
|
||||
for field in [f for f in present if f.startswith(SCRIPT_FIELD_PREFIX)]:
|
||||
# UI only: the service layer reads the dict, never a flat key.
|
||||
# Dropped AFTER the rebuild — the rebuild reads them.
|
||||
# A cleared selector arrives as an empty value for THAT field (t57
|
||||
# UNIT 0a verified the shape against real HA), so clearing is a field
|
||||
# operation: a `script_<command>` key that is present and empty REMOVES
|
||||
# its command, one that is present with a value sets it, and a key the
|
||||
# submission does not carry is untouched — the same rule as every other
|
||||
# option. Nothing depends on the page being complete, so a hand-made
|
||||
# partial submission cannot silently wipe the commands it omits. The
|
||||
# starting point is the normalized `command_scripts` view, so a command
|
||||
# this version no longer knows about is dropped rather than carried.
|
||||
script_fields = [f for f in present if f.startswith(SCRIPT_FIELD_PREFIX)]
|
||||
if script_fields:
|
||||
scripts = dict(command_scripts(self._options_dict))
|
||||
for field in script_fields:
|
||||
command = field[len(SCRIPT_FIELD_PREFIX):]
|
||||
value = str(present[field] or "").strip()
|
||||
if command in SCRIPT_COMMANDS and value:
|
||||
scripts[command] = value
|
||||
else:
|
||||
# cleared, or a field this version has no command for
|
||||
scripts.pop(command, None)
|
||||
# UI only: the service layer reads the dict, never a flat key
|
||||
present.pop(field)
|
||||
present[CONF_COMMAND_SCRIPTS] = scripts
|
||||
|
||||
for key, value in present.items():
|
||||
if value is None:
|
||||
|
||||
@@ -28,6 +28,57 @@
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Finish setup",
|
||||
"description": "Pair the transports and check anything else before the entry is created. {pairing}.",
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
},
|
||||
"menu_options": {
|
||||
"receiver": "Receiver",
|
||||
"connection": "Connection",
|
||||
"adb_pair": "Set up ADB",
|
||||
"protocol_pair": "Pair the remote protocol",
|
||||
"finish": "Create the entry"
|
||||
}
|
||||
},
|
||||
"receiver": {
|
||||
"title": "Receiver",
|
||||
"description": "The receiver or soundbar this Shield plays through — a media_player, or a plain switch.",
|
||||
"data": {
|
||||
"receiver_entity": "Receiver",
|
||||
"receiver_input": "Receiver input"
|
||||
},
|
||||
"data_description": {
|
||||
"receiver_entity": "The receiver, soundbar or speaker switch this Shield plays through. Leave blank if there is none.",
|
||||
"receiver_input": "The receiver input the Shield uses, for example 'Shield'. Leave blank if you are not sure."
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
"title": "Connection",
|
||||
"description": "The Shield's address, the ADB port and which transports this entry uses.",
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"adb_pair": {
|
||||
"title": "Allow ADB access",
|
||||
"description": "On the Shield, open Settings > System > Developer options and turn on Network debugging (it may be called ADB debugging). Then select Submit — the Shield shows an Allow prompt; approve it. Nothing happened? Check the address below and select Submit again. Not now? Turn on 'Continue without ADB pairing' and select Submit.",
|
||||
@@ -59,57 +110,6 @@
|
||||
"reauth_confirm": {
|
||||
"title": "Re-pair Nvidia Shield",
|
||||
"description": "The Shield no longer trusts the saved pairing certificate, usually after a factory reset. Select Submit to pair again — the Shield then shows a code."
|
||||
},
|
||||
"menu": {
|
||||
"title": "Finish setup",
|
||||
"description": "Pair the transports and check anything else before the entry is created.",
|
||||
"menu": {
|
||||
"receiver": "Receiver",
|
||||
"connection": "Connection",
|
||||
"adb": "Set up ADB",
|
||||
"protocol": "Pair the remote protocol",
|
||||
"finish": "Create the entry"
|
||||
},
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"setup_connection": {
|
||||
"title": "Connection",
|
||||
"description": "The Shield's address, the ADB port and which transports this entry uses.",
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"setup_receiver": {
|
||||
"title": "Receiver",
|
||||
"description": "The receiver or soundbar this Shield plays through — a media_player, or a plain switch.",
|
||||
"data": {
|
||||
"receiver_entity": "Receiver",
|
||||
"receiver_input": "Receiver input"
|
||||
},
|
||||
"data_description": {
|
||||
"receiver_entity": "The receiver, soundbar or speaker switch this Shield plays through. Leave blank if there is none.",
|
||||
"receiver_input": "The receiver input the Shield uses, for example 'Shield'. Leave blank if you are not sure."
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -143,14 +143,6 @@
|
||||
"init": {
|
||||
"title": "NVIDIA Shield settings",
|
||||
"description": "Settings for this Shield. Saving reloads the integration so the change takes effect. {discovery}.",
|
||||
"menu": {
|
||||
"connection": "Main setup",
|
||||
"receiver": "Receiver",
|
||||
"apps": "Apps",
|
||||
"discover": "Discover installed apps",
|
||||
"scripts": "Scripts",
|
||||
"finish": "Save and close"
|
||||
},
|
||||
"data": {
|
||||
"jellyfin_entity": "Jellyfin media player",
|
||||
"receiver_entity": "Receiver",
|
||||
@@ -191,6 +183,14 @@
|
||||
"channel_send_enter": "Presses ENTER after the last digit (protocol key first, then the ADB keyevent fallback). Off by default, matching TiviMate's tested behavior.",
|
||||
"logcat_events": "Detects app changes instantly through a dedicated logcat stream (AdbDevice.streaming_shell). Off by default — polling keeps the state correct either way, and the integration falls back to polling if the stream keeps failing.",
|
||||
"remote_command_script": "One script for remote.send_command. It replaces the key path when set and receives the call's own parameters as variables: command (the list of key commands), num_repeats, delay_secs and hold_secs."
|
||||
},
|
||||
"menu_options": {
|
||||
"connection": "Main setup",
|
||||
"receiver": "Receiver",
|
||||
"apps": "Apps",
|
||||
"discover": "Discover installed apps",
|
||||
"scripts": "Scripts",
|
||||
"finish": "Save and close"
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
|
||||
@@ -28,6 +28,57 @@
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"menu": {
|
||||
"title": "Finish setup",
|
||||
"description": "Pair the transports and check anything else before the entry is created. {pairing}.",
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
},
|
||||
"menu_options": {
|
||||
"receiver": "Receiver",
|
||||
"connection": "Connection",
|
||||
"adb_pair": "Set up ADB",
|
||||
"protocol_pair": "Pair the remote protocol",
|
||||
"finish": "Create the entry"
|
||||
}
|
||||
},
|
||||
"receiver": {
|
||||
"title": "Receiver",
|
||||
"description": "The receiver or soundbar this Shield plays through — a media_player, or a plain switch.",
|
||||
"data": {
|
||||
"receiver_entity": "Receiver",
|
||||
"receiver_input": "Receiver input"
|
||||
},
|
||||
"data_description": {
|
||||
"receiver_entity": "The receiver, soundbar or speaker switch this Shield plays through. Leave blank if there is none.",
|
||||
"receiver_input": "The receiver input the Shield uses, for example 'Shield'. Leave blank if you are not sure."
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
"title": "Connection",
|
||||
"description": "The Shield's address, the ADB port and which transports this entry uses.",
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"adb_pair": {
|
||||
"title": "Allow ADB access",
|
||||
"description": "On the Shield, open Settings > System > Developer options and turn on Network debugging (it may be called ADB debugging). Then select Submit — the Shield shows an Allow prompt; approve it. Nothing happened? Check the address below and select Submit again. Not now? Turn on 'Continue without ADB pairing' and select Submit.",
|
||||
@@ -59,57 +110,6 @@
|
||||
"reauth_confirm": {
|
||||
"title": "Re-pair Nvidia Shield",
|
||||
"description": "The Shield no longer trusts the saved pairing certificate, usually after a factory reset. Select Submit to pair again — the Shield then shows a code."
|
||||
},
|
||||
"menu": {
|
||||
"title": "Finish setup",
|
||||
"description": "Pair the transports and check anything else before the entry is created.",
|
||||
"menu": {
|
||||
"receiver": "Receiver",
|
||||
"connection": "Connection",
|
||||
"adb": "Set up ADB",
|
||||
"protocol": "Pair the remote protocol",
|
||||
"finish": "Create the entry"
|
||||
},
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"setup_connection": {
|
||||
"title": "Connection",
|
||||
"description": "The Shield's address, the ADB port and which transports this entry uses.",
|
||||
"data": {
|
||||
"host": "The Shield's IP address",
|
||||
"adb_port": "ADB port",
|
||||
"adb_enabled": "Read state over ADB",
|
||||
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if you need a different address.",
|
||||
"adb_port": "The ADB port. The standard value is 5555 — change it only if ADB uses a custom port on your network.",
|
||||
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
||||
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs a one-time PIN pairing right after this step."
|
||||
}
|
||||
},
|
||||
"setup_receiver": {
|
||||
"title": "Receiver",
|
||||
"description": "The receiver or soundbar this Shield plays through — a media_player, or a plain switch.",
|
||||
"data": {
|
||||
"receiver_entity": "Receiver",
|
||||
"receiver_input": "Receiver input"
|
||||
},
|
||||
"data_description": {
|
||||
"receiver_entity": "The receiver, soundbar or speaker switch this Shield plays through. Leave blank if there is none.",
|
||||
"receiver_input": "The receiver input the Shield uses, for example 'Shield'. Leave blank if you are not sure."
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
@@ -143,14 +143,6 @@
|
||||
"init": {
|
||||
"title": "NVIDIA Shield settings",
|
||||
"description": "Settings for this Shield. Saving reloads the integration so the change takes effect. {discovery}.",
|
||||
"menu": {
|
||||
"connection": "Main setup",
|
||||
"receiver": "Receiver",
|
||||
"apps": "Apps",
|
||||
"discover": "Discover installed apps",
|
||||
"scripts": "Scripts",
|
||||
"finish": "Save and close"
|
||||
},
|
||||
"data": {
|
||||
"jellyfin_entity": "Jellyfin media player",
|
||||
"receiver_entity": "Receiver",
|
||||
@@ -191,6 +183,14 @@
|
||||
"channel_send_enter": "Presses ENTER after the last digit (protocol key first, then the ADB keyevent fallback). Off by default, matching TiviMate's tested behavior.",
|
||||
"logcat_events": "Detects app changes instantly through a dedicated logcat stream (AdbDevice.streaming_shell). Off by default — polling keeps the state correct either way, and the integration falls back to polling if the stream keeps failing.",
|
||||
"remote_command_script": "One script for remote.send_command. It replaces the key path when set and receives the call's own parameters as variables: command (the list of key commands), num_repeats, delay_secs and hold_secs."
|
||||
},
|
||||
"menu_options": {
|
||||
"connection": "Main setup",
|
||||
"receiver": "Receiver",
|
||||
"apps": "Apps",
|
||||
"discover": "Discover installed apps",
|
||||
"scripts": "Scripts",
|
||||
"finish": "Save and close"
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
|
||||
Reference in new issue
Block a user