test(config_flow,media_player,remote): digit-delay/send-enter options (store + window/error validation), IME-first text entry with ADB fallback tier, icon column parse/prefill/store (P5-2)
This commit is contained in:
1 parent
3d026ae60e
commit
a3b397920e
7 files changed
+320
-18
No files matched your search
@@ -20,6 +20,7 @@ from .const import (
|
||||
CONF_ADB_PORT,
|
||||
CONF_ADB_STORE_APP_INFO,
|
||||
CONF_ANDROIDTV_ENTITY,
|
||||
CONF_APP_ICON_MAP,
|
||||
CONF_APP_NAME_MAP,
|
||||
CONF_CHANNEL_DIGIT_DELAY,
|
||||
CONF_CHANNEL_SEND_ENTER,
|
||||
@@ -80,13 +81,27 @@ def _find_jellyfin_entity(hass, device_name: str) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def _app_map_to_text(app_map: dict) -> str:
|
||||
"""Serialize the apps map to one ``package=name`` per line."""
|
||||
return "\n".join(f"{pkg}={name}" for pkg, name in app_map.items())
|
||||
def _app_map_to_text(app_map: dict, icon_map: dict | None = None) -> str:
|
||||
"""Serialize the apps map to one ``package=name[|icon]`` per line."""
|
||||
icon_map = icon_map or {}
|
||||
lines = []
|
||||
for pkg, name in app_map.items():
|
||||
icon = icon_map.get(pkg)
|
||||
lines.append(f"{pkg}={name}|{icon}" if icon else f"{pkg}={name}")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _app_map_from_text(text: str) -> dict[str, str] | None:
|
||||
"""Parse one ``package=name`` per line; None on malformed input."""
|
||||
def _app_map_from_text(
|
||||
text: str, icons: dict[str, str] | None = None
|
||||
) -> dict[str, str] | None:
|
||||
"""Parse one ``package=name[|icon]`` per line; None on malformed input.
|
||||
|
||||
The optional second column (a picture URL or entity id) feeds
|
||||
entity_picture through CONF_APP_ICON_MAP; single-column maps stay
|
||||
valid. Malformed icon text (spaces) invalidates the row the same way
|
||||
a malformed name does.
|
||||
"""
|
||||
icons = icons if icons is not None else {}
|
||||
app_map: dict[str, str] = {}
|
||||
for line in text.split("\n"):
|
||||
line = line.strip()
|
||||
@@ -94,11 +109,18 @@ def _app_map_from_text(text: str) -> dict[str, str] | None:
|
||||
continue
|
||||
if "=" not in line:
|
||||
return None
|
||||
package, name = line.split("=", 1)
|
||||
package, value = line.split("=", 1)
|
||||
parts = value.strip().split("|", 1)
|
||||
name = parts[0].strip()
|
||||
icon = parts[1].strip() if len(parts) > 1 else ""
|
||||
package = package.strip()
|
||||
name = name.strip()
|
||||
if not package or not name or " " in package:
|
||||
return None
|
||||
if icon:
|
||||
if " " in icon:
|
||||
return None
|
||||
icons[package] = icon
|
||||
app_map[package] = name
|
||||
return app_map
|
||||
|
||||
@@ -511,12 +533,13 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlow):
|
||||
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)
|
||||
icons: dict[str, str] = {}
|
||||
if app_map_text is None:
|
||||
app_map = self._options.get(CONF_APP_NAME_MAP, {})
|
||||
elif app_map_text.strip() == "":
|
||||
app_map = {}
|
||||
else:
|
||||
app_map = _app_map_from_text(app_map_text)
|
||||
app_map = _app_map_from_text(app_map_text, icons)
|
||||
if app_map is None:
|
||||
errors["base"] = "invalid_app_map"
|
||||
if not errors:
|
||||
@@ -524,16 +547,22 @@ class NvidiaShieldOptionsFlow(config_entries.OptionsFlow):
|
||||
self._options[CONF_CHANNEL_DIGIT_DELAY] = delay
|
||||
self._options.update(user_input)
|
||||
self._options[CONF_APP_NAME_MAP] = app_map
|
||||
# P5-2: the icon column — blank/sparse keeps prior entries?
|
||||
# No: the editor is the source of truth, so a save without
|
||||
# icons clears them (single-column stays valid).
|
||||
self._options[CONF_APP_ICON_MAP] = icons
|
||||
return self.async_create_entry(title="", data=self._options)
|
||||
|
||||
sel = _build_selectors()
|
||||
schema_dict = {}
|
||||
|
||||
# Apps name/icon map editor (P4-1, core androidtv_remote options
|
||||
# pattern, simplified): one package=name per line.
|
||||
# pattern, simplified): one package=name[|icon] per line (P5-2 adds
|
||||
# the optional icon column).
|
||||
current_map = self._options.get(CONF_APP_NAME_MAP, {})
|
||||
current_icons = self._options.get(CONF_APP_ICON_MAP, {})
|
||||
if current_map:
|
||||
app_map_default = _app_map_to_text(current_map)
|
||||
app_map_default = _app_map_to_text(current_map, current_icons)
|
||||
schema_dict[vol.Optional(CONF_APP_NAME_MAP, default=app_map_default)] = str
|
||||
else:
|
||||
schema_dict[vol.Optional(CONF_APP_NAME_MAP)] = str
|
||||
|
||||
Reference in new issue
Block a user