From a51372f7b3c1f3ba86a73391f7360b757f1f2306 Mon Sep 17 00:00:00 2001 From: Nick Whyte Date: Tue, 19 May 2020 23:11:04 +1000 Subject: [PATCH] Fire events on homekit TV remote key press (#29588) * Fire events on homekit TV remote key press * Changes from code review * black * isort * flake8 * Update tests/components/homekit/test_type_media_players.py Co-authored-by: J. Nick Koston Co-authored-by: J. Nick Koston --- homeassistant/components/homekit/const.py | 17 +++++ .../components/homekit/type_media_players.py | 72 ++++++++++++------- .../homekit/test_type_media_players.py | 19 +++++ 3 files changed, 84 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/homekit/const.py b/homeassistant/components/homekit/const.py index 3291fab7a30..8c431830589 100644 --- a/homeassistant/components/homekit/const.py +++ b/homeassistant/components/homekit/const.py @@ -27,6 +27,7 @@ ATTR_INTERGRATION = "platform" ATTR_MANUFACTURER = "manufacturer" ATTR_MODEL = "model" ATTR_SOFTWARE_VERSION = "sw_version" +ATTR_KEY_NAME = "key_name" # #### Config #### CONF_ADVERTISE_IP = "advertise_ip" @@ -79,6 +80,7 @@ FEATURE_TOGGLE_MUTE = "toggle_mute" # #### HomeKit Component Event #### EVENT_HOMEKIT_CHANGED = "homekit_state_change" +EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED = "homekit_tv_remote_key_pressed" # #### HomeKit Component Services #### SERVICE_HOMEKIT_START = "start" @@ -228,6 +230,21 @@ THRESHOLD_CO2 = 1000 DEFAULT_MIN_TEMP_WATER_HEATER = 40 # °C DEFAULT_MAX_TEMP_WATER_HEATER = 60 # °C +# #### Media Player Key Names #### +KEY_ARROW_DOWN = "arrow_down" +KEY_ARROW_LEFT = "arrow_left" +KEY_ARROW_RIGHT = "arrow_right" +KEY_ARROW_UP = "arrow_up" +KEY_BACK = "back" +KEY_EXIT = "exit" +KEY_FAST_FORWARD = "fast_forward" +KEY_INFORMATION = "information" +KEY_NEXT_TRACK = "next_track" +KEY_PREVIOUS_TRACK = "previous_track" +KEY_REWIND = "rewind" +KEY_SELECT = "select" +KEY_PLAY_PAUSE = "play_pause" + # #### Door states #### HK_DOOR_OPEN = 0 HK_DOOR_CLOSED = 1 diff --git a/homeassistant/components/homekit/type_media_players.py b/homeassistant/components/homekit/type_media_players.py index 4a104972b02..886c15a5fb9 100644 --- a/homeassistant/components/homekit/type_media_players.py +++ b/homeassistant/components/homekit/type_media_players.py @@ -40,6 +40,7 @@ from homeassistant.core import callback from .accessories import TYPES, HomeAccessory from .const import ( + ATTR_KEY_NAME, CHAR_ACTIVE, CHAR_ACTIVE_IDENTIFIER, CHAR_CONFIGURED_NAME, @@ -56,10 +57,24 @@ from .const import ( CHAR_VOLUME_CONTROL_TYPE, CHAR_VOLUME_SELECTOR, CONF_FEATURE_LIST, + EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE, + KEY_ARROW_DOWN, + KEY_ARROW_LEFT, + KEY_ARROW_RIGHT, + KEY_ARROW_UP, + KEY_BACK, + KEY_EXIT, + KEY_FAST_FORWARD, + KEY_INFORMATION, + KEY_NEXT_TRACK, + KEY_PLAY_PAUSE, + KEY_PREVIOUS_TRACK, + KEY_REWIND, + KEY_SELECT, SERV_INPUT_SOURCE, SERV_SWITCH, SERV_TELEVISION, @@ -70,19 +85,19 @@ from .util import get_media_player_features _LOGGER = logging.getLogger(__name__) MEDIA_PLAYER_KEYS = { - # 0: "Rewind", - # 1: "FastForward", - # 2: "NextTrack", - # 3: "PreviousTrack", - # 4: "ArrowUp", - # 5: "ArrowDown", - # 6: "ArrowLeft", - # 7: "ArrowRight", - # 8: "Select", - # 9: "Back", - # 10: "Exit", - 11: SERVICE_MEDIA_PLAY_PAUSE, - # 15: "Information", + 0: KEY_REWIND, + 1: KEY_FAST_FORWARD, + 2: KEY_NEXT_TRACK, + 3: KEY_PREVIOUS_TRACK, + 4: KEY_ARROW_UP, + 5: KEY_ARROW_DOWN, + 6: KEY_ARROW_LEFT, + 7: KEY_ARROW_RIGHT, + 8: KEY_SELECT, + 9: KEY_BACK, + 10: KEY_EXIT, + 11: KEY_PLAY_PAUSE, + 15: KEY_INFORMATION, } # Names may not contain special characters @@ -363,19 +378,28 @@ class TelevisionMediaPlayer(HomeAccessory): def set_remote_key(self, value): """Send remote key value if call came from HomeKit.""" _LOGGER.debug("%s: Set remote key to %s", self.entity_id, value) - service = MEDIA_PLAYER_KEYS.get(value) - if service: - # Handle Play Pause - if service == SERVICE_MEDIA_PLAY_PAUSE: - state = self.hass.states.get(self.entity_id).state - if state in (STATE_PLAYING, STATE_PAUSED): - service = ( - SERVICE_MEDIA_PLAY - if state == STATE_PAUSED - else SERVICE_MEDIA_PAUSE - ) + key_name = MEDIA_PLAYER_KEYS.get(value) + if key_name is None: + _LOGGER.warning("%s: Unhandled key press for %s", self.entity_id, value) + return + + if key_name == KEY_PLAY_PAUSE: + # Handle Play Pause by directly updating the media player entity. + state = self.hass.states.get(self.entity_id).state + if state in (STATE_PLAYING, STATE_PAUSED): + service = ( + SERVICE_MEDIA_PLAY if state == STATE_PAUSED else SERVICE_MEDIA_PAUSE + ) + else: + service = SERVICE_MEDIA_PLAY_PAUSE params = {ATTR_ENTITY_ID: self.entity_id} self.call_service(DOMAIN, service, params) + else: + # Other keys can be handled by listening to the event bus + self.hass.bus.fire( + EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED, + {ATTR_KEY_NAME: key_name, ATTR_ENTITY_ID: self.entity_id}, + ) @callback def async_update_state(self, new_state): diff --git a/tests/components/homekit/test_type_media_players.py b/tests/components/homekit/test_type_media_players.py index bd533417121..e4842b93125 100644 --- a/tests/components/homekit/test_type_media_players.py +++ b/tests/components/homekit/test_type_media_players.py @@ -1,12 +1,15 @@ """Test different accessory types: Media Players.""" from homeassistant.components.homekit.const import ( + ATTR_KEY_NAME, ATTR_VALUE, CONF_FEATURE_LIST, + EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED, FEATURE_ON_OFF, FEATURE_PLAY_PAUSE, FEATURE_PLAY_STOP, FEATURE_TOGGLE_MUTE, + KEY_ARROW_RIGHT, ) from homeassistant.components.homekit.type_media_players import ( MediaPlayer, @@ -342,6 +345,22 @@ async def test_media_player_television(hass, hk_driver, events, caplog): assert len(events) == 11 assert events[-1].data[ATTR_VALUE] is None + events = [] + + def listener(event): + events.append(event) + + hass.bus.async_listen(EVENT_HOMEKIT_TV_REMOTE_KEY_PRESSED, listener) + + await hass.async_add_executor_job(acc.char_remote_key.client_update_value, 20) + await hass.async_block_till_done() + + await hass.async_add_executor_job(acc.char_remote_key.client_update_value, 7) + await hass.async_block_till_done() + + assert len(events) == 1 + assert events[0].data[ATTR_KEY_NAME] == KEY_ARROW_RIGHT + async def test_media_player_television_basic(hass, hk_driver, events, caplog): """Test if basic television accessory and HA are updated accordingly."""