Allow filtering of sources for Android TV (#30994)
parent
61e41f0ddc
commit
31dc2ad284
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue