mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Generate ADB key for Android TV integration (#27344)
* Generate ADB key for Android TV integration * Remove 'do_nothing' function * Remove 'return True' * Re-add 2 'return True' lines
This commit is contained in:
parent
2c535c92bd
commit
28cef89e03
@ -3,8 +3,8 @@
|
||||
"name": "Androidtv",
|
||||
"documentation": "https://www.home-assistant.io/integrations/androidtv",
|
||||
"requirements": [
|
||||
"adb-shell==0.0.4",
|
||||
"androidtv==0.0.30"
|
||||
"adb-shell==0.0.7",
|
||||
"androidtv==0.0.32"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@JeffLIrion"]
|
||||
|
@ -1,8 +1,10 @@
|
||||
"""Support for functionality to interact with Android TV / Fire TV devices."""
|
||||
import functools
|
||||
import logging
|
||||
import os
|
||||
import voluptuous as vol
|
||||
|
||||
from adb_shell.auth.keygen import keygen
|
||||
from adb_shell.exceptions import (
|
||||
InvalidChecksumError,
|
||||
InvalidCommandError,
|
||||
@ -40,6 +42,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.storage import STORAGE_DIR
|
||||
|
||||
ANDROIDTV_DOMAIN = "androidtv"
|
||||
|
||||
@ -133,27 +136,39 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
|
||||
if CONF_ADB_SERVER_IP not in config:
|
||||
# Use "adb_shell" (Python ADB implementation)
|
||||
adb_log = "using Python ADB implementation " + (
|
||||
f"with adbkey='{config[CONF_ADBKEY]}'"
|
||||
if CONF_ADBKEY in config
|
||||
else "without adbkey authentication"
|
||||
)
|
||||
if CONF_ADBKEY in config:
|
||||
if CONF_ADBKEY not in config:
|
||||
# Generate ADB key files (if they don't exist)
|
||||
adbkey = hass.config.path(STORAGE_DIR, "androidtv_adbkey")
|
||||
if not os.path.isfile(adbkey):
|
||||
keygen(adbkey)
|
||||
|
||||
adb_log = f"using Python ADB implementation with adbkey='{adbkey}'"
|
||||
|
||||
aftv = setup(
|
||||
host,
|
||||
adbkey,
|
||||
device_class=config[CONF_DEVICE_CLASS],
|
||||
state_detection_rules=config[CONF_STATE_DETECTION_RULES],
|
||||
auth_timeout_s=10.0,
|
||||
)
|
||||
|
||||
else:
|
||||
adb_log = (
|
||||
f"using Python ADB implementation with adbkey='{config[CONF_ADBKEY]}'"
|
||||
)
|
||||
|
||||
aftv = setup(
|
||||
host,
|
||||
config[CONF_ADBKEY],
|
||||
device_class=config[CONF_DEVICE_CLASS],
|
||||
state_detection_rules=config[CONF_STATE_DETECTION_RULES],
|
||||
auth_timeout_s=10.0,
|
||||
)
|
||||
|
||||
else:
|
||||
aftv = setup(
|
||||
host,
|
||||
device_class=config[CONF_DEVICE_CLASS],
|
||||
state_detection_rules=config[CONF_STATE_DETECTION_RULES],
|
||||
)
|
||||
else:
|
||||
# Use "pure-python-adb" (communicate with ADB server)
|
||||
adb_log = f"using ADB server at {config[CONF_ADB_SERVER_IP]}:{config[CONF_ADB_SERVER_PORT]}"
|
||||
|
||||
aftv = setup(
|
||||
host,
|
||||
adb_server_ip=config[CONF_ADB_SERVER_IP],
|
||||
@ -161,7 +176,6 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
device_class=config[CONF_DEVICE_CLASS],
|
||||
state_detection_rules=config[CONF_STATE_DETECTION_RULES],
|
||||
)
|
||||
adb_log = f"using ADB server at {config[CONF_ADB_SERVER_IP]}:{config[CONF_ADB_SERVER_PORT]}"
|
||||
|
||||
if not aftv.available:
|
||||
# Determine the name that will be used for the device in the log
|
||||
@ -257,7 +271,7 @@ def adb_decorator(override_available=False):
|
||||
"establishing attempt in the next update. Error: %s",
|
||||
err,
|
||||
)
|
||||
self.aftv.adb.close()
|
||||
self.aftv.adb_close()
|
||||
self._available = False # pylint: disable=protected-access
|
||||
return None
|
||||
|
||||
@ -429,7 +443,7 @@ class AndroidTVDevice(ADBDevice):
|
||||
# Check if device is disconnected.
|
||||
if not self._available:
|
||||
# Try to connect
|
||||
self._available = self.aftv.connect(always_log_errors=False)
|
||||
self._available = self.aftv.adb_connect(always_log_errors=False)
|
||||
|
||||
# To be safe, wait until the next update to run ADB commands if
|
||||
# using the Python ADB implementation.
|
||||
@ -508,7 +522,7 @@ class FireTVDevice(ADBDevice):
|
||||
# Check if device is disconnected.
|
||||
if not self._available:
|
||||
# Try to connect
|
||||
self._available = self.aftv.connect(always_log_errors=False)
|
||||
self._available = self.aftv.adb_connect(always_log_errors=False)
|
||||
|
||||
# To be safe, wait until the next update to run ADB commands if
|
||||
# using the Python ADB implementation.
|
||||
|
@ -112,7 +112,7 @@ adafruit-blinka==1.2.1
|
||||
adafruit-circuitpython-mcp230xx==1.1.2
|
||||
|
||||
# homeassistant.components.androidtv
|
||||
adb-shell==0.0.4
|
||||
adb-shell==0.0.7
|
||||
|
||||
# homeassistant.components.adguard
|
||||
adguardhome==0.2.1
|
||||
@ -203,7 +203,7 @@ ambiclimate==0.2.1
|
||||
amcrest==1.5.3
|
||||
|
||||
# homeassistant.components.androidtv
|
||||
androidtv==0.0.30
|
||||
androidtv==0.0.32
|
||||
|
||||
# homeassistant.components.anel_pwrctrl
|
||||
anel_pwrctrl-homeassistant==0.0.1.dev2
|
||||
|
@ -49,7 +49,7 @@ YesssSMS==0.4.1
|
||||
abodepy==0.16.5
|
||||
|
||||
# homeassistant.components.androidtv
|
||||
adb-shell==0.0.4
|
||||
adb-shell==0.0.7
|
||||
|
||||
# homeassistant.components.adguard
|
||||
adguardhome==0.2.1
|
||||
@ -98,7 +98,7 @@ airly==0.0.2
|
||||
ambiclimate==0.2.1
|
||||
|
||||
# homeassistant.components.androidtv
|
||||
androidtv==0.0.30
|
||||
androidtv==0.0.32
|
||||
|
||||
# homeassistant.components.apns
|
||||
apns2==0.3.0
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Define patches used for androidtv tests."""
|
||||
|
||||
from socket import error as socket_error
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import mock_open, patch
|
||||
|
||||
|
||||
class AdbDeviceFake:
|
||||
@ -128,3 +128,15 @@ def patch_shell(response=None, error=False):
|
||||
|
||||
|
||||
PATCH_ADB_DEVICE = patch("androidtv.adb_manager.AdbDevice", AdbDeviceFake)
|
||||
PATCH_ANDROIDTV_OPEN = patch("androidtv.adb_manager.open", mock_open())
|
||||
PATCH_KEYGEN = patch("homeassistant.components.androidtv.media_player.keygen")
|
||||
PATCH_SIGNER = patch("androidtv.adb_manager.PythonRSASigner")
|
||||
|
||||
|
||||
def isfile(filepath):
|
||||
"""Mock `os.path.isfile`."""
|
||||
return filepath.endswith("adbkey")
|
||||
|
||||
|
||||
PATCH_ISFILE = patch("os.path.isfile", isfile)
|
||||
PATCH_ACCESS = patch("os.access", return_value=True)
|
||||
|
@ -5,6 +5,7 @@ from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.androidtv.media_player import (
|
||||
ANDROIDTV_DOMAIN,
|
||||
CONF_ADB_SERVER_IP,
|
||||
CONF_ADBKEY,
|
||||
)
|
||||
from homeassistant.components.media_player.const import DOMAIN
|
||||
from homeassistant.const import (
|
||||
@ -61,14 +62,8 @@ CONFIG_FIRETV_ADB_SERVER = {
|
||||
}
|
||||
|
||||
|
||||
async def _test_reconnect(hass, caplog, config):
|
||||
"""Test that the error and reconnection attempts are logged correctly.
|
||||
|
||||
"Handles device/service unavailable. Log a warning once when
|
||||
unavailable, log once when reconnected."
|
||||
|
||||
https://developers.home-assistant.io/docs/en/integration_quality_scale_index.html
|
||||
"""
|
||||
def _setup(hass, config):
|
||||
"""Perform common setup tasks for the tests."""
|
||||
if CONF_ADB_SERVER_IP not in config[DOMAIN]:
|
||||
patch_key = "python"
|
||||
else:
|
||||
@ -79,10 +74,26 @@ async def _test_reconnect(hass, caplog, config):
|
||||
else:
|
||||
entity_id = "media_player.fire_tv"
|
||||
|
||||
return patch_key, entity_id
|
||||
|
||||
|
||||
async def _test_reconnect(hass, caplog, config):
|
||||
"""Test that the error and reconnection attempts are logged correctly.
|
||||
|
||||
"Handles device/service unavailable. Log a warning once when
|
||||
unavailable, log once when reconnected."
|
||||
|
||||
https://developers.home-assistant.io/docs/en/integration_quality_scale_index.html
|
||||
"""
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
], patchers.patch_shell("")[patch_key]:
|
||||
], patchers.patch_shell("")[
|
||||
patch_key
|
||||
], patchers.PATCH_KEYGEN, patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER:
|
||||
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
|
||||
@ -93,7 +104,7 @@ async def _test_reconnect(hass, caplog, config):
|
||||
|
||||
with patchers.patch_connect(False)[patch_key], patchers.patch_shell(error=True)[
|
||||
patch_key
|
||||
]:
|
||||
], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER:
|
||||
for _ in range(5):
|
||||
await hass.helpers.entity_component.async_update_entity(entity_id)
|
||||
state = hass.states.get(entity_id)
|
||||
@ -105,7 +116,9 @@ async def _test_reconnect(hass, caplog, config):
|
||||
assert caplog.record_tuples[1][1] == logging.WARNING
|
||||
|
||||
caplog.set_level(logging.DEBUG)
|
||||
with patchers.patch_connect(True)[patch_key], patchers.patch_shell("1")[patch_key]:
|
||||
with patchers.patch_connect(True)[patch_key], patchers.patch_shell("1")[
|
||||
patch_key
|
||||
], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER:
|
||||
# Update 1 will reconnect
|
||||
await hass.helpers.entity_component.async_update_entity(entity_id)
|
||||
|
||||
@ -143,19 +156,13 @@ async def _test_adb_shell_returns_none(hass, config):
|
||||
|
||||
The state should be `None` and the device should be unavailable.
|
||||
"""
|
||||
if CONF_ADB_SERVER_IP not in config[DOMAIN]:
|
||||
patch_key = "python"
|
||||
else:
|
||||
patch_key = "server"
|
||||
|
||||
if config[DOMAIN].get(CONF_DEVICE_CLASS) != "firetv":
|
||||
entity_id = "media_player.android_tv"
|
||||
else:
|
||||
entity_id = "media_player.fire_tv"
|
||||
patch_key, entity_id = _setup(hass, config)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
], patchers.patch_shell("")[patch_key]:
|
||||
], patchers.patch_shell("")[
|
||||
patch_key
|
||||
], patchers.PATCH_KEYGEN, patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER:
|
||||
assert await async_setup_component(hass, DOMAIN, config)
|
||||
await hass.helpers.entity_component.async_update_entity(entity_id)
|
||||
state = hass.states.get(entity_id)
|
||||
@ -164,7 +171,7 @@ async def _test_adb_shell_returns_none(hass, config):
|
||||
|
||||
with patchers.patch_shell(None)[patch_key], patchers.patch_shell(error=True)[
|
||||
patch_key
|
||||
]:
|
||||
], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER:
|
||||
await hass.helpers.entity_component.async_update_entity(entity_id)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
@ -251,3 +258,21 @@ async def test_adb_shell_returns_none_firetv_adb_server(hass):
|
||||
|
||||
"""
|
||||
assert await _test_adb_shell_returns_none(hass, CONFIG_FIRETV_ADB_SERVER)
|
||||
|
||||
|
||||
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)
|
||||
|
||||
with patchers.PATCH_ADB_DEVICE, patchers.patch_connect(True)[
|
||||
patch_key
|
||||
], patchers.patch_shell("")[
|
||||
patch_key
|
||||
], patchers.PATCH_ANDROIDTV_OPEN, patchers.PATCH_SIGNER, patchers.PATCH_ISFILE, patchers.PATCH_ACCESS:
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user