core/homeassistant/components/alexa.py

191 lines
5.1 KiB
Python
Raw Normal View History

2015-12-13 06:29:02 +00:00
"""
2016-02-23 20:06:50 +00:00
Support for Alexa skill service end point.
2015-12-13 06:29:02 +00:00
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/alexa/
"""
import enum
import logging
from homeassistant.const import HTTP_OK, HTTP_UNPROCESSABLE_ENTITY
from homeassistant.helpers.service import call_from_config
2016-02-23 20:06:50 +00:00
from homeassistant.helpers import template
2015-12-13 06:29:02 +00:00
DOMAIN = 'alexa'
DEPENDENCIES = ['http']
_LOGGER = logging.getLogger(__name__)
_CONFIG = {}
API_ENDPOINT = '/api/alexa'
CONF_INTENTS = 'intents'
CONF_CARD = 'card'
CONF_SPEECH = 'speech'
CONF_ACTION = 'action'
2015-12-13 06:29:02 +00:00
def setup(hass, config):
2016-02-23 20:06:50 +00:00
"""Activate Alexa component."""
2015-12-13 06:29:02 +00:00
_CONFIG.update(config[DOMAIN].get(CONF_INTENTS, {}))
hass.http.register_path('POST', API_ENDPOINT, _handle_alexa, True)
return True
def _handle_alexa(handler, path_match, data):
2016-02-23 20:06:50 +00:00
"""Handle Alexa."""
2015-12-13 06:29:02 +00:00
_LOGGER.debug('Received Alexa request: %s', data)
req = data.get('request')
if req is None:
_LOGGER.error('Received invalid data from Alexa: %s', data)
handler.write_json_message(
"Invalid value received for port", HTTP_UNPROCESSABLE_ENTITY)
return
req_type = req['type']
if req_type == 'SessionEndedRequest':
handler.send_response(HTTP_OK)
handler.end_headers()
return
intent = req.get('intent')
response = AlexaResponse(handler.server.hass, intent)
if req_type == 'LaunchRequest':
response.add_speech(
SpeechType.plaintext,
"Hello, and welcome to the future. How may I help?")
handler.write_json(response.as_dict())
return
if req_type != 'IntentRequest':
_LOGGER.warning('Received unsupported request: %s', req_type)
return
intent_name = intent['name']
config = _CONFIG.get(intent_name)
if config is None:
_LOGGER.warning('Received unknown intent %s', intent_name)
response.add_speech(
SpeechType.plaintext,
"This intent is not yet configured within Home Assistant.")
handler.write_json(response.as_dict())
return
speech = config.get(CONF_SPEECH)
card = config.get(CONF_CARD)
action = config.get(CONF_ACTION)
2015-12-13 06:29:02 +00:00
# pylint: disable=unsubscriptable-object
if speech is not None:
response.add_speech(SpeechType[speech['type']], speech['text'])
if card is not None:
response.add_card(CardType[card['type']], card['title'],
card['content'])
if action is not None:
call_from_config(handler.server.hass, action, True)
2015-12-13 06:29:02 +00:00
handler.write_json(response.as_dict())
class SpeechType(enum.Enum):
2016-02-23 20:06:50 +00:00
"""Alexa speech types."""
2015-12-13 06:29:02 +00:00
plaintext = "PlainText"
ssml = "SSML"
class CardType(enum.Enum):
2016-02-23 20:06:50 +00:00
"""Alexa card types."""
2015-12-13 06:29:02 +00:00
simple = "Simple"
link_account = "LinkAccount"
class AlexaResponse(object):
2016-02-23 20:06:50 +00:00
"""Helps generating the response for Alexa."""
2015-12-13 06:29:02 +00:00
def __init__(self, hass, intent=None):
self.hass = hass
self.speech = None
self.card = None
self.reprompt = None
self.session_attributes = {}
self.should_end_session = True
if intent is not None and 'slots' in intent:
self.variables = {key: value['value'] for key, value
2015-12-22 10:08:46 +00:00
in intent['slots'].items() if 'value' in value}
2015-12-13 06:29:02 +00:00
else:
self.variables = {}
def add_card(self, card_type, title, content):
""" Add a card to the response. """
assert self.card is None
card = {
"type": card_type.value
}
if card_type == CardType.link_account:
self.card = card
return
card["title"] = self._render(title),
card["content"] = self._render(content)
self.card = card
def add_speech(self, speech_type, text):
""" Add speech to the response. """
assert self.speech is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
self.speech = {
'type': speech_type.value,
key: self._render(text)
}
def add_reprompt(self, speech_type, text):
2016-02-23 20:06:50 +00:00
"""Add reprompt if user does not answer."""
2015-12-13 06:29:02 +00:00
assert self.reprompt is None
key = 'ssml' if speech_type == SpeechType.ssml else 'text'
self.reprompt = {
'type': speech_type.value,
key: self._render(text)
}
def as_dict(self):
2016-02-23 20:06:50 +00:00
"""Returns response in an Alexa valid dict."""
2015-12-13 06:29:02 +00:00
response = {
'shouldEndSession': self.should_end_session
}
if self.card is not None:
response['card'] = self.card
if self.speech is not None:
response['outputSpeech'] = self.speech
if self.reprompt is not None:
response['reprompt'] = {
'outputSpeech': self.reprompt
}
return {
'version': '1.0',
'sessionAttributes': self.session_attributes,
'response': response,
}
def _render(self, template_string):
2016-02-23 20:06:50 +00:00
"""Render a response, adding data from intent if available."""
2015-12-13 06:29:02 +00:00
return template.render(self.hass, template_string, self.variables)