From 16dd90ac78b6196dee5c7bd952b3760c3bfdd682 Mon Sep 17 00:00:00 2001 From: Kenny Millington Date: Fri, 10 Nov 2017 17:35:57 +0000 Subject: [PATCH] Add support for Alexa intent slot synonyms. (#10469) --- homeassistant/components/alexa/intent.py | 20 +++- tests/components/alexa/test_intent.py | 128 ++++++++++++++++++++++- 2 files changed, 146 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/alexa/intent.py b/homeassistant/components/alexa/intent.py index a0d0062414db85..56887a8a701c4e 100644 --- a/homeassistant/components/alexa/intent.py +++ b/homeassistant/components/alexa/intent.py @@ -138,10 +138,28 @@ def __init__(self, hass, intent_info): # Intent is None if request was a LaunchRequest or SessionEndedRequest if intent_info is not None: for key, value in intent_info.get('slots', {}).items(): + underscored_key = key.replace('.', '_') + if 'value' in value: - underscored_key = key.replace('.', '_') self.variables[underscored_key] = value['value'] + if 'resolutions' in value: + self._populate_resolved_values(underscored_key, value) + + def _populate_resolved_values(self, underscored_key, value): + for resolution in value['resolutions']['resolutionsPerAuthority']: + if 'values' not in resolution: + continue + + for resolved in resolution['values']: + if 'value' not in resolved: + continue + + if 'id' in resolved['value']: + self.variables[underscored_key] = resolved['value']['id'] + elif 'name' in resolved['value']: + self.variables[underscored_key] = resolved['value']['name'] + def add_card(self, card_type, title, content): """Add a card to the response.""" assert self.card is None diff --git a/tests/components/alexa/test_intent.py b/tests/components/alexa/test_intent.py index 565ebec64aa21f..19ecf8526229da 100644 --- a/tests/components/alexa/test_intent.py +++ b/tests/components/alexa/test_intent.py @@ -13,6 +13,7 @@ SESSION_ID = "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000" APPLICATION_ID = "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe" REQUEST_ID = "amzn1.echo-api.request.0000000-0000-0000-0000-00000000000" +AUTHORITY_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.ZODIAC" # pylint: disable=invalid-name calls = [] @@ -90,7 +91,7 @@ def mock_service(call): "type": "plain", "text": "LaunchRequest has been received.", } - } + }, } })) return loop.run_until_complete(test_client(hass.http.app)) @@ -207,6 +208,131 @@ def test_intent_request_with_slots(alexa_client): assert text == "You told us your sign is virgo." +@asyncio.coroutine +def test_intent_request_with_slots_and_id_resolution(alexa_client): + """Test a request with slots and an id synonym.""" + data = { + "version": "1.0", + "session": { + "new": False, + "sessionId": SESSION_ID, + "application": { + "applicationId": APPLICATION_ID + }, + "attributes": { + "supportedHoroscopePeriods": { + "daily": True, + "weekly": False, + "monthly": False + } + }, + "user": { + "userId": "amzn1.account.AM3B00000000000000000000000" + } + }, + "request": { + "type": "IntentRequest", + "requestId": REQUEST_ID, + "timestamp": "2015-05-13T12:34:56Z", + "intent": { + "name": "GetZodiacHoroscopeIntent", + "slots": { + "ZodiacSign": { + "name": "ZodiacSign", + "value": "virgo", + "resolutions": { + "resolutionsPerAuthority": [ + { + "authority": AUTHORITY_ID, + "status": { + "code": "ER_SUCCESS_MATCH" + }, + "values": [ + { + "value": { + "name": "Virgo", + "id": "VIRGO" + } + } + ] + } + ] + } + } + } + } + } + } + req = yield from _intent_req(alexa_client, data) + assert req.status == 200 + data = yield from req.json() + text = data.get("response", {}).get("outputSpeech", + {}).get("text") + assert text == "You told us your sign is VIRGO." + + +@asyncio.coroutine +def test_intent_request_with_slots_and_name_resolution(alexa_client): + """Test a request with slots and a name synonym.""" + data = { + "version": "1.0", + "session": { + "new": False, + "sessionId": SESSION_ID, + "application": { + "applicationId": APPLICATION_ID + }, + "attributes": { + "supportedHoroscopePeriods": { + "daily": True, + "weekly": False, + "monthly": False + } + }, + "user": { + "userId": "amzn1.account.AM3B00000000000000000000000" + } + }, + "request": { + "type": "IntentRequest", + "requestId": REQUEST_ID, + "timestamp": "2015-05-13T12:34:56Z", + "intent": { + "name": "GetZodiacHoroscopeIntent", + "slots": { + "ZodiacSign": { + "name": "ZodiacSign", + "value": "virgo", + "resolutions": { + "resolutionsPerAuthority": [ + { + "authority": AUTHORITY_ID, + "status": { + "code": "ER_SUCCESS_MATCH" + }, + "values": [ + { + "value": { + "name": "Virgo" + } + } + ] + } + ] + } + } + } + } + } + } + req = yield from _intent_req(alexa_client, data) + assert req.status == 200 + data = yield from req.json() + text = data.get("response", {}).get("outputSpeech", + {}).get("text") + assert text == "You told us your sign is Virgo." + + @asyncio.coroutine def test_intent_request_with_slots_but_no_value(alexa_client): """Test a request with slots but no value."""