Add support to reprompt user (#65256)
This commit is contained in:
parent
0cfc7112fa
commit
076faaa4a4
5 changed files with 121 additions and 7 deletions
|
@ -166,7 +166,10 @@ async def async_handle_intent(hass, message):
|
||||||
alexa_response.add_speech(
|
alexa_response.add_speech(
|
||||||
alexa_speech, intent_response.speech[intent_speech]["speech"]
|
alexa_speech, intent_response.speech[intent_speech]["speech"]
|
||||||
)
|
)
|
||||||
break
|
if intent_speech in intent_response.reprompt:
|
||||||
|
alexa_response.add_reprompt(
|
||||||
|
alexa_speech, intent_response.reprompt[intent_speech]["reprompt"]
|
||||||
|
)
|
||||||
|
|
||||||
if "simple" in intent_response.card:
|
if "simple" in intent_response.card:
|
||||||
alexa_response.add_card(
|
alexa_response.add_card(
|
||||||
|
@ -267,10 +270,9 @@ class AlexaResponse:
|
||||||
|
|
||||||
key = "ssml" if speech_type == SpeechType.ssml else "text"
|
key = "ssml" if speech_type == SpeechType.ssml else "text"
|
||||||
|
|
||||||
self.reprompt = {
|
self.should_end_session = False
|
||||||
"type": speech_type.value,
|
|
||||||
key: text.async_render(self.variables, parse_result=False),
|
self.reprompt = {"type": speech_type.value, key: text}
|
||||||
}
|
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
"""Return response in an Alexa valid dict."""
|
"""Return response in an Alexa valid dict."""
|
||||||
|
|
|
@ -12,6 +12,7 @@ DOMAIN = "intent_script"
|
||||||
|
|
||||||
CONF_INTENTS = "intents"
|
CONF_INTENTS = "intents"
|
||||||
CONF_SPEECH = "speech"
|
CONF_SPEECH = "speech"
|
||||||
|
CONF_REPROMPT = "reprompt"
|
||||||
|
|
||||||
CONF_ACTION = "action"
|
CONF_ACTION = "action"
|
||||||
CONF_CARD = "card"
|
CONF_CARD = "card"
|
||||||
|
@ -39,6 +40,10 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
vol.Optional(CONF_TYPE, default="plain"): cv.string,
|
vol.Optional(CONF_TYPE, default="plain"): cv.string,
|
||||||
vol.Required(CONF_TEXT): cv.template,
|
vol.Required(CONF_TEXT): cv.template,
|
||||||
},
|
},
|
||||||
|
vol.Optional(CONF_REPROMPT): {
|
||||||
|
vol.Optional(CONF_TYPE, default="plain"): cv.string,
|
||||||
|
vol.Required(CONF_TEXT): cv.template,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -72,6 +77,7 @@ class ScriptIntentHandler(intent.IntentHandler):
|
||||||
async def async_handle(self, intent_obj):
|
async def async_handle(self, intent_obj):
|
||||||
"""Handle the intent."""
|
"""Handle the intent."""
|
||||||
speech = self.config.get(CONF_SPEECH)
|
speech = self.config.get(CONF_SPEECH)
|
||||||
|
reprompt = self.config.get(CONF_REPROMPT)
|
||||||
card = self.config.get(CONF_CARD)
|
card = self.config.get(CONF_CARD)
|
||||||
action = self.config.get(CONF_ACTION)
|
action = self.config.get(CONF_ACTION)
|
||||||
is_async_action = self.config.get(CONF_ASYNC_ACTION)
|
is_async_action = self.config.get(CONF_ASYNC_ACTION)
|
||||||
|
@ -93,6 +99,12 @@ class ScriptIntentHandler(intent.IntentHandler):
|
||||||
speech[CONF_TYPE],
|
speech[CONF_TYPE],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if reprompt is not None and reprompt[CONF_TEXT].template:
|
||||||
|
response.async_set_reprompt(
|
||||||
|
reprompt[CONF_TEXT].async_render(slots, parse_result=False),
|
||||||
|
reprompt[CONF_TYPE],
|
||||||
|
)
|
||||||
|
|
||||||
if card is not None:
|
if card is not None:
|
||||||
response.async_set_card(
|
response.async_set_card(
|
||||||
card[CONF_TITLE].async_render(slots, parse_result=False),
|
card[CONF_TITLE].async_render(slots, parse_result=False),
|
||||||
|
|
|
@ -249,6 +249,7 @@ class IntentResponse:
|
||||||
"""Initialize an IntentResponse."""
|
"""Initialize an IntentResponse."""
|
||||||
self.intent = intent
|
self.intent = intent
|
||||||
self.speech: dict[str, dict[str, Any]] = {}
|
self.speech: dict[str, dict[str, Any]] = {}
|
||||||
|
self.reprompt: dict[str, dict[str, Any]] = {}
|
||||||
self.card: dict[str, dict[str, str]] = {}
|
self.card: dict[str, dict[str, str]] = {}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
|
@ -258,14 +259,25 @@ class IntentResponse:
|
||||||
"""Set speech response."""
|
"""Set speech response."""
|
||||||
self.speech[speech_type] = {"speech": speech, "extra_data": extra_data}
|
self.speech[speech_type] = {"speech": speech, "extra_data": extra_data}
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_set_reprompt(
|
||||||
|
self, speech: str, speech_type: str = "plain", extra_data: Any | None = None
|
||||||
|
) -> None:
|
||||||
|
"""Set reprompt response."""
|
||||||
|
self.reprompt[speech_type] = {"reprompt": speech, "extra_data": extra_data}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_set_card(
|
def async_set_card(
|
||||||
self, title: str, content: str, card_type: str = "simple"
|
self, title: str, content: str, card_type: str = "simple"
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set speech response."""
|
"""Set card response."""
|
||||||
self.card[card_type] = {"title": title, "content": content}
|
self.card[card_type] = {"title": title, "content": content}
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def as_dict(self) -> dict[str, dict[str, dict[str, Any]]]:
|
def as_dict(self) -> dict[str, dict[str, dict[str, Any]]]:
|
||||||
"""Return a dictionary representation of an intent response."""
|
"""Return a dictionary representation of an intent response."""
|
||||||
return {"speech": self.speech, "card": self.card}
|
return (
|
||||||
|
{"speech": self.speech, "reprompt": self.reprompt, "card": self.card}
|
||||||
|
if self.reprompt
|
||||||
|
else {"speech": self.speech, "card": self.card}
|
||||||
|
)
|
||||||
|
|
|
@ -13,6 +13,9 @@ from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
SESSION_ID = "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000"
|
SESSION_ID = "amzn1.echo-api.session.0000000-0000-0000-0000-00000000000"
|
||||||
APPLICATION_ID = "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe"
|
APPLICATION_ID = "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe"
|
||||||
|
APPLICATION_ID_SESSION_OPEN = (
|
||||||
|
"amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebf"
|
||||||
|
)
|
||||||
REQUEST_ID = "amzn1.echo-api.request.0000000-0000-0000-0000-00000000000"
|
REQUEST_ID = "amzn1.echo-api.request.0000000-0000-0000-0000-00000000000"
|
||||||
AUTHORITY_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.ZODIAC"
|
AUTHORITY_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.ZODIAC"
|
||||||
BUILTIN_AUTH_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.TEST"
|
BUILTIN_AUTH_ID = "amzn1.er-authority.000000-d0ed-0000-ad00-000000d00ebe.TEST"
|
||||||
|
@ -102,6 +105,16 @@ def alexa_client(loop, hass, hass_client):
|
||||||
"text": "LaunchRequest has been received.",
|
"text": "LaunchRequest has been received.",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
APPLICATION_ID_SESSION_OPEN: {
|
||||||
|
"speech": {
|
||||||
|
"type": "plain",
|
||||||
|
"text": "LaunchRequest has been received.",
|
||||||
|
},
|
||||||
|
"reprompt": {
|
||||||
|
"type": "plain",
|
||||||
|
"text": "LaunchRequest has been received.",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -139,6 +152,36 @@ async def test_intent_launch_request(alexa_client):
|
||||||
data = await req.json()
|
data = await req.json()
|
||||||
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
text = data.get("response", {}).get("outputSpeech", {}).get("text")
|
||||||
assert text == "LaunchRequest has been received."
|
assert text == "LaunchRequest has been received."
|
||||||
|
assert data.get("response", {}).get("shouldEndSession")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_launch_request_with_session_open(alexa_client):
|
||||||
|
"""Test the launch of a request."""
|
||||||
|
data = {
|
||||||
|
"version": "1.0",
|
||||||
|
"session": {
|
||||||
|
"new": True,
|
||||||
|
"sessionId": SESSION_ID,
|
||||||
|
"application": {"applicationId": APPLICATION_ID_SESSION_OPEN},
|
||||||
|
"attributes": {},
|
||||||
|
"user": {"userId": "amzn1.account.AM3B00000000000000000000000"},
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"type": "LaunchRequest",
|
||||||
|
"requestId": REQUEST_ID,
|
||||||
|
"timestamp": "2015-05-13T12:34:56Z",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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 == "LaunchRequest has been received."
|
||||||
|
text = (
|
||||||
|
data.get("response", {}).get("reprompt", {}).get("outputSpeech", {}).get("text")
|
||||||
|
)
|
||||||
|
assert text == "LaunchRequest has been received."
|
||||||
|
assert not data.get("response", {}).get("shouldEndSession")
|
||||||
|
|
||||||
|
|
||||||
async def test_intent_launch_request_not_configured(alexa_client):
|
async def test_intent_launch_request_not_configured(alexa_client):
|
||||||
|
|
|
@ -40,3 +40,48 @@ async def test_intent_script(hass):
|
||||||
|
|
||||||
assert response.card["simple"]["title"] == "Hello Paulus"
|
assert response.card["simple"]["title"] == "Hello Paulus"
|
||||||
assert response.card["simple"]["content"] == "Content for Paulus"
|
assert response.card["simple"]["content"] == "Content for Paulus"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_intent_script_wait_response(hass):
|
||||||
|
"""Test intent scripts work."""
|
||||||
|
calls = async_mock_service(hass, "test", "service")
|
||||||
|
|
||||||
|
await async_setup_component(
|
||||||
|
hass,
|
||||||
|
"intent_script",
|
||||||
|
{
|
||||||
|
"intent_script": {
|
||||||
|
"HelloWorldWaitResponse": {
|
||||||
|
"action": {
|
||||||
|
"service": "test.service",
|
||||||
|
"data_template": {"hello": "{{ name }}"},
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"title": "Hello {{ name }}",
|
||||||
|
"content": "Content for {{ name }}",
|
||||||
|
},
|
||||||
|
"speech": {"text": "Good morning {{ name }}"},
|
||||||
|
"reprompt": {
|
||||||
|
"text": "I didn't hear you, {{ name }}... I said good morning!"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await intent.async_handle(
|
||||||
|
hass, "test", "HelloWorldWaitResponse", {"name": {"value": "Paulus"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].data["hello"] == "Paulus"
|
||||||
|
|
||||||
|
assert response.speech["plain"]["speech"] == "Good morning Paulus"
|
||||||
|
|
||||||
|
assert (
|
||||||
|
response.reprompt["plain"]["reprompt"]
|
||||||
|
== "I didn't hear you, Paulus... I said good morning!"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.card["simple"]["title"] == "Hello Paulus"
|
||||||
|
assert response.card["simple"]["content"] == "Content for Paulus"
|
||||||
|
|
Loading…
Add table
Reference in a new issue