def setUp(self): """Set up a `FireTVDevice` media player.""" with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[ self.PATCH_KEY], patchers.patch_shell("")[self.PATCH_KEY]: aftv = setup("HOST", 5555, device_class="firetv") self.aftv = FireTVDevice(aftv, "Fake Fire TV", {}, True, None, None)
def test_update_lock_not_acquired(self): """Test that the state does not get updated when a `LockNotAcquiredException` is raised.""" patch_key = "server" with patchers.patch_connect(True)[patch_key], patchers.patch_shell( "")[patch_key]: aftv = setup("HOST", 5555, adb_server_ip="ADB_SERVER_IP", device_class="androidtv") self.aftv = AndroidTVDevice(aftv, "Fake Android TV", {}, True, None, None) with patchers.patch_shell("")[patch_key]: self.aftv.update() assert self.aftv.state == STATE_OFF with patch("androidtv.androidtv.androidtv_sync.AndroidTVSync.update", side_effect=LockNotAcquiredException): with patchers.patch_shell("1")[patch_key]: self.aftv.update() assert self.aftv.state == STATE_OFF with patchers.patch_shell("1")[patch_key]: self.aftv.update() assert self.aftv.state == STATE_STANDBY
def setUp(self): """Set up a `FireTVDevice` media player.""" with patchers.patch_connect(True)[self.PATCH_KEY], patchers.patch_shell("")[self.PATCH_KEY]: aftv = setup( "HOST", 5555, adb_server_ip="ADB_SERVER_IP", device_class="firetv" ) self.aftv = FireTVDevice(aftv, "Fake Fire TV", {}, True, None, None)
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_setup(self): """Test that the ``setup`` function works correctly.""" with self.assertRaises(ValueError): setup("HOST", 5555, device_class="INVALID") with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT1 )[self.PATCH_KEY]: ftv = setup("HOST", 5555) self.assertIsInstance(ftv, FireTVSync) self.assertDictEqual(ftv.device_properties, DEVICE_PROPERTIES_DICT1) with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT2 )[self.PATCH_KEY]: atv = setup("HOST", 5555) self.assertIsInstance(atv, AndroidTVSync) self.assertDictEqual(atv.device_properties, DEVICE_PROPERTIES_DICT2) with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT1 )[self.PATCH_KEY]: ftv = setup("HOST", 5555, device_class="androidtv") self.assertIsInstance(ftv, AndroidTVSync) self.assertDictEqual(ftv.device_properties, DEVICE_PROPERTIES_DICT1) with patchers.PATCH_ADB_DEVICE_TCP, patchers.patch_connect(True)[self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT2 )[self.PATCH_KEY]: atv = setup("HOST", 5555, device_class="firetv") self.assertIsInstance(atv, FireTVSync) self.assertDictEqual(atv.device_properties, DEVICE_PROPERTIES_DICT2)
def test_adb_command_key(self): """Test sending a key command via the `androidtv.adb_command` service.""" patch_key = "server" command = "HOME" response = None with patchers.patch_connect(True)[patch_key], patchers.patch_shell("")[patch_key]: aftv = setup("HOST", 5555, adb_server_ip="ADB_SERVER_IP", device_class="androidtv") self.aftv = AndroidTVDevice(aftv, "Fake Android TV", {}, True, None, None) with patch("androidtv.basetv.basetv_sync.BaseTVSync.adb_shell", return_value=response) as patch_shell: self.aftv.adb_command(command) patch_shell.assert_called_with("input keyevent {}".format(self.aftv._keys[command])) assert self.aftv._adb_response is None
def test_adb_command_get_properties(self): """Test sending the "GET_PROPERTIES" command via the `androidtv.adb_command` service.""" patch_key = "server" command = "GET_PROPERTIES" response = {"key": "value"} with patchers.patch_connect(True)[patch_key], patchers.patch_shell("")[patch_key]: aftv = setup( "HOST", 5555, adb_server_ip="ADB_SERVER_IP", device_class="androidtv" ) self.aftv = AndroidTVDevice(aftv, "Fake Android TV", {}, True, None, None) with patch("androidtv.androidtv.androidtv_sync.AndroidTVSync.get_properties_dict", return_value=response) as patch_get_props: self.aftv.adb_command(command) assert patch_get_props.called assert self.aftv._adb_response == str(response)
def test_adb_command(self): """Test sending a command via the `androidtv.adb_command` service.""" patch_key = "server" command = "test command" response = "test response" with patchers.patch_connect(True)[patch_key], patchers.patch_shell("")[patch_key]: aftv = setup( "HOST", 5555, adb_server_ip="ADB_SERVER_IP", device_class="androidtv" ) self.aftv = AndroidTVDevice(aftv, "Fake Android TV", {}, True, None, None) with patch("androidtv.basetv.BaseTV.adb_shell", return_value=response) as patch_shell: self.aftv.adb_command(command) patch_shell.assert_called_with(command) assert self.aftv._adb_response == response
def test_setup(self): """Test that the ``setup`` function works correctly. """ with self.assertRaises(ValueError): setup('IP:5555', device_class='INVALID') with patchers.patch_adb_device, patchers.patch_connect(True)[ self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT1)[self.PATCH_KEY]: ftv = setup('IP:5555') self.assertIsInstance(ftv, FireTV) self.assertDictEqual(ftv.device_properties, DEVICE_PROPERTIES_DICT1) with patchers.patch_adb_device, patchers.patch_connect(True)[ self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT2)[self.PATCH_KEY]: atv = setup('IP:5555') self.assertIsInstance(atv, AndroidTV) self.assertDictEqual(atv.device_properties, DEVICE_PROPERTIES_DICT2) with patchers.patch_adb_device, patchers.patch_connect(True)[ self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT1)[self.PATCH_KEY]: ftv = setup('IP:5555', device_class='androidtv') self.assertIsInstance(ftv, AndroidTV) self.assertDictEqual(ftv.device_properties, DEVICE_PROPERTIES_DICT1) with patchers.patch_adb_device, patchers.patch_connect(True)[ self.PATCH_KEY], patchers.patch_shell( DEVICE_PROPERTIES_OUTPUT2)[self.PATCH_KEY]: atv = setup('IP:5555', device_class='firetv') self.assertIsInstance(atv, FireTV) self.assertDictEqual(atv.device_properties, DEVICE_PROPERTIES_DICT2)
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.""" from androidtv import setup hass.data.setdefault(ANDROIDTV_DOMAIN, {}) host = '{0}:{1}'.format(config[CONF_HOST], config[CONF_PORT]) if CONF_ADB_SERVER_IP not in config: # Use "python-adb" (Python ADB implementation) adb_log = "using Python ADB implementation " if CONF_ADBKEY in config: aftv = setup(host, config[CONF_ADBKEY], device_class=config[CONF_DEVICE_CLASS]) adb_log += "with adbkey='{0}'".format(config[CONF_ADBKEY]) else: aftv = setup(host, device_class=config[CONF_DEVICE_CLASS]) adb_log += "without adbkey authentication" else: # Use "pure-python-adb" (communicate with ADB server) 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]) adb_log = "using ADB server at {0}:{1}".format( 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 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.""" from androidtv import setup hass.data.setdefault(ANDROIDTV_DOMAIN, {}) host = '{0}:{1}'.format(config[CONF_HOST], config[CONF_PORT]) if CONF_ADB_SERVER_IP not in config: # Use "python-adb" (Python ADB implementation) if CONF_ADBKEY in config: aftv = setup(host, config[CONF_ADBKEY], device_class=config[CONF_DEVICE_CLASS]) adb_log = " using adbkey='{0}'".format(config[CONF_ADBKEY]) else: aftv = setup(host, device_class=config[CONF_DEVICE_CLASS]) adb_log = "" else: # Use "pure-python-adb" (communicate with ADB server) 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]) adb_log = " using ADB server at {0}:{1}".format( 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 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) return 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 and (not isinstance(output, str) or output.strip()): _LOGGER.info("Output of command '%s' from '%s': %s", cmd, target_device.entity_id, repr(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)