From 27a780dcc922df54da42002fd77fc7b9d16331a0 Mon Sep 17 00:00:00 2001 From: Jeff Irion Date: Thu, 28 Feb 2019 03:29:56 -0800 Subject: [PATCH] Register 'firetv.adb_command' service (#21419) * Register 'media_player.firetv_adb_cmd' service * Wrap the 'firetv_adb_cmd' service with 'adb_decorator' * Address reviewer comments * Move firetv to its own platform * Move 'adb_command' service description * Rename DOMAIN to FIRETV_DOMAIN * Import KEYS in __init__ method * Change 'self.KEYS' to 'self.keys' * Update firetv in .coveragerc * 'homeassistant.components.media_player.firetv' -> 'homeassistant.components.firetv' * 'homeassistant.components.firetv' -> 'homeassistant.components.firetv.media_player' --- .coveragerc | 4 +- homeassistant/components/firetv/__init__.py | 6 ++ .../firetv.py => firetv/media_player.py} | 63 ++++++++++++++++--- homeassistant/components/firetv/services.yaml | 11 ++++ requirements_all.txt | 2 +- 5 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 homeassistant/components/firetv/__init__.py rename homeassistant/components/{media_player/firetv.py => firetv/media_player.py} (80%) create mode 100644 homeassistant/components/firetv/services.yaml diff --git a/.coveragerc b/.coveragerc index f88299396825f..03dab64e32c16 100644 --- a/.coveragerc +++ b/.coveragerc @@ -168,6 +168,7 @@ omit = homeassistant/components/fan/wemo.py homeassistant/components/fastdotcom/* homeassistant/components/fibaro/* + homeassistant/components/firetv/* homeassistant/components/folder_watcher/* homeassistant/components/foursquare/* homeassistant/components/freebox/* @@ -280,7 +281,6 @@ omit = homeassistant/components/media_player/dunehd.py homeassistant/components/media_player/emby.py homeassistant/components/media_player/epson.py - homeassistant/components/media_player/firetv.py homeassistant/components/media_player/frontier_silicon.py homeassistant/components/media_player/gpmdp.py homeassistant/components/media_player/gstreamer.py @@ -696,4 +696,4 @@ exclude_lines = # Don't complain if tests don't hit defensive assertion code: raise AssertionError - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/homeassistant/components/firetv/__init__.py b/homeassistant/components/firetv/__init__.py new file mode 100644 index 0000000000000..68f556313328d --- /dev/null +++ b/homeassistant/components/firetv/__init__.py @@ -0,0 +1,6 @@ +""" +Support for functionality to interact with FireTV devices. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/media_player.firetv/ +""" diff --git a/homeassistant/components/media_player/firetv.py b/homeassistant/components/firetv/media_player.py similarity index 80% rename from homeassistant/components/media_player/firetv.py rename to homeassistant/components/firetv/media_player.py index fb7df736e5188..880e1c918a996 100644 --- a/homeassistant/components/media_player/firetv.py +++ b/homeassistant/components/firetv/media_player.py @@ -11,14 +11,15 @@ from homeassistant.components.media_player import ( MediaPlayerDevice, PLATFORM_SCHEMA) from homeassistant.components.media_player.const import ( - SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, - SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, SUPPORT_SELECT_SOURCE, SUPPORT_STOP, - SUPPORT_TURN_OFF, SUPPORT_TURN_ON) + SUPPORT_NEXT_TRACK, SUPPORT_PAUSE, SUPPORT_PLAY, SUPPORT_PREVIOUS_TRACK, + SUPPORT_SELECT_SOURCE, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON) from homeassistant.const import ( - CONF_HOST, CONF_NAME, CONF_PORT, STATE_IDLE, STATE_OFF, STATE_PAUSED, - STATE_PLAYING, STATE_STANDBY) + ATTR_COMMAND, ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_PORT, STATE_IDLE, + STATE_OFF, STATE_PAUSED, STATE_PLAYING, STATE_STANDBY) import homeassistant.helpers.config_validation as cv +FIRETV_DOMAIN = 'firetv' + REQUIREMENTS = ['firetv==1.0.9'] _LOGGER = logging.getLogger(__name__) @@ -37,6 +38,13 @@ DEFAULT_ADB_SERVER_PORT = 5037 DEFAULT_GET_SOURCES = True +SERVICE_ADB_COMMAND = 'adb_command' + +SERVICE_ADB_COMMAND_SCHEMA = vol.Schema({ + vol.Required(ATTR_ENTITY_ID): cv.entity_ids, + vol.Required(ATTR_COMMAND): cv.string, +}) + def has_adb_files(value): """Check that ADB key files exist.""" @@ -69,6 +77,8 @@ def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the FireTV platform.""" from firetv import FireTV + hass.data.setdefault(FIRETV_DOMAIN, {}) + host = '{0}:{1}'.format(config[CONF_HOST], config[CONF_PORT]) if CONF_ADB_SERVER_IP not in config: @@ -93,9 +103,35 @@ def setup_platform(hass, config, add_entities, discovery_info=None): name = config[CONF_NAME] get_sources = config[CONF_GET_SOURCES] - device = FireTVDevice(ftv, name, get_sources) - add_entities([device]) - _LOGGER.debug("Setup Fire TV at %s%s", host, adb_log) + if host in hass.data[FIRETV_DOMAIN]: + _LOGGER.warning("Platform already setup on %s, skipping", host) + else: + device = FireTVDevice(ftv, name, get_sources) + add_entities([device]) + _LOGGER.debug("Setup Fire TV at %s%s", host, adb_log) + hass.data[FIRETV_DOMAIN][host] = device + + if hass.services.has_service(FIRETV_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[FIRETV_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, repr(output)) + + hass.services.register(FIRETV_DOMAIN, SERVICE_ADB_COMMAND, + service_adb_command, + schema=SERVICE_ADB_COMMAND_SCHEMA) def adb_decorator(override_available=False): @@ -127,6 +163,9 @@ class FireTVDevice(MediaPlayerDevice): def __init__(self, ftv, name, get_sources): """Initialize the FireTV device.""" + from firetv import KEYS + self.keys = KEYS + self.firetv = ftv self._name = name @@ -276,3 +315,11 @@ def select_source(self, source): self.firetv.launch_app(source) else: self.firetv.stop_app(source[1:].lstrip()) + + @adb_decorator() + def adb_command(self, cmd): + """Send an ADB command to a Fire TV device.""" + key = self.keys.get(cmd) + if key: + return self.firetv.adb_shell('input keyevent {}'.format(key)) + return self.firetv.adb_shell(cmd) diff --git a/homeassistant/components/firetv/services.yaml b/homeassistant/components/firetv/services.yaml new file mode 100644 index 0000000000000..7801954764172 --- /dev/null +++ b/homeassistant/components/firetv/services.yaml @@ -0,0 +1,11 @@ +# Describes the format for available Fire TV services + +adb_command: + description: Send an ADB command to a Fire TV device. + fields: + entity_id: + description: Name(s) of Fire TV entities. + example: 'media_player.fire_tv_living_room' + command: + description: Either a key command or an ADB shell command. + example: 'HOME' diff --git a/requirements_all.txt b/requirements_all.txt index 1b6971455e6c8..b1e3c5707029e 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -419,7 +419,7 @@ fiblary3==0.1.7 # homeassistant.components.sensor.fints fints==1.0.1 -# homeassistant.components.media_player.firetv +# homeassistant.components.firetv.media_player firetv==1.0.9 # homeassistant.components.sensor.fitbit