Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP view to conversation to handle intents via JSON POST #28818

Merged
merged 1 commit into from
Nov 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion homeassistant/components/conversation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ async def get_agent(hass: core.HomeAssistant) -> AbstractConversationAgent:

async def async_setup(hass, config):
"""Register the process service."""

hass.data[DATA_CONFIG] = config

async def handle_service(service):
Expand All @@ -77,6 +76,7 @@ async def handle_service(service):
DOMAIN, SERVICE_PROCESS, handle_service, schema=SERVICE_PROCESS_SCHEMA
)
hass.http.register_view(ConversationProcessView())
hass.http.register_view(ConversationHandleView())
hass.components.websocket_api.async_register_command(websocket_process)
hass.components.websocket_api.async_register_command(websocket_get_agent_info)
hass.components.websocket_api.async_register_command(websocket_set_onboarding)
Expand Down Expand Up @@ -162,3 +162,40 @@ async def post(self, request, data):
)

return self.json(intent_result)


class ConversationHandleView(http.HomeAssistantView):
"""View to handle intents from JSON."""

url = "/api/conversation/handle"
name = "api:conversation:handle"

@RequestDataValidator(
vol.Schema(
{
vol.Required("name"): cv.string,
vol.Optional("data"): vol.Schema({cv.string: object}),
}
)
)
async def post(self, request, data):
"""Handle intent with name/data."""
hass = request.app["hass"]

try:
intent_name = data["name"]
slots = {
key: {"value": value} for key, value in data.get("data", {}).items()
}
intent_result = await intent.async_handle(
hass, DOMAIN, intent_name, slots, ""
)
except intent.IntentHandleError as err:
intent_result = intent.IntentResponse()
intent_result.async_set_speech(str(err))

if intent_result is None:
intent_result = intent.IntentResponse()
intent_result.async_set_speech("Sorry, I couldn't handle that")

return self.json(intent_result)
45 changes: 45 additions & 0 deletions tests/components/conversation/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,51 @@ async def async_handle(self, intent):
}


async def test_http_handle_intent(hass, hass_client):
"""Test handle intent via HTTP API."""

class TestIntentHandler(intent.IntentHandler):
"""Test Intent Handler."""

intent_type = "OrderBeer"

async def async_handle(self, intent):
"""Handle the intent."""
response = intent.create_response()
response.async_set_speech(
"I've ordered a {}!".format(intent.slots["type"]["value"])
)
response.async_set_card(
"Beer ordered", "You chose a {}.".format(intent.slots["type"]["value"])
)
return response

intent.async_register(hass, TestIntentHandler())

result = await async_setup_component(
hass,
"conversation",
{"conversation": {"intents": {"OrderBeer": ["I would like the {type} beer"]}}},
)
assert result

client = await hass_client()
resp = await client.post(
"/api/conversation/handle",
json={"name": "OrderBeer", "data": {"type": "Belgian"}},
)

assert resp.status == 200
data = await resp.json()

assert data == {
"card": {
"simple": {"content": "You chose a Belgian.", "title": "Beer ordered"}
},
"speech": {"plain": {"extra_data": None, "speech": "I've ordered a Belgian!"}},
}


@pytest.mark.parametrize("sentence", ("turn on kitchen", "turn kitchen on"))
async def test_turn_on_intent(hass, sentence):
"""Test calling the turn on intent."""
Expand Down