Register 'androidtv.download' and 'androidtv.upload' services (#30086)
* Add tests * Add FileSync test * Fill in services.yaml for 'androidtv.adb_filesync' service * Update example paths in services.yaml * Bump androidtv to 0.0.37 * Bump androidtv to 0.0.37 * Bump androidtv to 0.0.37 * Import LockNotAcquiredException * Import LockNotAcquiredException from androidtv.exceptions * Rename 'host' to 'address' * Add a logging statement when an ADB command is skipped * Check hass.config.is_allowed_path(local_path) * Add return * Fix pylint * Reduce duplicated code (AndroidTVDevice vs. FireTVDevice) * Split 'adb_filesync' service into 'download' and 'upload' services * Don't use '.get()' for required data; return if the services are already registered * Replace "command" with ATTR_COMMAND * Don't try to connect to a device if it is a duplicatepull/30528/head
parent
e88bfda2a8
commit
5ec5df77cc
|
@ -4,7 +4,7 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/androidtv",
|
||||
"requirements": [
|
||||
"adb-shell==0.1.0",
|
||||
"androidtv==0.0.36",
|
||||
"androidtv==0.0.37",
|
||||
"pure-python-adb==0.2.2.dev0"
|
||||
],
|
||||
"dependencies": [],
|
||||
|
|
|
@ -12,6 +12,7 @@ from adb_shell.exceptions import (
|
|||
)
|
||||
from androidtv import ha_state_detection_rules_validator, setup
|
||||
from androidtv.constants import APPS, KEYS
|
||||
from androidtv.exceptions import LockNotAcquiredException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.media_player import PLATFORM_SCHEMA, MediaPlayerDevice
|
||||
|
@ -72,6 +73,9 @@ SUPPORT_FIRETV = (
|
|||
| SUPPORT_STOP
|
||||
)
|
||||
|
||||
ATTR_DEVICE_PATH = "device_path"
|
||||
ATTR_LOCAL_PATH = "local_path"
|
||||
|
||||
CONF_ADBKEY = "adbkey"
|
||||
CONF_ADB_SERVER_IP = "adb_server_ip"
|
||||
CONF_ADB_SERVER_PORT = "adb_server_port"
|
||||
|
@ -92,11 +96,29 @@ DEVICE_FIRETV = "firetv"
|
|||
DEVICE_CLASSES = [DEFAULT_DEVICE_CLASS, DEVICE_ANDROIDTV, DEVICE_FIRETV]
|
||||
|
||||
SERVICE_ADB_COMMAND = "adb_command"
|
||||
SERVICE_DOWNLOAD = "download"
|
||||
SERVICE_UPLOAD = "upload"
|
||||
|
||||
SERVICE_ADB_COMMAND_SCHEMA = vol.Schema(
|
||||
{vol.Required(ATTR_ENTITY_ID): cv.entity_ids, vol.Required(ATTR_COMMAND): cv.string}
|
||||
)
|
||||
|
||||
SERVICE_DOWNLOAD_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_id,
|
||||
vol.Required(ATTR_DEVICE_PATH): cv.string,
|
||||
vol.Required(ATTR_LOCAL_PATH): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_UPLOAD_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_ENTITY_ID): cv.entity_ids,
|
||||
vol.Required(ATTR_DEVICE_PATH): cv.string,
|
||||
vol.Required(ATTR_LOCAL_PATH): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
|
@ -133,7 +155,11 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
"""Set up the Android TV / Fire TV platform."""
|
||||
hass.data.setdefault(ANDROIDTV_DOMAIN, {})
|
||||
|
||||
host = f"{config[CONF_HOST]}:{config[CONF_PORT]}"
|
||||
address = f"{config[CONF_HOST]}:{config[CONF_PORT]}"
|
||||
|
||||
if address in hass.data[ANDROIDTV_DOMAIN]:
|
||||
_LOGGER.warning("Platform already setup on %s, skipping", address)
|
||||
return
|
||||
|
||||
if CONF_ADB_SERVER_IP not in config:
|
||||
# Use "adb_shell" (Python ADB implementation)
|
||||
|
@ -192,44 +218,38 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
else:
|
||||
device_name = "Android TV / Fire TV device"
|
||||
|
||||
_LOGGER.warning("Could not connect to %s at %s %s", device_name, host, adb_log)
|
||||
_LOGGER.warning(
|
||||
"Could not connect to %s at %s %s", device_name, address, adb_log
|
||||
)
|
||||
raise PlatformNotReady
|
||||
|
||||
if host in hass.data[ANDROIDTV_DOMAIN]:
|
||||
_LOGGER.warning("Platform already setup on %s, skipping", host)
|
||||
else:
|
||||
if aftv.DEVICE_CLASS == DEVICE_ANDROIDTV:
|
||||
device = AndroidTVDevice(
|
||||
aftv,
|
||||
config[CONF_NAME],
|
||||
config[CONF_APPS],
|
||||
config[CONF_GET_SOURCES],
|
||||
config.get(CONF_TURN_ON_COMMAND),
|
||||
config.get(CONF_TURN_OFF_COMMAND),
|
||||
)
|
||||
device_name = config[CONF_NAME] if CONF_NAME in config else "Android TV"
|
||||
else:
|
||||
device = FireTVDevice(
|
||||
aftv,
|
||||
config[CONF_NAME],
|
||||
config[CONF_APPS],
|
||||
config[CONF_GET_SOURCES],
|
||||
config.get(CONF_TURN_ON_COMMAND),
|
||||
config.get(CONF_TURN_OFF_COMMAND),
|
||||
)
|
||||
device_name = config[CONF_NAME] if CONF_NAME in config else "Fire TV"
|
||||
device_args = [
|
||||
aftv,
|
||||
config[CONF_NAME],
|
||||
config[CONF_APPS],
|
||||
config[CONF_GET_SOURCES],
|
||||
config.get(CONF_TURN_ON_COMMAND),
|
||||
config.get(CONF_TURN_OFF_COMMAND),
|
||||
]
|
||||
|
||||
add_entities([device])
|
||||
_LOGGER.debug("Setup %s at %s %s", device_name, host, adb_log)
|
||||
hass.data[ANDROIDTV_DOMAIN][host] = device
|
||||
if aftv.DEVICE_CLASS == DEVICE_ANDROIDTV:
|
||||
device = AndroidTVDevice(*device_args)
|
||||
device_name = config.get(CONF_NAME, "Android TV")
|
||||
else:
|
||||
device = FireTVDevice(*device_args)
|
||||
device_name = config.get(CONF_NAME, "Fire TV")
|
||||
|
||||
add_entities([device])
|
||||
_LOGGER.debug("Setup %s at %s %s", device_name, address, adb_log)
|
||||
hass.data[ANDROIDTV_DOMAIN][address] = device
|
||||
|
||||
if hass.services.has_service(ANDROIDTV_DOMAIN, SERVICE_ADB_COMMAND):
|
||||
return
|
||||
|
||||
def service_adb_command(service):
|
||||
"""Dispatch service calls to target entities."""
|
||||
cmd = service.data.get(ATTR_COMMAND)
|
||||
entity_id = service.data.get(ATTR_ENTITY_ID)
|
||||
cmd = service.data[ATTR_COMMAND]
|
||||
entity_id = service.data[ATTR_ENTITY_ID]
|
||||
target_devices = [
|
||||
dev
|
||||
for dev in hass.data[ANDROIDTV_DOMAIN].values()
|
||||
|
@ -255,6 +275,52 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
|||
schema=SERVICE_ADB_COMMAND_SCHEMA,
|
||||
)
|
||||
|
||||
def service_download(service):
|
||||
"""Download a file from your Android TV / Fire TV device to your Home Assistant instance."""
|
||||
local_path = service.data[ATTR_LOCAL_PATH]
|
||||
if not hass.config.is_allowed_path(local_path):
|
||||
_LOGGER.warning("'%s' is not secure to load data from!", local_path)
|
||||
return
|
||||
|
||||
device_path = service.data[ATTR_DEVICE_PATH]
|
||||
entity_id = service.data[ATTR_ENTITY_ID]
|
||||
target_device = [
|
||||
dev
|
||||
for dev in hass.data[ANDROIDTV_DOMAIN].values()
|
||||
if dev.entity_id in entity_id
|
||||
][0]
|
||||
|
||||
target_device.adb_pull(local_path, device_path)
|
||||
|
||||
hass.services.register(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_DOWNLOAD,
|
||||
service_download,
|
||||
schema=SERVICE_DOWNLOAD_SCHEMA,
|
||||
)
|
||||
|
||||
def service_upload(service):
|
||||
"""Upload a file from your Home Assistant instance to an Android TV / Fire TV device."""
|
||||
local_path = service.data[ATTR_LOCAL_PATH]
|
||||
if not hass.config.is_allowed_path(local_path):
|
||||
_LOGGER.warning("'%s' is not secure to load data from!", local_path)
|
||||
return
|
||||
|
||||
device_path = service.data[ATTR_DEVICE_PATH]
|
||||
entity_id = service.data[ATTR_ENTITY_ID]
|
||||
target_devices = [
|
||||
dev
|
||||
for dev in hass.data[ANDROIDTV_DOMAIN].values()
|
||||
if dev.entity_id in entity_id
|
||||
]
|
||||
|
||||
for target_device in target_devices:
|
||||
target_device.adb_push(local_path, device_path)
|
||||
|
||||
hass.services.register(
|
||||
ANDROIDTV_DOMAIN, SERVICE_UPLOAD, service_upload, schema=SERVICE_UPLOAD_SCHEMA,
|
||||
)
|
||||
|
||||
|
||||
def adb_decorator(override_available=False):
|
||||
"""Wrap ADB methods and catch exceptions.
|
||||
|
@ -274,6 +340,12 @@ def adb_decorator(override_available=False):
|
|||
|
||||
try:
|
||||
return func(self, *args, **kwargs)
|
||||
except LockNotAcquiredException:
|
||||
# If the ADB lock could not be acquired, skip this command
|
||||
_LOGGER.info(
|
||||
"ADB command not executed because the connection is currently in use"
|
||||
)
|
||||
return
|
||||
except self.exceptions as err:
|
||||
_LOGGER.error(
|
||||
"Failed to execute an ADB command. ADB connection re-"
|
||||
|
@ -465,6 +537,16 @@ class ADBDevice(MediaPlayerDevice):
|
|||
self.schedule_update_ha_state()
|
||||
return self._adb_response
|
||||
|
||||
@adb_decorator()
|
||||
def adb_pull(self, local_path, device_path):
|
||||
"""Download a file from your Android TV / Fire TV device to your Home Assistant instance."""
|
||||
self.aftv.adb_pull(local_path, device_path)
|
||||
|
||||
@adb_decorator()
|
||||
def adb_push(self, local_path, device_path):
|
||||
"""Upload a file from your Home Assistant instance to an Android TV / Fire TV device."""
|
||||
self.aftv.adb_push(local_path, device_path)
|
||||
|
||||
|
||||
class AndroidTVDevice(ADBDevice):
|
||||
"""Representation of an Android TV device."""
|
||||
|
|
|
@ -9,3 +9,27 @@ adb_command:
|
|||
command:
|
||||
description: Either a key command or an ADB shell command.
|
||||
example: 'HOME'
|
||||
download:
|
||||
description: Download a file from your Android TV / Fire TV device to your Home Assistant instance.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of Android TV / Fire TV entity.
|
||||
example: 'media_player.android_tv_living_room'
|
||||
device_path:
|
||||
description: The filepath on the Android TV / Fire TV device.
|
||||
example: '/storage/emulated/0/Download/example.txt'
|
||||
local_path:
|
||||
description: The filepath on your Home Assistant instance.
|
||||
example: '/config/example.txt'
|
||||
upload:
|
||||
description: Upload a file from your Home Assistant instance to an Android TV / Fire TV device.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name(s) of Android TV / Fire TV entities.
|
||||
example: 'media_player.android_tv_living_room'
|
||||
device_path:
|
||||
description: The filepath on the Android TV / Fire TV device.
|
||||
example: '/storage/emulated/0/Download/example.txt'
|
||||
local_path:
|
||||
description: The filepath on your Home Assistant instance.
|
||||
example: '/config/example.txt'
|
||||
|
|
|
@ -220,7 +220,7 @@ ambiclimate==0.2.1
|
|||
amcrest==1.5.3
|
||||
|
||||
# homeassistant.components.androidtv
|
||||
androidtv==0.0.36
|
||||
androidtv==0.0.37
|
||||
|
||||
# homeassistant.components.anel_pwrctrl
|
||||
anel_pwrctrl-homeassistant==0.0.1.dev2
|
||||
|
|
|
@ -87,7 +87,7 @@ airly==0.0.2
|
|||
ambiclimate==0.2.1
|
||||
|
||||
# homeassistant.components.androidtv
|
||||
androidtv==0.0.36
|
||||
androidtv==0.0.37
|
||||
|
||||
# homeassistant.components.apns
|
||||
apns2==0.3.0
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
"""The tests for the androidtv platform."""
|
||||
import logging
|
||||
from unittest.mock import patch
|
||||
|
||||
from androidtv.exceptions import LockNotAcquiredException
|
||||
|
||||
from homeassistant.components.androidtv.media_player import (
|
||||
ANDROIDTV_DOMAIN,
|
||||
ATTR_COMMAND,
|
||||
ATTR_DEVICE_PATH,
|
||||
ATTR_LOCAL_PATH,
|
||||
CONF_ADB_SERVER_IP,
|
||||
CONF_ADBKEY,
|
||||
CONF_APPS,
|
||||
KEYS,
|
||||
SERVICE_ADB_COMMAND,
|
||||
SERVICE_DOWNLOAD,
|
||||
SERVICE_UPLOAD,
|
||||
)
|
||||
from homeassistant.components.media_player.const import (
|
||||
ATTR_INPUT_SOURCE,
|
||||
|
@ -70,7 +80,7 @@ CONFIG_FIRETV_ADB_SERVER = {
|
|||
}
|
||||
|
||||
|
||||
def _setup(hass, config):
|
||||
def _setup(config):
|
||||
"""Perform common setup tasks for the tests."""
|
||||
if CONF_ADB_SERVER_IP not in config[DOMAIN]:
|
||||
patch_key = "python"
|
||||
|
@ -93,7 +103,7 @@ async def _test_reconnect(hass, caplog, config):
|
|||
|
||||
https://developers.home-assistant.io/docs/en/integration_quality_scale_index.html
|
||||
"""
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
patch_key, entity_id = _setup(config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
|
@ -164,7 +174,7 @@ async def _test_adb_shell_returns_none(hass, config):
|
|||
|
||||
The state should be `None` and the device should be unavailable.
|
||||
"""
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
patch_key, entity_id = _setup(config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
|
@ -272,7 +282,7 @@ async def test_setup_with_adbkey(hass):
|
|||
"""Test that setup succeeds when using an ADB key."""
|
||||
config = CONFIG_ANDROIDTV_PYTHON_ADB.copy()
|
||||
config[DOMAIN][CONF_ADBKEY] = hass.config.path("user_provided_adbkey")
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
patch_key, entity_id = _setup(config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
|
@ -290,7 +300,7 @@ 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"}
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
patch_key, entity_id = _setup(config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
|
@ -362,7 +372,7 @@ 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"}
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
patch_key, entity_id = _setup(config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
|
@ -519,7 +529,7 @@ async def test_firetv_select_source_stop_app_id_no_name(hass):
|
|||
|
||||
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(hass, config)
|
||||
patch_key, entity_id = _setup(config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(False)[
|
||||
patch_key
|
||||
|
@ -569,14 +579,216 @@ async def test_setup_two_devices(hass):
|
|||
|
||||
async def test_setup_same_device_twice(hass):
|
||||
"""Test that setup succeeds with a duplicated config entry."""
|
||||
patch_key, entity_id = _setup(CONFIG_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
|
||||
assert hass.services.has_service(ANDROIDTV_DOMAIN, SERVICE_ADB_COMMAND)
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
|
||||
async def test_adb_command(hass):
|
||||
"""Test sending a command via the `androidtv.adb_command` service."""
|
||||
patch_key, entity_id = _setup(CONFIG_ANDROIDTV_ADB_SERVER)
|
||||
command = "test command"
|
||||
response = "test response"
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
with patch(
|
||||
"androidtv.basetv.BaseTV.adb_shell", return_value=response
|
||||
) as patch_shell:
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_ADB_COMMAND,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
patch_shell.assert_called_with(command)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.attributes["adb_response"] == response
|
||||
|
||||
|
||||
async def test_adb_command_key(hass):
|
||||
"""Test sending a key command via the `androidtv.adb_command` service."""
|
||||
patch_key = "server"
|
||||
entity_id = "media_player.android_tv"
|
||||
command = "HOME"
|
||||
response = None
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
with patch(
|
||||
"androidtv.basetv.BaseTV.adb_shell", return_value=response
|
||||
) as patch_shell:
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_ADB_COMMAND,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
patch_shell.assert_called_with(f"input keyevent {KEYS[command]}")
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.attributes["adb_response"] is None
|
||||
|
||||
|
||||
async def test_adb_command_get_properties(hass):
|
||||
"""Test sending the "GET_PROPERTIES" command via the `androidtv.adb_command` service."""
|
||||
patch_key = "server"
|
||||
entity_id = "media_player.android_tv"
|
||||
command = "GET_PROPERTIES"
|
||||
response = {"test key": "test value"}
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
with patch(
|
||||
"androidtv.androidtv.AndroidTV.get_properties_dict", return_value=response
|
||||
) as patch_get_props:
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_ADB_COMMAND,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_COMMAND: command},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
patch_get_props.assert_called()
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.attributes["adb_response"] == str(response)
|
||||
|
||||
|
||||
async def test_update_lock_not_acquired(hass):
|
||||
"""Test that the state does not get updated when a `LockNotAcquiredException` is raised."""
|
||||
patch_key, entity_id = _setup(CONFIG_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
with patchers.patch_shell("")[patch_key]:
|
||||
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 patch(
|
||||
"androidtv.androidtv.AndroidTV.update", side_effect=LockNotAcquiredException
|
||||
):
|
||||
with patchers.patch_shell("1")[patch_key]:
|
||||
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_shell("1")[patch_key]:
|
||||
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_IDLE
|
||||
|
||||
|
||||
async def test_download(hass):
|
||||
"""Test the `androidtv.download` service."""
|
||||
patch_key, entity_id = _setup(CONFIG_ANDROIDTV_ADB_SERVER)
|
||||
device_path = "device/path"
|
||||
local_path = "local/path"
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
# Failed download because path is not whitelisted
|
||||
with patch("androidtv.basetv.BaseTV.adb_pull") as patch_pull:
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_DOWNLOAD,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_DEVICE_PATH: device_path,
|
||||
ATTR_LOCAL_PATH: local_path,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
patch_pull.assert_not_called()
|
||||
|
||||
# Successful download
|
||||
with patch("androidtv.basetv.BaseTV.adb_pull") as patch_pull, patch.object(
|
||||
hass.config, "is_allowed_path", return_value=True
|
||||
):
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_DOWNLOAD,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_DEVICE_PATH: device_path,
|
||||
ATTR_LOCAL_PATH: local_path,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
patch_pull.assert_called_with(local_path, device_path)
|
||||
|
||||
|
||||
async def test_upload(hass):
|
||||
"""Test the `androidtv.upload` service."""
|
||||
patch_key, entity_id = _setup(CONFIG_ANDROIDTV_ADB_SERVER)
|
||||
device_path = "device/path"
|
||||
local_path = "local/path"
|
||||
|
||||
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_ANDROIDTV_ADB_SERVER)
|
||||
|
||||
# Failed upload because path is not whitelisted
|
||||
with patch("androidtv.basetv.BaseTV.adb_push") as patch_push:
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_UPLOAD,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_DEVICE_PATH: device_path,
|
||||
ATTR_LOCAL_PATH: local_path,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
patch_push.assert_not_called()
|
||||
|
||||
# Successful upload
|
||||
with patch("androidtv.basetv.BaseTV.adb_push") as patch_push, patch.object(
|
||||
hass.config, "is_allowed_path", return_value=True
|
||||
):
|
||||
await hass.services.async_call(
|
||||
ANDROIDTV_DOMAIN,
|
||||
SERVICE_UPLOAD,
|
||||
{
|
||||
ATTR_ENTITY_ID: entity_id,
|
||||
ATTR_DEVICE_PATH: device_path,
|
||||
ATTR_LOCAL_PATH: local_path,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
patch_push.assert_called_with(local_path, device_path)
|
||||
|
|
Loading…
Reference in New Issue