Use friendly app names for Fire TV sources (#28417)

* Use friendly app names for Fire TV sources

* Remove debugging statement

* Tests pass

* Use 'blocking=True' to patch service calls

* Remove parentheses
This commit is contained in:
Jeff Irion 2019-11-07 14:04:59 -08:00 committed by Aaron Bach
parent 1012215709
commit a71d852f16
3 changed files with 145 additions and 12 deletions

View File

@ -287,8 +287,11 @@ class ADBDevice(MediaPlayerDevice):
"""Initialize the Android TV / Fire TV device."""
self.aftv = aftv
self._name = name
self._apps = APPS.copy()
self._apps.update(apps)
self._app_id_to_name = APPS.copy()
self._app_id_to_name.update(apps)
self._app_name_to_id = {
value: key for key, value in self._app_id_to_name.items()
}
self._keys = KEYS
self._device_properties = self.aftv.device_properties
@ -328,7 +331,7 @@ class ADBDevice(MediaPlayerDevice):
@property
def app_name(self):
"""Return the friendly name of the current app."""
return self._apps.get(self._current_app, self._current_app)
return self._app_id_to_name.get(self._current_app, self._current_app)
@property
def available(self):
@ -518,7 +521,7 @@ class FireTVDevice(ADBDevice):
super().__init__(aftv, name, apps, turn_on_command, turn_off_command)
self._get_sources = get_sources
self._running_apps = None
self._sources = None
@adb_decorator(override_available=True)
def update(self):
@ -538,23 +541,28 @@ class FireTVDevice(ADBDevice):
return
# Get the `state`, `current_app`, and `running_apps`.
state, self._current_app, self._running_apps = self.aftv.update(
self._get_sources
)
state, self._current_app, running_apps = self.aftv.update(self._get_sources)
self._state = ANDROIDTV_STATES.get(state)
if self._state is None:
self._available = False
if running_apps:
self._sources = [
self._app_id_to_name.get(app_id, app_id) for app_id in running_apps
]
else:
self._sources = None
@property
def source(self):
"""Return the current app."""
return self._current_app
return self._app_id_to_name.get(self._current_app, self._current_app)
@property
def source_list(self):
"""Return a list of running apps."""
return self._running_apps
return self._sources
@property
def supported_features(self):
@ -575,6 +583,7 @@ class FireTVDevice(ADBDevice):
"""
if isinstance(source, str):
if not source.startswith("!"):
self.aftv.launch_app(source)
self.aftv.launch_app(self._app_name_to_id.get(source, source))
else:
self.aftv.stop_app(source[1:].lstrip())
source_ = source[1:].lstrip()
self.aftv.stop_app(self._app_name_to_id.get(source_, source_))

View File

@ -140,3 +140,15 @@ def isfile(filepath):
PATCH_ISFILE = patch("os.path.isfile", isfile)
PATCH_ACCESS = patch("os.access", return_value=True)
def patch_firetv_update(state, current_app, running_apps):
"""Patch the `FireTV.update()` method."""
return patch(
"androidtv.firetv.FireTV.update",
return_value=(state, current_app, running_apps),
)
PATCH_LAUNCH_APP = patch("androidtv.firetv.FireTV.launch_app")
PATCH_STOP_APP = patch("androidtv.firetv.FireTV.stop_app")

View File

@ -6,15 +6,22 @@ from homeassistant.components.androidtv.media_player import (
ANDROIDTV_DOMAIN,
CONF_ADB_SERVER_IP,
CONF_ADBKEY,
CONF_APPS,
)
from homeassistant.components.media_player.const import (
ATTR_INPUT_SOURCE,
DOMAIN,
SERVICE_SELECT_SOURCE,
)
from homeassistant.components.media_player.const import DOMAIN
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_DEVICE_CLASS,
CONF_HOST,
CONF_NAME,
CONF_PLATFORM,
STATE_IDLE,
STATE_OFF,
STATE_PLAYING,
STATE_UNAVAILABLE,
)
@ -276,3 +283,108 @@ async def test_setup_with_adbkey(hass):
state = hass.states.get(entity_id)
assert state is not None
assert state.state == STATE_OFF
async def test_firetv_sources(hass):
"""Test that sources (i.e., apps) are handled correctly for Fire TV devices."""
config = CONFIG_FIRETV_ADB_SERVER.copy()
config[DOMAIN][CONF_APPS] = {"com.app.test1": "TEST 1"}
patch_key, entity_id = _setup(hass, config)
with patchers.PATCH_ADB_DEVICE, patchers.patch_connect(True)[
patch_key
], patchers.patch_shell("")[patch_key]:
assert await async_setup_component(hass, DOMAIN, config)
await hass.helpers.entity_component.async_update_entity(entity_id)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == STATE_OFF
with patchers.patch_firetv_update(
"playing", "com.app.test1", ["com.app.test1", "com.app.test2"]
):
await hass.helpers.entity_component.async_update_entity(entity_id)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == STATE_PLAYING
assert state.attributes["source"] == "TEST 1"
assert state.attributes["source_list"] == ["TEST 1", "com.app.test2"]
with patchers.patch_firetv_update(
"playing", "com.app.test2", ["com.app.test2", "com.app.test1"]
):
await hass.helpers.entity_component.async_update_entity(entity_id)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == STATE_PLAYING
assert state.attributes["source"] == "com.app.test2"
assert state.attributes["source_list"] == ["com.app.test2", "TEST 1"]
async def _test_firetv_select_source(hass, source, expected_arg, method_patch):
"""Test that the `FireTV.launch_app` and `FireTV.stop_app` methods are called with the right parameter."""
config = CONFIG_FIRETV_ADB_SERVER.copy()
config[DOMAIN][CONF_APPS] = {"com.app.test1": "TEST 1"}
patch_key, entity_id = _setup(hass, config)
with patchers.PATCH_ADB_DEVICE, patchers.patch_connect(True)[
patch_key
], patchers.patch_shell("")[patch_key]:
assert await async_setup_component(hass, DOMAIN, config)
await hass.helpers.entity_component.async_update_entity(entity_id)
state = hass.states.get(entity_id)
assert state is not None
assert state.state == STATE_OFF
with method_patch as method_patch_:
await hass.services.async_call(
DOMAIN,
SERVICE_SELECT_SOURCE,
{ATTR_ENTITY_ID: entity_id, ATTR_INPUT_SOURCE: source},
blocking=True,
)
method_patch_.assert_called_with(expected_arg)
return True
async def test_firetv_select_source_launch_app_id(hass):
"""Test that an app can be launched using its app ID."""
assert await _test_firetv_select_source(
hass, "com.app.test1", "com.app.test1", patchers.PATCH_LAUNCH_APP
)
async def test_firetv_select_source_launch_app_name(hass):
"""Test that an app can be launched using its friendly name."""
assert await _test_firetv_select_source(
hass, "TEST 1", "com.app.test1", patchers.PATCH_LAUNCH_APP
)
async def test_firetv_select_source_launch_app_id_no_name(hass):
"""Test that an app can be launched using its app ID when it has no friendly name."""
assert await _test_firetv_select_source(
hass, "com.app.test2", "com.app.test2", patchers.PATCH_LAUNCH_APP
)
async def test_firetv_select_source_stop_app_id(hass):
"""Test that an app can be stopped using its app ID."""
assert await _test_firetv_select_source(
hass, "!com.app.test1", "com.app.test1", patchers.PATCH_STOP_APP
)
async def test_firetv_select_source_stop_app_name(hass):
"""Test that an app can be stopped using its friendly name."""
assert await _test_firetv_select_source(
hass, "!TEST 1", "com.app.test1", patchers.PATCH_STOP_APP
)
async def test_firetv_select_source_stop_app_id_no_name(hass):
"""Test that an app can be stopped using its app ID when it has no friendly name."""
assert await _test_firetv_select_source(
hass, "!com.app.test2", "com.app.test2", patchers.PATCH_STOP_APP
)