Allow filtering of sources for Android TV (#30994)

pull/31288/head
Jeff Irion 2020-01-29 12:13:09 -08:00 committed by GitHub
parent 61e41f0ddc
commit 31dc2ad284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 177 additions and 17 deletions

View File

@ -82,6 +82,7 @@ CONF_ADBKEY = "adbkey"
CONF_ADB_SERVER_IP = "adb_server_ip"
CONF_ADB_SERVER_PORT = "adb_server_port"
CONF_APPS = "apps"
CONF_EXCLUDE_UNNAMED_APPS = "exclude_unnamed_apps"
CONF_GET_SOURCES = "get_sources"
CONF_STATE_DETECTION_RULES = "state_detection_rules"
CONF_TURN_ON_COMMAND = "turn_on_command"
@ -134,12 +135,15 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
vol.Optional(CONF_ADB_SERVER_IP): cv.string,
vol.Optional(CONF_ADB_SERVER_PORT, default=DEFAULT_ADB_SERVER_PORT): cv.port,
vol.Optional(CONF_GET_SOURCES, default=DEFAULT_GET_SOURCES): cv.boolean,
vol.Optional(CONF_APPS, default=dict()): vol.Schema({cv.string: cv.string}),
vol.Optional(CONF_APPS, default=dict()): vol.Schema(
{cv.string: vol.Any(cv.string, None)}
),
vol.Optional(CONF_TURN_ON_COMMAND): cv.string,
vol.Optional(CONF_TURN_OFF_COMMAND): cv.string,
vol.Optional(CONF_STATE_DETECTION_RULES, default={}): vol.Schema(
{cv.string: ha_state_detection_rules_validator(vol.Invalid)}
),
vol.Optional(CONF_EXCLUDE_UNNAMED_APPS, default=False): cv.boolean,
}
)
@ -232,6 +236,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
config[CONF_GET_SOURCES],
config.get(CONF_TURN_ON_COMMAND),
config.get(CONF_TURN_OFF_COMMAND),
config[CONF_EXCLUDE_UNNAMED_APPS],
]
if aftv.DEVICE_CLASS == DEVICE_ANDROIDTV:
@ -367,7 +372,14 @@ class ADBDevice(MediaPlayerDevice):
"""Representation of an Android TV or Fire TV device."""
def __init__(
self, aftv, name, apps, get_sources, turn_on_command, turn_off_command
self,
aftv,
name,
apps,
get_sources,
turn_on_command,
turn_off_command,
exclude_unnamed_apps,
):
"""Initialize the Android TV / Fire TV device."""
self.aftv = aftv
@ -375,7 +387,7 @@ class ADBDevice(MediaPlayerDevice):
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()
value: key for key, value in self._app_id_to_name.items() if value
}
self._get_sources = get_sources
self._keys = KEYS
@ -386,6 +398,8 @@ class ADBDevice(MediaPlayerDevice):
self.turn_on_command = turn_on_command
self.turn_off_command = turn_off_command
self._exclude_unnamed_apps = exclude_unnamed_apps
# ADB exceptions to catch
if not self.aftv.adb_server_ip:
# Using "adb_shell" (Python ADB implementation)
@ -561,11 +575,24 @@ class AndroidTVDevice(ADBDevice):
"""Representation of an Android TV device."""
def __init__(
self, aftv, name, apps, get_sources, turn_on_command, turn_off_command
self,
aftv,
name,
apps,
get_sources,
turn_on_command,
turn_off_command,
exclude_unnamed_apps,
):
"""Initialize the Android TV device."""
super().__init__(
aftv, name, apps, get_sources, turn_on_command, turn_off_command
aftv,
name,
apps,
get_sources,
turn_on_command,
turn_off_command,
exclude_unnamed_apps,
)
self._is_volume_muted = None
@ -603,9 +630,13 @@ class AndroidTVDevice(ADBDevice):
self._available = False
if running_apps:
self._sources = [
self._app_id_to_name.get(app_id, app_id) for app_id in running_apps
sources = [
self._app_id_to_name.get(
app_id, app_id if not self._exclude_unnamed_apps else None
)
for app_id in running_apps
]
self._sources = [source for source in sources if source]
else:
self._sources = None
@ -678,9 +709,13 @@ class FireTVDevice(ADBDevice):
self._available = False
if running_apps:
self._sources = [
self._app_id_to_name.get(app_id, app_id) for app_id in running_apps
sources = [
self._app_id_to_name.get(
app_id, app_id if not self._exclude_unnamed_apps else None
)
for app_id in running_apps
]
self._sources = [source for source in sources if source]
else:
self._sources = None

View File

@ -12,6 +12,7 @@ from homeassistant.components.androidtv.media_player import (
CONF_ADB_SERVER_IP,
CONF_ADBKEY,
CONF_APPS,
CONF_EXCLUDE_UNNAMED_APPS,
KEYS,
SERVICE_ADB_COMMAND,
SERVICE_DOWNLOAD,
@ -300,7 +301,11 @@ async def test_setup_with_adbkey(hass):
async def _test_sources(hass, config0):
"""Test that sources (i.e., apps) are handled correctly for Android TV and Fire TV devices."""
config = config0.copy()
config[DOMAIN][CONF_APPS] = {"com.app.test1": "TEST 1"}
config[DOMAIN][CONF_APPS] = {
"com.app.test1": "TEST 1",
"com.app.test3": None,
"com.app.test4": "",
}
patch_key, entity_id = _setup(config)
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
@ -316,14 +321,16 @@ async def _test_sources(hass, config0):
patch_update = patchers.patch_androidtv_update(
"playing",
"com.app.test1",
["com.app.test1", "com.app.test2"],
["com.app.test1", "com.app.test2", "com.app.test3", "com.app.test4"],
"hdmi",
False,
1,
)
else:
patch_update = patchers.patch_firetv_update(
"playing", "com.app.test1", ["com.app.test1", "com.app.test2"]
"playing",
"com.app.test1",
["com.app.test1", "com.app.test2", "com.app.test3", "com.app.test4"],
)
with patch_update:
@ -332,20 +339,22 @@ async def _test_sources(hass, config0):
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"]
assert sorted(state.attributes["source_list"]) == ["TEST 1", "com.app.test2"]
if config[DOMAIN].get(CONF_DEVICE_CLASS) != "firetv":
patch_update = patchers.patch_androidtv_update(
"playing",
"com.app.test2",
["com.app.test2", "com.app.test1"],
["com.app.test2", "com.app.test1", "com.app.test3", "com.app.test4"],
"hdmi",
True,
0,
)
else:
patch_update = patchers.patch_firetv_update(
"playing", "com.app.test2", ["com.app.test2", "com.app.test1"]
"playing",
"com.app.test2",
["com.app.test2", "com.app.test1", "com.app.test3", "com.app.test4"],
)
with patch_update:
@ -354,7 +363,7 @@ async def _test_sources(hass, config0):
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"]
assert sorted(state.attributes["source_list"]) == ["TEST 1", "com.app.test2"]
return True
@ -369,10 +378,82 @@ async def test_firetv_sources(hass):
assert await _test_sources(hass, CONFIG_FIRETV_ADB_SERVER)
async def _test_exclude_sources(hass, config0, expected_sources):
"""Test that sources (i.e., apps) are handled correctly when the `exclude_unnamed_apps` config parameter is provided."""
config = config0.copy()
config[DOMAIN][CONF_APPS] = {
"com.app.test1": "TEST 1",
"com.app.test3": None,
"com.app.test4": "",
}
patch_key, entity_id = _setup(config)
with patchers.PATCH_ADB_DEVICE_TCP, 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
if config[DOMAIN].get(CONF_DEVICE_CLASS) != "firetv":
patch_update = patchers.patch_androidtv_update(
"playing",
"com.app.test1",
[
"com.app.test1",
"com.app.test2",
"com.app.test3",
"com.app.test4",
"com.app.test5",
],
"hdmi",
False,
1,
)
else:
patch_update = patchers.patch_firetv_update(
"playing",
"com.app.test1",
[
"com.app.test1",
"com.app.test2",
"com.app.test3",
"com.app.test4",
"com.app.test5",
],
)
with patch_update:
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 sorted(state.attributes["source_list"]) == expected_sources
return True
async def test_androidtv_exclude_sources(hass):
"""Test that sources (i.e., apps) are handled correctly for Android TV devices when the `exclude_unnamed_apps` config parameter is provided as true."""
config = CONFIG_ANDROIDTV_ADB_SERVER.copy()
config[DOMAIN][CONF_EXCLUDE_UNNAMED_APPS] = True
assert await _test_exclude_sources(hass, config, ["TEST 1"])
async def test_firetv_exclude_sources(hass):
"""Test that sources (i.e., apps) are handled correctly for Fire TV devices when the `exclude_unnamed_apps` config parameter is provided as true."""
config = CONFIG_FIRETV_ADB_SERVER.copy()
config[DOMAIN][CONF_EXCLUDE_UNNAMED_APPS] = True
assert await _test_exclude_sources(hass, config, ["TEST 1"])
async def _test_select_source(hass, config0, source, expected_arg, method_patch):
"""Test that the methods for launching and stopping apps are called correctly when selecting a source."""
config = config0.copy()
config[DOMAIN][CONF_APPS] = {"com.app.test1": "TEST 1"}
config[DOMAIN][CONF_APPS] = {"com.app.test1": "TEST 1", "com.app.test3": None}
patch_key, entity_id = _setup(config)
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
@ -429,6 +510,17 @@ async def test_androidtv_select_source_launch_app_id_no_name(hass):
)
async def test_androidtv_select_source_launch_app_hidden(hass):
"""Test that an app can be launched using its app ID when it is hidden from the sources list."""
assert await _test_select_source(
hass,
CONFIG_ANDROIDTV_ADB_SERVER,
"com.app.test3",
"com.app.test3",
patchers.PATCH_LAUNCH_APP,
)
async def test_androidtv_select_source_stop_app_id(hass):
"""Test that an app can be stopped using its app ID."""
assert await _test_select_source(
@ -462,6 +554,17 @@ async def test_androidtv_select_source_stop_app_id_no_name(hass):
)
async def test_androidtv_select_source_stop_app_hidden(hass):
"""Test that an app can be stopped using its app ID when it is hidden from the sources list."""
assert await _test_select_source(
hass,
CONFIG_ANDROIDTV_ADB_SERVER,
"!com.app.test3",
"com.app.test3",
patchers.PATCH_STOP_APP,
)
async def test_firetv_select_source_launch_app_id(hass):
"""Test that an app can be launched using its app ID."""
assert await _test_select_source(
@ -495,6 +598,17 @@ async def test_firetv_select_source_launch_app_id_no_name(hass):
)
async def test_firetv_select_source_launch_app_hidden(hass):
"""Test that an app can be launched using its app ID when it is hidden from the sources list."""
assert await _test_select_source(
hass,
CONFIG_FIRETV_ADB_SERVER,
"com.app.test3",
"com.app.test3",
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_select_source(
@ -528,6 +642,17 @@ async def test_firetv_select_source_stop_app_id_no_name(hass):
)
async def test_firetv_select_source_stop_hidden(hass):
"""Test that an app can be stopped using its app ID when it is hidden from the sources list."""
assert await _test_select_source(
hass,
CONFIG_FIRETV_ADB_SERVER,
"!com.app.test3",
"com.app.test3",
patchers.PATCH_STOP_APP,
)
async def _test_setup_fail(hass, config):
"""Test that the entity is not created when the ADB connection is not established."""
patch_key, entity_id = _setup(config)