mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Alexa Intent: Use the 'id' field and expose nearest resolutions as variables (#86709)
* Use the 'id' field and nearest resolutions Expose nearest Resolution (ID and Value) as Variables * Add more specific type hints * Change type definition of request * Add deprecation warning and remove variables * Remove deprecation warning & update tests * Fix wrong value assignment * revert future changes
This commit is contained in:
parent
97cac66195
commit
0f2caf864a
@ -1,6 +1,7 @@
|
||||
"""Support for Alexa skill service end point."""
|
||||
import enum
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components import http
|
||||
from homeassistant.core import callback
|
||||
@ -180,12 +181,15 @@ async def async_handle_intent(hass, message):
|
||||
return alexa_response.as_dict()
|
||||
|
||||
|
||||
def resolve_slot_synonyms(key, request):
|
||||
def resolve_slot_data(key: str, request: dict[str, Any]) -> dict[str, str]:
|
||||
"""Check slot request for synonym resolutions."""
|
||||
# Default to the spoken slot value if more than one or none are found. For
|
||||
# Default to the spoken slot value if more than one or none are found. Always
|
||||
# passes the id and name of the nearest possible slot resolution. For
|
||||
# reference to the request object structure, see the Alexa docs:
|
||||
# https://tinyurl.com/ybvm7jhs
|
||||
resolved_value = request["value"]
|
||||
resolved_data = {}
|
||||
resolved_data["value"] = request["value"]
|
||||
resolved_data["id"] = ""
|
||||
|
||||
if (
|
||||
"resolutions" in request
|
||||
@ -200,20 +204,26 @@ def resolve_slot_synonyms(key, request):
|
||||
if entry["status"]["code"] != SYN_RESOLUTION_MATCH:
|
||||
continue
|
||||
|
||||
possible_values.extend([item["value"]["name"] for item in entry["values"]])
|
||||
possible_values.extend([item["value"] for item in entry["values"]])
|
||||
|
||||
# Always set id if available, otherwise an empty string is used as id
|
||||
if len(possible_values) >= 1:
|
||||
# Set ID if available
|
||||
if "id" in possible_values[0]:
|
||||
resolved_data["id"] = possible_values[0]["id"]
|
||||
|
||||
# If there is only one match use the resolved value, otherwise the
|
||||
# resolution cannot be determined, so use the spoken slot value
|
||||
# resolution cannot be determined, so use the spoken slot value and empty string as id
|
||||
if len(possible_values) == 1:
|
||||
resolved_value = possible_values[0]
|
||||
resolved_data["value"] = possible_values[0]["name"]
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"Found multiple synonym resolutions for slot value: {%s: %s}",
|
||||
key,
|
||||
resolved_value,
|
||||
resolved_data["value"],
|
||||
)
|
||||
|
||||
return resolved_value
|
||||
return resolved_data
|
||||
|
||||
|
||||
class AlexaResponse:
|
||||
@ -237,8 +247,10 @@ class AlexaResponse:
|
||||
continue
|
||||
|
||||
_key = key.replace(".", "_")
|
||||
_slot_data = resolve_slot_data(key, value)
|
||||
|
||||
self.variables[_key] = resolve_slot_synonyms(key, value)
|
||||
self.variables[_key] = _slot_data["value"]
|
||||
self.variables[_key + "_Id"] = _slot_data["id"]
|
||||
|
||||
def add_card(self, card_type, title, content):
|
||||
"""Add a card to the response."""
|
||||
|
@ -77,6 +77,12 @@ def alexa_client(event_loop, hass, hass_client):
|
||||
"text": "You told us your sign is {{ ZodiacSign }}.",
|
||||
}
|
||||
},
|
||||
"GetZodiacHoroscopeIDIntent": {
|
||||
"speech": {
|
||||
"type": "plain",
|
||||
"text": "You told us your sign is {{ ZodiacSign_Id }}.",
|
||||
}
|
||||
},
|
||||
"AMAZON.PlaybackAction<object@MusicCreativeWork>": {
|
||||
"speech": {
|
||||
"type": "plain",
|
||||
@ -299,6 +305,113 @@ async def test_intent_request_with_slots_and_synonym_resolution(alexa_client) ->
|
||||
assert text == "You told us your sign is Virgo."
|
||||
|
||||
|
||||
async def test_intent_request_with_slots_and_synonym_id_resolution(
|
||||
alexa_client,
|
||||
) -> None:
|
||||
"""Test a request with slots, id 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": "GetZodiacHoroscopeIDIntent",
|
||||
"slots": {
|
||||
"ZodiacSign": {
|
||||
"name": "ZodiacSign",
|
||||
"value": "V zodiac",
|
||||
"resolutions": {
|
||||
"resolutionsPerAuthority": [
|
||||
{
|
||||
"authority": AUTHORITY_ID,
|
||||
"status": {"code": "ER_SUCCESS_MATCH"},
|
||||
"values": [{"value": {"name": "Virgo", "id": "1"}}],
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
req = await _intent_req(alexa_client, data)
|
||||
assert req.status == HTTPStatus.OK
|
||||
data = await req.json()
|
||||
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
||||
assert text == "You told us your sign is 1."
|
||||
|
||||
|
||||
async def test_intent_request_with_slots_and_multi_synonym_id_resolution(
|
||||
alexa_client,
|
||||
) -> None:
|
||||
"""Test a request with slots and multiple name synonyms (id)."""
|
||||
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": "GetZodiacHoroscopeIDIntent",
|
||||
"slots": {
|
||||
"ZodiacSign": {
|
||||
"name": "ZodiacSign",
|
||||
"value": "Virgio Test",
|
||||
"resolutions": {
|
||||
"resolutionsPerAuthority": [
|
||||
{
|
||||
"authority": AUTHORITY_ID,
|
||||
"status": {"code": "ER_SUCCESS_MATCH"},
|
||||
"values": [
|
||||
{"value": {"name": "Virgio Test", "id": "2"}}
|
||||
],
|
||||
},
|
||||
{
|
||||
"authority": AUTHORITY_ID,
|
||||
"status": {"code": "ER_SUCCESS_MATCH"},
|
||||
"values": [{"value": {"name": "Virgo", "id": "1"}}],
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
req = await _intent_req(alexa_client, data)
|
||||
assert req.status == HTTPStatus.OK
|
||||
data = await req.json()
|
||||
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
||||
assert text == "You told us your sign is 2."
|
||||
|
||||
|
||||
async def test_intent_request_with_slots_and_multi_synonym_resolution(
|
||||
alexa_client,
|
||||
) -> None:
|
||||
|
Loading…
x
Reference in New Issue
Block a user