alexa: Add media_player InputController support (#11946)

`media_player`s can support select_source so map that to an
InputController.

[1]: https://developer.amazon.com/docs/device-apis/alexa-inputcontroller.html
This commit is contained in:
Phil Kates 2018-01-28 22:22:04 -08:00 committed by Paulus Schoutsen
parent 7d6ef4445e
commit 766875f702
2 changed files with 89 additions and 0 deletions

View File

@ -357,6 +357,11 @@ class _AlexaPlaybackController(_AlexaInterface):
return 'Alexa.PlaybackController'
class _AlexaInputController(_AlexaInterface):
def name(self):
return 'Alexa.InputController'
class _AlexaTemperatureSensor(_AlexaInterface):
def name(self):
return 'Alexa.TemperatureSensor'
@ -475,6 +480,9 @@ class _MediaPlayerCapabilities(_AlexaEntity):
if supported & playback_features:
yield _AlexaPlaybackController(self.entity)
if supported & media_player.SUPPORT_SELECT_SOURCE:
yield _AlexaInputController(self.entity)
@ENTITY_ADAPTERS.register(scene.DOMAIN)
class _SceneCapabilities(_AlexaEntity):
@ -1081,6 +1089,41 @@ def async_api_set_volume(hass, config, request, entity):
return api_message(request)
@HANDLERS.register(('Alexa.InputController', 'SelectInput'))
@extract_entity
@asyncio.coroutine
def async_api_select_input(hass, config, request, entity):
"""Process a set input request."""
media_input = request[API_PAYLOAD]['input']
# attempt to map the ALL UPPERCASE payload name to a source
source_list = entity.attributes[media_player.ATTR_INPUT_SOURCE_LIST] or []
for source in source_list:
# response will always be space separated, so format the source in the
# most likely way to find a match
formatted_source = source.lower().replace('-', ' ').replace('_', ' ')
if formatted_source in media_input.lower():
media_input = source
break
else:
msg = 'failed to map input {} to a media source on {}'.format(
media_input, entity.entity_id)
_LOGGER.error(msg)
return api_error(
request, error_type='INVALID_VALUE', error_message=msg)
data = {
ATTR_ENTITY_ID: entity.entity_id,
media_player.ATTR_INPUT_SOURCE: media_input,
}
yield from hass.services.async_call(
entity.domain, media_player.SERVICE_SELECT_SOURCE,
data, blocking=False)
return api_message(request)
@HANDLERS.register(('Alexa.Speaker', 'AdjustVolume'))
@extract_entity
@asyncio.coroutine

View File

@ -1161,6 +1161,52 @@ def test_api_set_volume(hass):
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize(
"domain,payload,source_list,idx", [
('media_player', 'GAME CONSOLE', ['tv', 'game console'], 1),
('media_player', 'SATELLITE TV', ['satellite-tv', 'game console'], 0),
('media_player', 'SATELLITE TV', ['satellite_tv', 'game console'], 0),
('media_player', 'BAD DEVICE', ['satellite_tv', 'game console'], None),
]
)
def test_api_select_input(hass, domain, payload, source_list, idx):
"""Test api set input process."""
request = get_new_request(
'Alexa.InputController', 'SelectInput', 'media_player#test')
# add payload
request['directive']['payload']['input'] = payload
# setup test devices
hass.states.async_set(
'media_player.test', 'off', {
'friendly_name': "Test media player",
'source': 'unknown',
'source_list': source_list,
})
call_media_player = async_mock_service(hass, domain, 'select_source')
msg = yield from smart_home.async_handle_message(
hass, DEFAULT_CONFIG, request)
yield from hass.async_block_till_done()
assert 'event' in msg
msg = msg['event']
# test where no source matches
if idx is None:
assert len(call_media_player) == 0
assert msg['header']['name'] == 'ErrorResponse'
return
assert len(call_media_player) == 1
assert call_media_player[0].data['entity_id'] == 'media_player.test'
assert call_media_player[0].data['source'] == source_list[idx]
assert msg['header']['name'] == 'Response'
@asyncio.coroutine
@pytest.mark.parametrize(
"result,adjust", [(0.7, '-5'), (0.8, '5'), (0, '-80')])