135 lines
4.0 KiB
Python
135 lines
4.0 KiB
Python
"""
|
|
Support for Dialogflow webhook.
|
|
|
|
For more details about this component, please refer to the documentation at
|
|
https://home-assistant.io/components/dialogflow/
|
|
"""
|
|
import asyncio
|
|
import logging
|
|
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.const import HTTP_BAD_REQUEST
|
|
from homeassistant.helpers import intent, template
|
|
from homeassistant.components.http import HomeAssistantView
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
CONF_INTENTS = 'intents'
|
|
CONF_SPEECH = 'speech'
|
|
CONF_ACTION = 'action'
|
|
CONF_ASYNC_ACTION = 'async_action'
|
|
|
|
DEFAULT_CONF_ASYNC_ACTION = False
|
|
DEPENDENCIES = ['http']
|
|
DOMAIN = 'dialogflow'
|
|
|
|
INTENTS_API_ENDPOINT = '/api/dialogflow'
|
|
|
|
SOURCE = "Home Assistant Dialogflow"
|
|
|
|
CONFIG_SCHEMA = vol.Schema({
|
|
DOMAIN: {}
|
|
}, extra=vol.ALLOW_EXTRA)
|
|
|
|
|
|
@asyncio.coroutine
|
|
def async_setup(hass, config):
|
|
"""Set up Dialogflow component."""
|
|
hass.http.register_view(DialogflowIntentsView)
|
|
|
|
return True
|
|
|
|
|
|
class DialogflowIntentsView(HomeAssistantView):
|
|
"""Handle Dialogflow requests."""
|
|
|
|
url = INTENTS_API_ENDPOINT
|
|
name = 'api:dialogflow'
|
|
|
|
@asyncio.coroutine
|
|
def post(self, request):
|
|
"""Handle Dialogflow."""
|
|
hass = request.app['hass']
|
|
data = yield from request.json()
|
|
|
|
_LOGGER.debug("Received Dialogflow request: %s", data)
|
|
|
|
req = data.get('result')
|
|
|
|
if req is None:
|
|
_LOGGER.error("Received invalid data from Dialogflow: %s", data)
|
|
return self.json_message(
|
|
"Expected result value not received", HTTP_BAD_REQUEST)
|
|
|
|
action_incomplete = req['actionIncomplete']
|
|
|
|
if action_incomplete:
|
|
return None
|
|
|
|
action = req.get('action')
|
|
parameters = req.get('parameters')
|
|
dialogflow_response = DialogflowResponse(parameters)
|
|
|
|
if action == "":
|
|
_LOGGER.warning("Received intent with empty action")
|
|
dialogflow_response.add_speech(
|
|
"You have not defined an action in your Dialogflow intent.")
|
|
return self.json(dialogflow_response)
|
|
|
|
try:
|
|
intent_response = yield from intent.async_handle(
|
|
hass, DOMAIN, action,
|
|
{key: {'value': value} for key, value
|
|
in parameters.items()})
|
|
|
|
except intent.UnknownIntent as err:
|
|
_LOGGER.warning("Received unknown intent %s", action)
|
|
dialogflow_response.add_speech(
|
|
"This intent is not yet configured within Home Assistant.")
|
|
return self.json(dialogflow_response)
|
|
|
|
except intent.InvalidSlotInfo as err:
|
|
_LOGGER.error("Received invalid slot data: %s", err)
|
|
return self.json_message('Invalid slot data received',
|
|
HTTP_BAD_REQUEST)
|
|
except intent.IntentError:
|
|
_LOGGER.exception("Error handling request for %s", action)
|
|
return self.json_message('Error handling intent', HTTP_BAD_REQUEST)
|
|
|
|
if 'plain' in intent_response.speech:
|
|
dialogflow_response.add_speech(
|
|
intent_response.speech['plain']['speech'])
|
|
|
|
return self.json(dialogflow_response)
|
|
|
|
|
|
class DialogflowResponse(object):
|
|
"""Help generating the response for Dialogflow."""
|
|
|
|
def __init__(self, parameters):
|
|
"""Initialize the Dialogflow response."""
|
|
self.speech = None
|
|
self.parameters = {}
|
|
# Parameter names replace '.' and '-' for '_'
|
|
for key, value in parameters.items():
|
|
underscored_key = key.replace('.', '_').replace('-', '_')
|
|
self.parameters[underscored_key] = value
|
|
|
|
def add_speech(self, text):
|
|
"""Add speech to the response."""
|
|
assert self.speech is None
|
|
|
|
if isinstance(text, template.Template):
|
|
text = text.async_render(self.parameters)
|
|
|
|
self.speech = text
|
|
|
|
def as_dict(self):
|
|
"""Return response in a Dialogflow valid dictionary."""
|
|
return {
|
|
'speech': self.speech,
|
|
'displayText': self.speech,
|
|
'source': SOURCE,
|
|
}
|