mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add intent integration to expose intent handle API (#29124)
* Add intent integration to expose intent handle API. * Run hassfest + fix scaffolding * Update __init__.py
This commit is contained in:
parent
ec61a86678
commit
004476a1f8
@ -156,6 +156,7 @@ homeassistant/components/input_number/* @home-assistant/core
|
|||||||
homeassistant/components/input_select/* @home-assistant/core
|
homeassistant/components/input_select/* @home-assistant/core
|
||||||
homeassistant/components/input_text/* @home-assistant/core
|
homeassistant/components/input_text/* @home-assistant/core
|
||||||
homeassistant/components/integration/* @dgomes
|
homeassistant/components/integration/* @dgomes
|
||||||
|
homeassistant/components/intent/* @home-assistant/core
|
||||||
homeassistant/components/ios/* @robbiet480
|
homeassistant/components/ios/* @robbiet480
|
||||||
homeassistant/components/ipma/* @dgomes
|
homeassistant/components/ipma/* @dgomes
|
||||||
homeassistant/components/iqvia/* @bachya
|
homeassistant/components/iqvia/* @bachya
|
||||||
|
@ -68,7 +68,6 @@ async def async_setup(hass, config):
|
|||||||
DOMAIN, SERVICE_PROCESS, handle_service, schema=SERVICE_PROCESS_SCHEMA
|
DOMAIN, SERVICE_PROCESS, handle_service, schema=SERVICE_PROCESS_SCHEMA
|
||||||
)
|
)
|
||||||
hass.http.register_view(ConversationProcessView())
|
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_process)
|
||||||
hass.components.websocket_api.async_register_command(websocket_get_agent_info)
|
hass.components.websocket_api.async_register_command(websocket_get_agent_info)
|
||||||
hass.components.websocket_api.async_register_command(websocket_set_onboarding)
|
hass.components.websocket_api.async_register_command(websocket_set_onboarding)
|
||||||
@ -139,43 +138,6 @@ class ConversationProcessView(http.HomeAssistantView):
|
|||||||
return self.json(intent_result)
|
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:
|
async def _get_agent(hass: core.HomeAssistant) -> AbstractConversationAgent:
|
||||||
"""Get the active conversation agent."""
|
"""Get the active conversation agent."""
|
||||||
agent = hass.data.get(DATA_AGENT)
|
agent = hass.data.get(DATA_AGENT)
|
||||||
|
54
homeassistant/components/intent/__init__.py
Normal file
54
homeassistant/components/intent/__init__.py
Normal 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)
|
3
homeassistant/components/intent/const.py
Normal file
3
homeassistant/components/intent/const.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"""Constants for the Intent integration."""
|
||||||
|
|
||||||
|
DOMAIN = "intent"
|
11
homeassistant/components/intent/manifest.json
Normal file
11
homeassistant/components/intent/manifest.json
Normal 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"]
|
||||||
|
}
|
@ -4,7 +4,8 @@
|
|||||||
"config_flow": false,
|
"config_flow": false,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/NEW_DOMAIN",
|
"documentation": "https://www.home-assistant.io/integrations/NEW_DOMAIN",
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"ssdp": {},
|
"ssdp": [],
|
||||||
|
"zeroconf": [],
|
||||||
"homekit": {},
|
"homekit": {},
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": []
|
"codeowners": []
|
||||||
|
@ -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"))
|
@pytest.mark.parametrize("sentence", ("turn on kitchen", "turn kitchen on"))
|
||||||
async def test_turn_on_intent(hass, sentence):
|
async def test_turn_on_intent(hass, sentence):
|
||||||
"""Test calling the turn on intent."""
|
"""Test calling the turn on intent."""
|
||||||
|
1
tests/components/intent/__init__.py
Normal file
1
tests/components/intent/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for the Intent integration."""
|
44
tests/components/intent/test_init.py
Normal file
44
tests/components/intent/test_init.py
Normal 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!"}},
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user