Add intent integration to expose intent handle API (#29124)

* Add intent integration to expose intent handle API.

* Run hassfest + fix scaffolding

* Update __init__.py
pull/29170/head
Paulus Schoutsen 2019-11-27 02:25:43 -08:00 committed by Pascal Vizeli
parent ec61a86678
commit 004476a1f8
9 changed files with 116 additions and 85 deletions

View File

@ -156,6 +156,7 @@ homeassistant/components/input_number/* @home-assistant/core
homeassistant/components/input_select/* @home-assistant/core
homeassistant/components/input_text/* @home-assistant/core
homeassistant/components/integration/* @dgomes
homeassistant/components/intent/* @home-assistant/core
homeassistant/components/ios/* @robbiet480
homeassistant/components/ipma/* @dgomes
homeassistant/components/iqvia/* @bachya

View File

@ -68,7 +68,6 @@ async def async_setup(hass, config):
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)
@ -139,43 +138,6 @@ class ConversationProcessView(http.HomeAssistantView):
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, "", self.context(request)
)
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)
async def _get_agent(hass: core.HomeAssistant) -> AbstractConversationAgent:
"""Get the active conversation agent."""
agent = hass.data.get(DATA_AGENT)

View File

@ -0,0 +1,54 @@
"""The Intent integration."""
import voluptuous as vol
from homeassistant.core import HomeAssistant
from homeassistant.components import http
from homeassistant.components.http.data_validator import RequestDataValidator
from homeassistant.helpers import config_validation as cv, intent
from .const import DOMAIN
CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass: HomeAssistant, config: dict):
"""Set up the Intent component."""
hass.http.register_view(IntentHandleView())
return True
class IntentHandleView(http.HomeAssistantView):
"""View to handle intents from JSON."""
url = "/api/intent/handle"
name = "api:intent: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, "", self.context(request)
)
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)

View File

@ -0,0 +1,3 @@
"""Constants for the Intent integration."""
DOMAIN = "intent"

View File

@ -0,0 +1,11 @@
{
"domain": "intent",
"name": "Intent",
"config_flow": false,
"documentation": "https://www.home-assistant.io/integrations/intent",
"requirements": [],
"ssdp": [],
"homekit": {},
"dependencies": ["http"],
"codeowners": ["@home-assistant/core"]
}

View File

@ -4,7 +4,8 @@
"config_flow": false,
"documentation": "https://www.home-assistant.io/integrations/NEW_DOMAIN",
"requirements": [],
"ssdp": {},
"ssdp": [],
"zeroconf": [],
"homekit": {},
"dependencies": [],
"codeowners": []

View File

@ -129,52 +129,6 @@ async def test_http_processing_intent(hass, hass_client, hass_admin_user):
}
async def test_http_handle_intent(hass, hass_client, hass_admin_user):
"""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."""
assert intent.context.user_id == hass_admin_user.id
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."""

View File

@ -0,0 +1 @@
"""Tests for the Intent integration."""

View File

@ -0,0 +1,44 @@
"""Tests for Intent component."""
from homeassistant.setup import async_setup_component
from homeassistant.helpers import intent
async def test_http_handle_intent(hass, hass_client, hass_admin_user):
"""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."""
assert intent.context.user_id == hass_admin_user.id
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, "intent", {})
assert result
client = await hass_client()
resp = await client.post(
"/api/intent/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!"}},
}