From 27a780dcc922df54da42002fd77fc7b9d16331a0 Mon Sep 17 00:00:00 2001
From: Jeff Irion <JeffLIrion@users.noreply.github.com>
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 f8829939682..03dab64e32c 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 00000000000..68f55631332
--- /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 fb7df736e51..880e1c918a9 100644
--- a/homeassistant/components/media_player/firetv.py
+++ b/homeassistant/components/firetv/media_player.py
@@ -11,14 +11,15 @@ import voluptuous as vol
 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_PORT = 5555
 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 @@ class FireTVDevice(MediaPlayerDevice):
                 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 00000000000..78019547641
--- /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 1b6971455e6..b1e3c570702 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
-- 
GitLab