def connect_device_usb() -> NoReturn: global device, device_is_connected, device_name, device_android_version, device_user if not os.path.exists(cfg.adb_key_file_path): keygen(cfg.adb_key_file_path) log_info(f"[ADB] generated and stored a new adb-RSA-key (was missing)", logger="debuglog") with open(cfg.adb_key_file_path) as f: priv = f.read() with open(cfg.adb_key_file_path + '.pub') as f: pub = f.read() signer = PythonRSASigner(pub, priv) try: device = AdbDeviceUsb( ) # TODO: there can be more than one phone, determine with "available", "list" or similar except UsbDeviceNotFoundError: device = None log_error( f"[ADB] is the device connected and ADB activated on device?", logger="debuglog") except DevicePathInvalidError: device = None log_error( "[ADB] installation seems incomplete, adb-shell[usb] is missing (or not working as intended) or adb-server is still running on your system", logger="debuglog") if device is not None: device.connect(rsa_keys=[signer], auth_timeout_s=30) if not is_device_available(): return device_is_connected = True log_info(f"[ADB] connected to USB-Device", logger="debuglog") update_device_properties()
def setup_androidtv(hass, config): """Generate an ADB key (if needed) and connect to the Android TV / Fire TV.""" adbkey = config.get(CONF_ADBKEY, hass.config.path(STORAGE_DIR, "androidtv_adbkey")) if CONF_ADB_SERVER_IP not in config: # Use "adb_shell" (Python ADB implementation) if not os.path.isfile(adbkey): # Generate ADB key files keygen(adbkey) adb_log = f"using Python ADB implementation with adbkey='{adbkey}'" 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( config[CONF_HOST], config[CONF_PORT], adbkey, config.get(CONF_ADB_SERVER_IP, ""), config[CONF_ADB_SERVER_PORT], config[CONF_STATE_DETECTION_RULES], config[CONF_DEVICE_CLASS], 10.0, ) return aftv, adb_log
def test_connect_with_key(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST_WITH_AUTH) self.assertTrue(self.device.connect([signer]))
def test_value_error(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): with patch('adb_shell.auth.sign_pythonrsa.decoder.decode', return_value=([None, [None]], None)): with self.assertRaises(ValueError): keygen('tests/adbkey') self.signer = PythonRSASigner.FromRSAKeyPath( 'tests/adbkey')
def test_index_error(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): with patch('adb_shell.auth.sign_pythonrsa.decoder.decode', side_effect=IndexError): with self.assertRaises(ValueError): keygen('tests/adbkey') self.signer = PythonRSASigner.FromRSAKeyPath( 'tests/adbkey')
def test_connect_with_key_invalid_response(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_INVALID) with self.assertRaises(exceptions.InvalidResponseError): self.device.connect([signer]) self.assertFalse(self.device.available)
async def test_connect_with_new_key(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') signer.pub_key = u'' self.device._transport._bulk_read = b''.join( patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY) self.assertTrue(await self.device.connect([signer]))
def test_connect_with_new_key_and_callback(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') signer.pub_key = u'' self._callback_invoked = False def auth_callback(device): self._callback_invoked = True self.device._handle._bulk_read = b''.join(patchers.BULK_READ_LIST_WITH_AUTH_NEW_KEY) self.assertTrue(self.device.connect([signer], auth_callback=auth_callback)) self.assertTrue(self._callback_invoked)
def adb_connect(): key_dir = os.path.join(expanduser("~"), '.pyadb') if not os.path.exists(key_dir): os.makedirs(key_dir) key_path = os.path.join(key_dir, 'adbkey') keygen.keygen(key_path) adbkey = str(key_path) with open(adbkey) as f: priv = f.read() with open(adbkey + '.pub') as f: pub = f.read() signer = PythonRSASigner(pub, priv) device = AdbDeviceUsb() device.connect(rsa_keys=[signer], auth_timeout_s=60) return device
def _setup_androidtv(hass, config): """Generate an ADB key (if needed) and load it.""" adbkey = config.get(CONF_ADBKEY, hass.config.path(STORAGE_DIR, "androidtv_adbkey")) if CONF_ADB_SERVER_IP not in config: # Use "adb_shell" (Python ADB implementation) if not os.path.isfile(adbkey): # Generate ADB key files keygen(adbkey) # Load the ADB key signer = ADBPythonSync.load_adbkey(adbkey) adb_log = f"using Python ADB implementation with adbkey='{adbkey}'" else: # Use "pure-python-adb" (communicate with ADB server) signer = None adb_log = f"using ADB server at {config[CONF_ADB_SERVER_IP]}:{config[CONF_ADB_SERVER_PORT]}" return adbkey, signer, adb_log
def setUp(self): with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') self.signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey')
def setUp(self): with patch('adb_shell.auth.sign_cryptography.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') self.signer = CryptographySigner('tests/adbkey')
def connect(self): if not Path(ADBClient.key_path).exists(): keygen(self.key_path) signer = PythonRSASigner.FromRSAKeyPath(self.key_path) self.device.connect(rsa_keys=[signer])
def _keygen(self): try: keygen(self._adbkey) except Exception as exc: self._logger.error(exc)
def test_issue29(self): # https://github.com/JeffLIrion/adb_shell/issues/29 with patch('adb_shell.auth.sign_pythonrsa.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') signer = PythonRSASigner.FromRSAKeyPath('tests/adbkey') msg1 = AdbMessage(command=constants.OKAY, arg0=1, arg1=1, data=b'\x00') msg2 = AdbMessage(command=constants.CLSE, arg0=1, arg1=1, data=b'') self.device._handle._bulk_read = b''.join([ b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\xc5\n\x00\x00\xbe\xaa\xab\xb7', # Line 22 b"\x17\xbf\xbf\xff\xc7\xa2eo'Sh\xdf\x8e\xf5\xff\xe0\tJ6H", # Line 23 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 26 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 27 b'OKAY\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 290 (modified --> Line 30) b'CLSE\xa2\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 291 b'CLSE\xa2\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 292 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x001\x00\x00\x00\xa8\xad\xab\xba', # Line 31 b'1', # Line 32 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x001\x00\x00\x00\xa8\xad\xab\xba', # Line 35 b'1', # Line 36 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x000\x00\x00\x00\xa8\xad\xab\xba', # Line 39 b'0', # Line 40 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x13\x00\x00\x000\x06\x00\x00\xa8\xad\xab\xba', # Line 43 b'Wake Locks: size=0\n', # Line 44 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x1e\x00\x00\x00V\x0b\x00\x00\xa8\xad\xab\xba', # Line 47 b'com.google.android.youtube.tv\n', # Line 48 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00\x98\x00\x00\x00\xa13\x00\x00\xa8\xad\xab\xba', # Line 51 b' state=PlaybackState {state=0, position=0, buffered position=0, speed=0.0, updated=0, actions=0, custom actions=[], active item id=-1, error=null}\n', # Line 52 b'WRTE\x99\x00\x00\x00\x01\x00\x00\x00.\x01\x00\x00\xceP\x00\x00\xa8\xad\xab\xba', # Line 55 b'- STREAM_MUSIC:\n Muted: false\n Min: 0\n Max: 15\n Current: 2 (speaker): 11, 4 (headset): 10, 8 (headphone): 10, 400 (hdmi): 6, 40000000 (default): 11\n Devices: hdmi\n- STREAM_ALARM:\n Muted: false\n Min: 0\n Max: 7\n Current: 40000000 (default): 6\n Devices: speaker\n- STREAM_NOTIFICATION:\n', # Line 56 b'CLSE\x99\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 59 b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x94\t\x00\x00\xbe\xaa\xab\xb7', # Line 297 b'P\xa5\x86\x97\xe8\x01\xb09\x8c>F\x9d\xc6\xbd\xc0J\x80!\xbb\x1a', # Line 298 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 301 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 302 b'OKAY\xa5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 305 b'CLSE\xa5\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 306 msg1.pack(), msg1.data, msg2.pack(), b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00e\x0c\x00\x00\xbe\xaa\xab\xb7', # Line 315 b'\xd3\xef\x7f_\xa6\xc0`b\x19\\z\xe4\xf3\xe2\xed\x8d\xe1W\xfbH', # Line 316 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 319 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 320 b'OKAY\xa7\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 323 b'CLSE\xa7\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 324 msg1.pack(), msg1.data, msg2.pack(), b'AUTH\x01\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x93\x08\x00\x00\xbe\xaa\xab\xb7', # Line 333 b's\xd4_e\xa4s\x02\x95\x0f\x1e\xec\n\x95Y9[`\x8e\xe1f', # Line 334 b"CNXN\x00\x00\x00\x01\x00\x10\x00\x00i\x00\x00\x00.'\x00\x00\xbc\xb1\xa7\xb1", # Line 337 b'device::ro.product.name=once;ro.product.model=MIBOX3;ro.product.device=once;features=stat_v2,cmd,shell_v2', # Line 338 b'OKAY\xa9\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xb4\xbe\xa6', # Line 341 b'CLSE\xa9\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\xb3\xac\xba', # Line 342 msg1.pack(), msg1.data, msg2.pack() ]) self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command') self.assertTrue(self.device.connect([signer])) self.device.shell('Android TV update command') self.device.shell('Android TV update command')
def setUp(self): with patch('adb_shell.auth.sign_pycryptodome.open', open_priv_pub), patch('adb_shell.auth.keygen.open', open_priv_pub): keygen('tests/adbkey') self.signer = PycryptodomeAuthSigner('tests/adbkey')
async def async_step_user(self, user_input=None): """Handle a flow initiated by the user.""" error = None if user_input is not None: host = user_input[CONF_HOST] adb_key = user_input.get(CONF_ADBKEY) adb_server = user_input.get(CONF_ADB_SERVER_IP) if adb_key and adb_server: return self._show_setup_form(user_input, "key_and_server") if adb_key: isfile = await self.hass.async_add_executor_job(_is_file, adb_key) if not isfile: return self._show_setup_form(user_input, "adbkey_not_file") ip_address = await self.hass.async_add_executor_job(_get_ip, host) if not ip_address: return self._show_setup_form(user_input, "invalid_host") self._async_abort_entries_match({CONF_HOST: host}) if ip_address != host: self._async_abort_entries_match({CONF_HOST: ip_address}) device_calss = user_input.get(CONF_DEVICE_CLASS) if device_calss == "ais": user_input[CONF_DEVICE_CLASS] = DEVICE_ANDROIDTV if host == "127.0.0.1" and ais_global.has_root(): await self.async_execute_command( "su -c 'setprop persist.service.adb.enable 1'" ) await self.async_execute_command( "su -c 'setprop service.adb.tcp.port 5555'" ) await self.async_execute_command("su -c 'stop adbd'") await self.async_execute_command( "su -c 'touch /data/misc/adb/adb_keys'" ) adbkey = self.hass.config.path(STORAGE_DIR, "androidtv_adbkey") if not os.path.isfile(adbkey): # Generate ADB key files keygen(adbkey) await self.async_execute_command( "su -c 'rm /data/misc/adb/adb_keys'" ) await self.async_execute_command( "su -c 'cat /data/data/pl.sviete.dom/files/home/AIS/.storage/androidtv_adbkey.pub >> " "/data/misc/adb/adb_keys'" ) # TODO add without rm # await self.async_execute_command( # "su -c 'grep -F -f /data/data/pl.sviete.dom/files/home/AIS" # "/.storage/androidtv_adbkey.pub /data/misc/adb/adb_keys || cat " # "/data/data/pl.sviete.dom/files/home/AIS/.storage" # "/androidtv_adbkey.pub >> /data/misc/adb/adb_keys'" # ) await self.async_execute_command( "su -c 'chmod 0644 /data/misc/adb/adb_keys'" ) await self.async_execute_command("su -c 'start adbd'") error, unique_id = await self._async_check_connection(user_input) if error is None: if not unique_id: return self.async_abort(reason="invalid_unique_id") await self.async_set_unique_id(unique_id) self._abort_if_unique_id_configured() return self.async_create_entry( title=user_input.get(CONF_NAME) or host, data=user_input ) user_input = user_input or {} return self._show_setup_form(user_input, error)
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]}" if CONF_ADB_SERVER_IP not in config: # Use "adb_shell" (Python ADB implementation) 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: # 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], adb_server_port=config[CONF_ADB_SERVER_PORT], device_class=config[CONF_DEVICE_CLASS], state_detection_rules=config[CONF_STATE_DETECTION_RULES], ) if not aftv.available: # Determine the name that will be used for the device in the log if CONF_NAME in config: device_name = config[CONF_NAME] elif config[CONF_DEVICE_CLASS] == DEVICE_ANDROIDTV: device_name = "Android TV device" elif config[CONF_DEVICE_CLASS] == DEVICE_FIRETV: device_name = "Fire TV device" else: device_name = "Android TV / Fire TV device" _LOGGER.warning("Could not connect to %s at %s %s", device_name, host, 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.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" add_entities([device]) _LOGGER.debug("Setup %s at %s %s", device_name, host, adb_log) hass.data[ANDROIDTV_DOMAIN][host] = 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) target_devices = [ dev for dev in hass.data[ANDROIDTV_DOMAIN].values() if dev.entity_id in entity_id ] for target_device in target_devices: output = target_device.adb_command(cmd) # log the output, if there is any if output: _LOGGER.info( "Output of command '%s' from '%s': %s", cmd, target_device.entity_id, output, ) hass.services.register( ANDROIDTV_DOMAIN, SERVICE_ADB_COMMAND, service_adb_command, schema=SERVICE_ADB_COMMAND_SCHEMA, )
def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Android TV / Fire TV platform.""" hass.data.setdefault(ANDROIDTV_DOMAIN, {}) 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) 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( config[CONF_HOST], config[CONF_PORT], 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( config[CONF_HOST], config[CONF_PORT], config[CONF_ADBKEY], device_class=config[CONF_DEVICE_CLASS], state_detection_rules=config[CONF_STATE_DETECTION_RULES], auth_timeout_s=10.0, ) 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( config[CONF_HOST], config[CONF_PORT], adb_server_ip=config[CONF_ADB_SERVER_IP], adb_server_port=config[CONF_ADB_SERVER_PORT], device_class=config[CONF_DEVICE_CLASS], state_detection_rules=config[CONF_STATE_DETECTION_RULES], ) if not aftv.available: # Determine the name that will be used for the device in the log if CONF_NAME in config: device_name = config[CONF_NAME] elif config[CONF_DEVICE_CLASS] == DEVICE_ANDROIDTV: device_name = "Android TV device" elif config[CONF_DEVICE_CLASS] == DEVICE_FIRETV: device_name = "Fire TV device" else: device_name = "Android TV / Fire TV device" _LOGGER.warning("Could not connect to %s at %s %s", device_name, address, adb_log) raise PlatformNotReady 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), config[CONF_EXCLUDE_UNNAMED_APPS], config[CONF_SCREENCAP], ] 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[ATTR_COMMAND] 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: output = target_device.adb_command(cmd) # log the output, if there is any if output: _LOGGER.info( "Output of command '%s' from '%s': %s", cmd, target_device.entity_id, output, ) hass.services.register( ANDROIDTV_DOMAIN, SERVICE_ADB_COMMAND, service_adb_command, 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)