The user asked for "an image entity of the device screen capture? Can that be added". It is added as `image.<entry>_screen_capture`, and the three boundaries are the feature: - it NEVER drives state — the state is the capture timestamp, and the capture path reads the ADB poller and nothing else (pinned by a manager that raises on any other attribute, and by a real-HA run whose recorded state is the timestamp with no bytes anywhere in the attributes); - it is OPT-IN and OFF by default (`screen_capture_enabled`, Main setup row) — with the option absent `async_setup_entry` adds no entity at all; - it NEVER polls — HA's own `should_poll = False` for image entities is kept, so only an image fetch or `homeassistant.update_entity` captures; concurrent fetches share one shell and a capture is reused for 5 s. BINARY-SAFE CAPTURE: `screencap -p | base64` over the existing shell, decoded locally with `base64.b64decode` and gated on the PNG signature AND the IHDR chunk. `AdbDevice.shell()` returns `str`, so raw PNG bytes cannot travel through it; the alternative (a device temp file + the library's file-read path) needs the raw AdbDevice and writes to the Shield's shared storage, where a crash between write and delete leaves a screenshot behind. The base64 route leaves no device-side artifact — nothing to clean up — at the cost of ~33% more bytes on the wire. HONEST FAILURES: ADB off / ADB down / a capture that returned no PNG / a transport that raised all make the entity UNAVAILABLE with `capture_status` (`adb_disabled` / `adb_disconnected` / `capture_failed`) — never a blank or zero-byte image. `image_last_updated` moves only on success. Tests: 25 new cases (a real 2x1 PNG fixture on the wire — bytes, never a str; every non-PNG answer rejected; disconnected/failed/recovery paths; capture reuse, coalescing and the expiry window; opt-in default OFF and on) plus 4 config-flow cases (schema default False, off stays off, on is stored, and it can be turned back off). The stub harness gains the image platform and `util.dt` faces, the real-HA entity matrix gains this class, and the drift sweep's coverage test demanded exactly that. README documents the cost, the opt-in and the boundary.
393 lines
23 KiB
JSON
393 lines
23 KiB
JSON
{
|
|
"config": {
|
|
"step": {
|
|
"user": {
|
|
"title": "NVIDIA Shield",
|
|
"description": "{discovery_note}Set up your NVIDIA Shield. Everything here is optional except one state source — the Nvidia Remote Protocol or ADB. Link the devices and services you want to use.",
|
|
"data": {
|
|
"name": "Name",
|
|
"jellyfin_entity": "Jellyfin media player",
|
|
"receiver_entity": "Receiver",
|
|
"receiver_input": "Receiver input",
|
|
"power_entity": "Power switch",
|
|
"adb_enabled": "Read state over ADB",
|
|
"adb_store_app_info": "Show title and artist",
|
|
"host": "The Shield's IP address",
|
|
"adb_port": "ADB port",
|
|
"protocol_enabled": "Add the Nvidia Remote Protocol"
|
|
},
|
|
"data_description": {
|
|
"jellyfin_entity": "The Jellyfin media_player for this Shield. Leave blank to detect it automatically.",
|
|
"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.",
|
|
"power_entity": "A smart plug switch, if you want reliable on/off detection.",
|
|
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
|
"adb_store_app_info": "Shows the title and artist from non-Jellyfin apps. Turn off to show only the app name (privacy mode).",
|
|
"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.",
|
|
"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. Playback turns a switch receiver on when it is off.",
|
|
"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.",
|
|
"data": {
|
|
"host": "The Shield's IP address",
|
|
"adb_port": "ADB port",
|
|
"skip_pairing": "Continue without ADB pairing"
|
|
},
|
|
"data_description": {
|
|
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it only if the Shield moved or you gave it a different address.",
|
|
"adb_port": "The standard ADB port is 5555.",
|
|
"skip_pairing": "Finishes setup now and leaves ADB unpaired. The Pair ADB button stays available on the device page, so you can pair later."
|
|
}
|
|
},
|
|
"protocol_pair": {
|
|
"title": "Pair the remote protocol",
|
|
"description": "While this step is open the Shield shows a pairing code. Type it below and select Submit. If no code appears on the Shield, check the IP address below and select Submit again — pairing restarts and the Shield shows a new code. To set up without the remote protocol, turn on 'Continue without protocol pairing'.",
|
|
"data": {
|
|
"pairing_code": "Pairing code shown on the Shield",
|
|
"host": "The Shield's IP address",
|
|
"skip_pairing": "Continue without protocol pairing"
|
|
},
|
|
"data_description": {
|
|
"pairing_code": "The code the Shield displays while this step is open.",
|
|
"host": "The Shield's IP address. Pre-filled when the Shield was discovered — change it if the Shield moved.",
|
|
"skip_pairing": "Finishes setup with the remote protocol off — it stays off until you pair it. Pair later from this integration's entry menu: choose Reconfigure."
|
|
}
|
|
},
|
|
"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."
|
|
}
|
|
},
|
|
"error": {
|
|
"adb_failed": "The Shield did not accept the ADB connection. Make sure Network debugging is on, approve the Allow prompt on the Shield, then select Submit to try again.",
|
|
"adb_no_host": "No IP address for the Shield yet. Fill in the host field above (a discovered Shield is pre-filled automatically).",
|
|
"protocol_no_host": "No IP address for the Shield yet. Fill in the host field above (a discovered Shield is pre-filled automatically).",
|
|
"protocol_pair_failed": "The Shield rejected that pairing code. Check the code on the Shield and select Submit to try again.",
|
|
"protocol_unavailable": "The androidtvremote2 library is not installed, so the remote protocol cannot pair. Install it and restart Home Assistant, or turn on 'Continue without protocol pairing' to finish setup anyway.",
|
|
"protocol_unreachable": "The Shield did not answer at that address. Check the address above and that the Shield is awake, then select Submit to try again — or turn on 'Continue without protocol pairing' to finish setup without it.",
|
|
"no_state_source": "No state source selected. Choose at least one: 'Add the Nvidia Remote Protocol' or 'Read state over ADB'.",
|
|
"invalid_app_map": "Each line must be package=name with a non-empty package (no spaces) and a non-empty name — for example ar.tvplayer.tv=TiviMate.",
|
|
"invalid_digit_delay": "The digit delay must be between 0.05 and 1.0 seconds.",
|
|
"invalid_app_row": "A row needs a package id with no spaces and a non-empty name — for example ar.tvplayer.tv=TiviMate. An icon, if given, cannot contain spaces.",
|
|
"unknown_app_row": "That app is no longer in the list. Pick one of the rows offered, or add it again."
|
|
},
|
|
"abort": {
|
|
"already_configured": "This Shield is already set up.",
|
|
"reauth_successful": "Re-paired successfully.",
|
|
"reauth_skipped": "Pairing skipped. The saved certificate is still not trusted, so the remote protocol stays unavailable until you pair again.",
|
|
"reauth_entry_missing": "This entry no longer exists.",
|
|
"reconfigure_skipped": "Nothing changed — the remote protocol stays off until you pair it. Choose Reconfigure from this integration's entry menu when you are ready.",
|
|
"reconfigure_entry_missing": "This entry no longer exists, so there is nothing to reconfigure.",
|
|
"zeroconf_no_host": "The discovery event carried no usable address.",
|
|
"yaml_seed_only": {},
|
|
"yaml_seed": {
|
|
"title": "YAML succeeded earlier",
|
|
"message": "The configuration.yaml nvidia_shield block is seed-only: it starts the standard interactive flow. Removing the YAML block creates or deletes nothing."
|
|
}
|
|
}
|
|
},
|
|
"options": {
|
|
"step": {
|
|
"init": {
|
|
"title": "NVIDIA Shield settings",
|
|
"description": "Settings for this Shield. Saving reloads the integration so the change takes effect. {discovery}.",
|
|
"data": {
|
|
"jellyfin_entity": "Jellyfin media player",
|
|
"receiver_entity": "Receiver",
|
|
"receiver_input": "Receiver input",
|
|
"power_entity": "Power switch",
|
|
"adb_enabled": "Read state over ADB",
|
|
"protocol_enabled": "Add the Nvidia Remote Protocol",
|
|
"adb_store_app_info": "Show title and artist",
|
|
"host": "The Shield's IP address",
|
|
"adb_port": "ADB port",
|
|
"app_name_map": "App names and icons",
|
|
"adb_command_enabled": "Enable the adb_command service",
|
|
"channel_digit_delay": "Channel digit delay (seconds)",
|
|
"channel_send_enter": "Send ENTER after channel digits",
|
|
"script_turn_on": "Turn on script",
|
|
"script_turn_off": "Turn off script",
|
|
"script_volume_up": "Volume up script",
|
|
"script_volume_down": "Volume down script",
|
|
"script_volume_set": "Volume set script",
|
|
"script_volume_mute": "Mute script",
|
|
"script_select_source": "Select source script",
|
|
"logcat_events": "Instant app detection (logcat stream)",
|
|
"remote_command_script": "Remote command script"
|
|
},
|
|
"data_description": {
|
|
"jellyfin_entity": "The Jellyfin media_player for this Shield. Stream details come from the HA Jellyfin integration — no extra setup needed.",
|
|
"receiver_entity": "The receiver or soundbar this Shield plays through — a media_player, or a plain switch that playback turns on when it is off.",
|
|
"receiver_input": "The receiver input the Shield uses, for example 'Shield'.",
|
|
"power_entity": "A smart plug switch, if you want reliable on/off detection.",
|
|
"adb_enabled": "Reads now-playing information over ADB from any app. Requires Developer options > Network debugging on the Shield.",
|
|
"adb_store_app_info": "Shows the title and artist from non-Jellyfin apps. Turn off to show only the app name (privacy mode).",
|
|
"host": "The Shield's IP address. Change it only if the Shield moved or you gave it a different address.",
|
|
"protocol_enabled": "Adds the all-in-one remote-protocol transport (power, availability, app push and volume readout). Turning it on needs a one-time PIN pairing — use Reconfigure on the entry to pair.",
|
|
"adb_port": "The ADB port. The standard value is 5555.",
|
|
"app_name_map": "One entry per line, package=name (for example ar.tvplayer.tv=TiviMate), with an optional |icon after the name. Leave blank to clear.",
|
|
"adb_command_enabled": "Adds the nvidia_shield.adb_command service (a raw ADB shell command over the persistent connection). Security-relevant: leave it off unless you need it — the same opt-in posture as the core androidtv integration.",
|
|
"channel_digit_delay": "Pause between the channel digits entered via play_media(channel) — 0.05 to 1.0 seconds (default 0.1, the tested value).",
|
|
"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": {
|
|
"title": "Main setup",
|
|
"description": "The Shield's address, the transports it is read over, and the devices this entry links to. Changes here are kept when you save.",
|
|
"data": {
|
|
"host": "The Shield's IP address",
|
|
"adb_port": "ADB port",
|
|
"adb_enabled": "Read state over ADB",
|
|
"adb_store_app_info": "Show title and artist",
|
|
"protocol_enabled": "Add the Nvidia Remote Protocol",
|
|
"logcat_events": "Watch app launches over logcat",
|
|
"adb_command_enabled": "Allow the adb_command action",
|
|
"screen_capture_enabled": "Add a screen capture image entity",
|
|
"jellyfin_entity": "Jellyfin media player",
|
|
"power_entity": "Power switch"
|
|
},
|
|
"data_description": {
|
|
"host": "The Shield's IP address. Change it only if the Shield moved.",
|
|
"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.",
|
|
"adb_store_app_info": "Shows the title and artist from non-Jellyfin apps. Turn off to show only the app name (privacy mode).",
|
|
"protocol_enabled": "The all-in-one remote-protocol transport (power, availability, app push and volume readout). It needs the one-time PIN pairing from setup.",
|
|
"logcat_events": "Uses a second ADB stream to notice app launches the moment they happen. Leave off unless you want the faster app switch.",
|
|
"adb_command_enabled": "Exposes the adb_command action for this entry's media player. Off by default; it runs arbitrary shell commands on the Shield.",
|
|
"screen_capture_enabled": "Adds an image entity showing the Shield's screen on demand. Off by default: a capture is a full-resolution screen grab, it is a manual/diagnostic surface, and it never drives state.",
|
|
"jellyfin_entity": "The Jellyfin media_player for this Shield. Leave blank to detect it automatically.",
|
|
"power_entity": "A smart plug switch, if you want reliable on/off detection."
|
|
}
|
|
},
|
|
"receiver": {
|
|
"title": "Receiver",
|
|
"description": "The receiver or soundbar this Shield plays through — a media_player, or a plain switch for a soundbar that only turns on and off. Playback turns a switch receiver on when it is off, so no receiver volume scripts are needed for that.",
|
|
"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."
|
|
}
|
|
},
|
|
"apps": {
|
|
"title": "Apps",
|
|
"description": "The apps this Shield offers: they fill the media player's source list and name the apps in automations. Add, edit or remove ONE app with the rows below, or paste the whole list as text. The list carries the apps discovery found as well as the rows you saved — a detected app is marked, and picking it lets you give it a name. Currently: {apps}",
|
|
"menu_options": {
|
|
"apps_text": "Edit the whole list as text",
|
|
"apps_add": "Add an app",
|
|
"apps_edit": "Edit an app",
|
|
"apps_remove": "Remove an app",
|
|
"menu_again": "Back to the main menu"
|
|
}
|
|
},
|
|
"apps_text": {
|
|
"title": "Apps as text",
|
|
"description": "One app per line: the package id, an equals sign, the name to show, and optionally a pipe and an icon (a picture URL or a camera entity id). Blank uses the built-in list. Icons the device reported: {icons} — the icon column itself takes a picture URL or a camera entity.",
|
|
"data": {
|
|
"app_name_map": "App names and icons"
|
|
},
|
|
"data_description": {
|
|
"app_name_map": "One row per app, for example ar.tvplayer.tv=TiviMate|http://192.168.1.10/tivi.png. The name is what the source list, the Current App sensor and the media player show."
|
|
}
|
|
},
|
|
"apps_add": {
|
|
"title": "Add an app",
|
|
"description": "Adds ONE row. The package id is what the Shield launches, the name is what the card, the sensor and the source list show.",
|
|
"data": {
|
|
"app_package": "Package id",
|
|
"app_name": "Name to show",
|
|
"app_icon": "Icon (optional)"
|
|
},
|
|
"data_description": {
|
|
"app_package": "The Android package id, for example ar.tvplayer.tv or com.netflix.ninja. No spaces.",
|
|
"app_name": "What the source list, the Current App sensor and the media player show for this app.",
|
|
"app_icon": "Optional: a picture URL or a camera entity id. Leave blank to show no picture."
|
|
}
|
|
},
|
|
"apps_edit": {
|
|
"title": "Edit an app",
|
|
"description": "Pick the app to edit: the rows you saved plus the apps discovery found (marked 'detected'). The next step edits the name and the icon, and saving a detected app stores it as one of your own rows.",
|
|
"data": {
|
|
"app_package": "App"
|
|
},
|
|
"data_description": {
|
|
"app_package": "Every app you can act on. '(no name yet)' means the device reported no label for it — pick it and type one. To change a package id itself, remove the row and add it again, or edit the whole list as text."
|
|
}
|
|
},
|
|
"apps_edit_form": {
|
|
"title": "Edit an app",
|
|
"description": "Editing {app}. The package id identifies the row and is not changed here.",
|
|
"data": {
|
|
"app_name": "Name to show",
|
|
"app_icon": "Icon (optional)"
|
|
},
|
|
"data_description": {
|
|
"app_name": "What the source list, the Current App sensor and the media player show for this app.",
|
|
"app_icon": "A picture URL or a camera entity id. Clear it to drop the picture."
|
|
}
|
|
},
|
|
"apps_remove": {
|
|
"title": "Remove an app",
|
|
"description": "Pick the app to remove. Nothing is removed until you confirm on the next step.",
|
|
"data": {
|
|
"app_package": "App"
|
|
},
|
|
"data_description": {
|
|
"app_package": "The rows saved so far. The confirmation is next."
|
|
}
|
|
},
|
|
"apps_remove_confirm": {
|
|
"title": "Remove an app",
|
|
"description": "Remove {app} from the list? It leaves the source list when you save, and the app itself stays installed on the Shield.",
|
|
"menu_options": {
|
|
"apps_remove_yes": "Remove it",
|
|
"apps_remove_no": "Keep it"
|
|
}
|
|
},
|
|
"scripts": {
|
|
"title": "Scripts",
|
|
"description": "Optional: run a Home Assistant script instead of the built-in command, per command. Clear a row to go back to the built-in path.",
|
|
"data": {
|
|
"remote_command_script": "Script for remote.send_command",
|
|
"script_turn_on": "turn_on script",
|
|
"script_turn_off": "turn_off script",
|
|
"script_volume_up": "volume_up script",
|
|
"script_volume_down": "volume_down script",
|
|
"script_volume_set": "volume_set script",
|
|
"script_volume_mute": "volume_mute script",
|
|
"script_select_source": "select_source script",
|
|
"channel_digit_delay": "Delay between channel digits (seconds)",
|
|
"channel_send_enter": "Send ENTER after channel digits"
|
|
},
|
|
"data_description": {
|
|
"remote_command_script": "Replaces the whole remote.send_command path for this Shield. Leave blank to use the built-in key presses.",
|
|
"channel_digit_delay": "The gap between the digits of a channel number, 0.05 to 1.0 seconds. Default 0.1.",
|
|
"channel_send_enter": "Turn on if your channel app needs ENTER after the digits."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"services": {
|
|
"adb_command": {
|
|
"name": "ADB command",
|
|
"description": "Send a raw ADB shell command to the persistent connection of an Nvidia Shield entry (option-gated use).",
|
|
"fields": {
|
|
"command": {
|
|
"name": "Command",
|
|
"description": "The shell command to run on the device."
|
|
},
|
|
"entity_id": {
|
|
"name": "Entity",
|
|
"description": "The Nvidia Shield media player to target."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"exceptions": {
|
|
"invalid_command": {
|
|
"title": "Empty command",
|
|
"message": "The command field is empty — nothing was sent."
|
|
},
|
|
"unsupported_target": {
|
|
"title": "Unsupported entity for adb_command",
|
|
"message": "{entity_id} is not an adb_command participant (its entry must enable the adb_command_enabled option) — command not sent."
|
|
},
|
|
"adb_unavailable": {
|
|
"title": "ADB unavailable",
|
|
"message": "The ADB connection of {entity_id} is down — command not sent."
|
|
},
|
|
"unknown_source": {
|
|
"title": "Unknown source",
|
|
"message": "'{source}' is not one of this Shield's apps. Use a name from the source list — the app names from the Apps map, or the built-in list when no map is set."
|
|
},
|
|
"command_script_failed": {
|
|
"title": "Command script failed",
|
|
"message": "The script configured for {action} ({script}) failed: {error}"
|
|
}
|
|
},
|
|
"entity": {
|
|
"sensor": {
|
|
"current_app": {
|
|
"name": "Current app"
|
|
},
|
|
"now_playing": {
|
|
"name": "Now playing"
|
|
},
|
|
"installed_apps": {
|
|
"name": "Installed apps"
|
|
}
|
|
},
|
|
"image": {
|
|
"screen_capture": {
|
|
"name": "Screen capture"
|
|
}
|
|
}
|
|
}
|
|
}
|