Android TV Remote: Add option to disable IME (#95765)

This commit is contained in:
tronikos 2023-07-24 11:00:51 -07:00 committed by GitHub
parent 2cfc11d4b9
commit d0722e2312
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 7 deletions

View file

@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from .const import DOMAIN
from .helpers import create_api
from .helpers import create_api, get_enable_ime
_LOGGER = logging.getLogger(__name__)
@ -27,7 +27,7 @@ PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER, Platform.REMOTE]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Android TV Remote from a config entry."""
api = create_api(hass, entry.data[CONF_HOST])
api = create_api(hass, entry.data[CONF_HOST], get_enable_ime(entry))
@callback
def is_available_updated(is_available: bool) -> None:
@ -76,6 +76,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
entry.async_on_unload(
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
)
entry.async_on_unload(entry.add_update_listener(update_listener))
return True
@ -87,3 +88,8 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
api.disconnect()
return unload_ok
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle options update."""
await hass.config_entries.async_reload(entry.entry_id)

View file

@ -15,11 +15,12 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components import zeroconf
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.device_registry import format_mac
from .const import DOMAIN
from .helpers import create_api
from .const import CONF_ENABLE_IME, DOMAIN
from .helpers import create_api, get_enable_ime
STEP_USER_DATA_SCHEMA = vol.Schema(
{
@ -55,7 +56,7 @@ class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is not None:
self.host = user_input["host"]
assert self.host
api = create_api(self.hass, self.host)
api = create_api(self.hass, self.host, enable_ime=False)
try:
self.name, self.mac = await api.async_get_name_and_mac()
assert self.mac
@ -75,7 +76,7 @@ class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def _async_start_pair(self) -> FlowResult:
"""Start pairing with the Android TV. Navigate to the pair flow to enter the PIN shown on screen."""
assert self.host
self.api = create_api(self.hass, self.host)
self.api = create_api(self.hass, self.host, enable_ime=False)
await self.api.async_generate_cert_if_missing()
await self.api.async_start_pairing()
return await self.async_step_pair()
@ -186,3 +187,38 @@ class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
description_placeholders={CONF_NAME: self.name},
errors=errors,
)
@staticmethod
@callback
def async_get_options_flow(
config_entry: config_entries.ConfigEntry,
) -> config_entries.OptionsFlow:
"""Create the options flow."""
return OptionsFlowHandler(config_entry)
class OptionsFlowHandler(config_entries.OptionsFlow):
"""Android TV Remote options flow."""
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize options flow."""
self.config_entry = config_entry
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Manage the options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Required(
CONF_ENABLE_IME,
default=get_enable_ime(self.config_entry),
): bool,
}
),
)

View file

@ -4,3 +4,6 @@ from __future__ import annotations
from typing import Final
DOMAIN: Final = "androidtv_remote"
CONF_ENABLE_IME: Final = "enable_ime"
CONF_ENABLE_IME_DEFAULT_VALUE: Final = True

View file

@ -3,11 +3,14 @@ from __future__ import annotations
from androidtvremote2 import AndroidTVRemote
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.storage import STORAGE_DIR
from .const import CONF_ENABLE_IME, CONF_ENABLE_IME_DEFAULT_VALUE
def create_api(hass: HomeAssistant, host: str) -> AndroidTVRemote:
def create_api(hass: HomeAssistant, host: str, enable_ime: bool) -> AndroidTVRemote:
"""Create an AndroidTVRemote instance."""
return AndroidTVRemote(
client_name="Home Assistant",
@ -15,4 +18,10 @@ def create_api(hass: HomeAssistant, host: str) -> AndroidTVRemote:
keyfile=hass.config.path(STORAGE_DIR, "androidtv_remote_key.pem"),
host=host,
loop=hass.loop,
enable_ime=enable_ime,
)
def get_enable_ime(entry: ConfigEntry) -> bool:
"""Get value of enable_ime option or its default value."""
return entry.options.get(CONF_ENABLE_IME, CONF_ENABLE_IME_DEFAULT_VALUE)

View file

@ -34,5 +34,14 @@
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"options": {
"step": {
"init": {
"data": {
"enable_ime": "Enable IME. Needed for getting the current app. Disable for devices that show 'Use keyboard on mobile device screen' instead of the on screen keyboard."
}
}
}
}
}

View file

@ -857,3 +857,59 @@ async def test_reauth_flow_cannot_connect(
await hass.async_block_till_done()
assert len(mock_unload_entry.mock_calls) == 0
assert len(mock_setup_entry.mock_calls) == 0
async def test_options_flow(
hass: HomeAssistant, mock_config_entry: MockConfigEntry, mock_api: MagicMock
) -> None:
"""Test options flow."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_api.disconnect.call_count == 0
assert mock_api.async_connect.call_count == 1
# Trigger options flow, first time
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
assert result["type"] == "form"
assert result["step_id"] == "init"
data_schema = result["data_schema"].schema
assert set(data_schema) == {"enable_ime"}
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"enable_ime": False},
)
assert result["type"] == "create_entry"
assert mock_config_entry.options == {"enable_ime": False}
await hass.async_block_till_done()
assert mock_api.disconnect.call_count == 1
assert mock_api.async_connect.call_count == 2
# Trigger options flow, second time, no change, doesn't reload
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"enable_ime": False},
)
assert result["type"] == "create_entry"
assert mock_config_entry.options == {"enable_ime": False}
await hass.async_block_till_done()
assert mock_api.disconnect.call_count == 1
assert mock_api.async_connect.call_count == 2
# Trigger options flow, third time, change, reloads
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={"enable_ime": True},
)
assert result["type"] == "create_entry"
assert mock_config_entry.options == {"enable_ime": True}
await hass.async_block_till_done()
assert mock_api.disconnect.call_count == 2
assert mock_api.async_connect.call_count == 3