mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 12:59:34 +00:00
Add Intent component (#8434)
* Add intent component * Add intent script component * Add shopping list component * Convert Snips to use intent component * Convert Alexa to use intent component * Lint * Fix Alexa tests * Update snips test * Add intent support to conversation * Add API to view shopping list contents * Lint * Fix demo test * Lint * lint * Remove type from slot schema * Add dependency to conversation * Move intent to be a helper * Fix conversation * Clean up intent helper * Fix Alexa * Snips to use new hass.components * Allow registering intents with conversation at any point in time * Shopping list to register sentences * Add HTTP endpoint to Conversation * Add async action option to intent_script * Update API.ai to use intents * Cleanup Alexa * Shopping list component to register built-in panel * Rename shopping list intent to inlude Hass name
This commit is contained in:
165
homeassistant/helpers/intent.py
Normal file
165
homeassistant/helpers/intent.py
Normal file
@@ -0,0 +1,165 @@
|
||||
"""Module to coordinate user intentions."""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
|
||||
DATA_KEY = 'intent'
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SLOT_SCHEMA = vol.Schema({
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
SPEECH_TYPE_PLAIN = 'plain'
|
||||
SPEECH_TYPE_SSML = 'ssml'
|
||||
|
||||
|
||||
@callback
|
||||
def async_register(hass, handler):
|
||||
"""Register an intent with Home Assistant."""
|
||||
intents = hass.data.get(DATA_KEY)
|
||||
if intents is None:
|
||||
intents = hass.data[DATA_KEY] = {}
|
||||
|
||||
if handler.intent_type in intents:
|
||||
_LOGGER.warning('Intent %s is being overwritten by %s.',
|
||||
handler.intent_type, handler)
|
||||
|
||||
intents[handler.intent_type] = handler
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_handle(hass, platform, intent_type, slots=None, text_input=None):
|
||||
"""Handle an intent."""
|
||||
handler = hass.data.get(DATA_KEY, {}).get(intent_type)
|
||||
|
||||
if handler is None:
|
||||
raise UnknownIntent()
|
||||
|
||||
intent = Intent(hass, platform, intent_type, slots or {}, text_input)
|
||||
|
||||
try:
|
||||
_LOGGER.info("Triggering intent handler %s", handler)
|
||||
result = yield from handler.async_handle(intent)
|
||||
return result
|
||||
except vol.Invalid as err:
|
||||
raise InvalidSlotInfo from err
|
||||
except Exception as err:
|
||||
raise IntentHandleError from err
|
||||
|
||||
|
||||
class IntentError(HomeAssistantError):
|
||||
"""Base class for intent related errors."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnknownIntent(IntentError):
|
||||
"""When the intent is not registered."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidSlotInfo(IntentError):
|
||||
"""When the slot data is invalid."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class IntentHandleError(IntentError):
|
||||
"""Error while handling intent."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class IntentHandler:
|
||||
"""Intent handler registration."""
|
||||
|
||||
intent_type = None
|
||||
slot_schema = None
|
||||
_slot_schema = None
|
||||
platforms = None
|
||||
|
||||
@callback
|
||||
def async_can_handle(self, intent_obj):
|
||||
"""Test if an intent can be handled."""
|
||||
return self.platforms is None or intent_obj.platform in self.platforms
|
||||
|
||||
@callback
|
||||
def async_validate_slots(self, slots):
|
||||
"""Validate slot information."""
|
||||
if self.slot_schema is None:
|
||||
return slots
|
||||
|
||||
if self._slot_schema is None:
|
||||
self._slot_schema = vol.Schema({
|
||||
key: SLOT_SCHEMA.extend({'value': validator})
|
||||
for key, validator in self.slot_schema.items()})
|
||||
|
||||
return self._slot_schema(slots)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_handle(self, intent_obj):
|
||||
"""Handle the intent."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def __repr__(self):
|
||||
"""String representation of intent handler."""
|
||||
return '<{} - {}>'.format(self.__class__.__name__, self.intent_type)
|
||||
|
||||
|
||||
class Intent:
|
||||
"""Hold the intent."""
|
||||
|
||||
__slots__ = ['hass', 'platform', 'intent_type', 'slots', 'text_input']
|
||||
|
||||
def __init__(self, hass, platform, intent_type, slots, text_input):
|
||||
"""Initialize an intent."""
|
||||
self.hass = hass
|
||||
self.platform = platform
|
||||
self.intent_type = intent_type
|
||||
self.slots = slots
|
||||
self.text_input = text_input
|
||||
|
||||
@callback
|
||||
def create_response(self):
|
||||
"""Create a response."""
|
||||
return IntentResponse(self)
|
||||
|
||||
|
||||
class IntentResponse:
|
||||
"""Response to an intent."""
|
||||
|
||||
def __init__(self, intent):
|
||||
"""Initialize an IntentResponse."""
|
||||
self.intent = intent
|
||||
self.speech = {}
|
||||
self.card = {}
|
||||
|
||||
@callback
|
||||
def async_set_speech(self, speech, speech_type='plain', extra_data=None):
|
||||
"""Set speech response."""
|
||||
self.speech[speech_type] = {
|
||||
'speech': speech,
|
||||
'extra_data': extra_data,
|
||||
}
|
||||
|
||||
@callback
|
||||
def async_set_card(self, title, content, card_type='simple'):
|
||||
"""Set speech response."""
|
||||
self.card[card_type] = {
|
||||
'title': title,
|
||||
'content': content,
|
||||
}
|
||||
|
||||
@callback
|
||||
def as_dict(self):
|
||||
"""Return a dictionary representation of an intent response."""
|
||||
return {
|
||||
'speech': self.speech,
|
||||
'card': self.card,
|
||||
}
|
||||
Reference in New Issue
Block a user