Snips (new) added speech response, parse snips/duration (#11513)

* Snips changed to using <username>:intentName instead of user_IDSTRING__intentName

* added response to snips

* blank line

* added unittests

* houndy

* sdtuff

* more stuff

* Update test_snips.py

* Update snips.py

* Split log tests to avoid dict ordering in py34

* Update test_snips.py

* Update test_snips.py

* still broken

* fixed tests

* fixed tests

* removed fix submitted in another PR
This commit is contained in:
tschmidty69 2018-01-07 16:42:08 -05:00 committed by Paulus Schoutsen
parent efb83dde19
commit 4a6b5ba02b
2 changed files with 202 additions and 28 deletions

View File

@ -7,8 +7,10 @@ https://home-assistant.io/components/snips/
import asyncio
import json
import logging
from datetime import timedelta
import voluptuous as vol
from homeassistant.helpers import intent, config_validation as cv
import homeassistant.components.mqtt as mqtt
DOMAIN = 'snips'
DEPENDENCIES = ['mqtt']
@ -59,21 +61,52 @@ def async_setup(hass, config):
_LOGGER.error('Intent has invalid schema: %s. %s', err, request)
return
snips_response = None
intent_type = request['intent']['intentName'].split('__')[-1]
slots = {}
for slot in request.get('slots', []):
if 'value' in slot['value']:
slots[slot['slotName']] = {'value': slot['value']['value']}
else:
slots[slot['slotName']] = {'value': slot['rawValue']}
slots[slot['slotName']] = {'value': resolve_slot_values(slot)}
try:
yield from intent.async_handle(
intent_response = yield from intent.async_handle(
hass, DOMAIN, intent_type, slots, request['input'])
if 'plain' in intent_response.speech:
snips_response = intent_response.speech['plain']['speech']
except intent.UnknownIntent as err:
_LOGGER.warning("Received unknown intent %s",
request['intent']['intentName'])
snips_response = "Unknown Intent"
except intent.IntentError:
_LOGGER.exception("Error while handling intent: %s.", intent_type)
snips_response = "Error while handling intent"
notification = {'sessionId': request.get('sessionId', 'default'),
'text': snips_response}
_LOGGER.debug("send_response %s", json.dumps(notification))
mqtt.async_publish(hass, 'hermes/dialogueManager/endSession',
json.dumps(notification))
yield from hass.components.mqtt.async_subscribe(
INTENT_TOPIC, message_received)
return True
def resolve_slot_values(slot):
"""Convert snips builtin types to useable values."""
if 'value' in slot['value']:
value = slot['value']['value']
else:
value = slot['rawValue']
if slot.get('entity') == "snips/duration":
delta = timedelta(weeks=slot['value']['weeks'],
days=slot['value']['days'],
hours=slot['value']['hours'],
minutes=slot['value']['minutes'],
seconds=slot['value']['seconds'])
value = delta.seconds
return value

View File

@ -1,41 +1,42 @@
"""Test the Snips component."""
import asyncio
import json
from homeassistant.core import callback
from homeassistant.bootstrap import async_setup_component
from tests.common import async_fire_mqtt_message, async_mock_intent
EXAMPLE_MSG = """
{
"input": "turn the lights green",
"intent": {
"intentName": "Lights",
"probability": 1
},
"slots": [
{
"slotName": "light_color",
"value": {
"kind": "Custom",
"value": "green"
}
}
]
}
"""
@asyncio.coroutine
def test_snips_call_action(hass, mqtt_mock):
"""Test calling action via Snips."""
def test_snips_intent(hass, mqtt_mock):
"""Test intent via Snips."""
result = yield from async_setup_component(hass, "snips", {
"snips": {},
})
assert result
payload = """
{
"input": "turn the lights green",
"intent": {
"intentName": "Lights",
"probability": 1
},
"slots": [
{
"slotName": "light_color",
"value": {
"kind": "Custom",
"value": "green"
}
}
]
}
"""
intents = async_mock_intent(hass, 'Lights')
async_fire_mqtt_message(hass, 'hermes/intent/activateLights',
EXAMPLE_MSG)
async_fire_mqtt_message(hass, 'hermes/intent/Lights',
payload)
yield from hass.async_block_till_done()
assert len(intents) == 1
intent = intents[0]
@ -43,3 +44,143 @@ def test_snips_call_action(hass, mqtt_mock):
assert intent.intent_type == 'Lights'
assert intent.slots == {'light_color': {'value': 'green'}}
assert intent.text_input == 'turn the lights green'
@asyncio.coroutine
def test_snips_intent_with_snips_duration(hass, mqtt_mock):
"""Test intent with Snips duration."""
result = yield from async_setup_component(hass, "snips", {
"snips": {},
})
assert result
payload = """
{
"input": "set a timer of five minutes",
"intent": {
"intentName": "SetTimer"
},
"slots": [
{
"rawValue": "five minutes",
"value": {
"kind": "Duration",
"years": 0,
"quarters": 0,
"months": 0,
"weeks": 0,
"days": 0,
"hours": 0,
"minutes": 5,
"seconds": 0,
"precision": "Exact"
},
"range": {
"start": 15,
"end": 27
},
"entity": "snips/duration",
"slotName": "timer_duration"
}
]
}
"""
intents = async_mock_intent(hass, 'SetTimer')
async_fire_mqtt_message(hass, 'hermes/intent/SetTimer',
payload)
yield from hass.async_block_till_done()
assert len(intents) == 1
intent = intents[0]
assert intent.platform == 'snips'
assert intent.intent_type == 'SetTimer'
assert intent.slots == {'timer_duration': {'value': 300}}
@asyncio.coroutine
def test_intent_speech_response(hass, mqtt_mock):
"""Test intent speech response via Snips."""
event = 'call_service'
events = []
@callback
def record_event(event):
"""Add recorded event to set."""
events.append(event)
result = yield from async_setup_component(hass, "snips", {
"snips": {},
})
assert result
result = yield from async_setup_component(hass, "intent_script", {
"intent_script": {
"spokenIntent": {
"speech": {
"type": "plain",
"text": "I am speaking to you"
}
}
}
})
assert result
payload = """
{
"input": "speak to me",
"sessionId": "abcdef0123456789",
"intent": {
"intentName": "spokenIntent"
},
"slots": []
}
"""
hass.bus.async_listen(event, record_event)
async_fire_mqtt_message(hass, 'hermes/intent/spokenIntent',
payload)
yield from hass.async_block_till_done()
assert len(events) == 1
assert events[0].data['domain'] == 'mqtt'
assert events[0].data['service'] == 'publish'
payload = json.loads(events[0].data['service_data']['payload'])
topic = events[0].data['service_data']['topic']
assert payload['sessionId'] == 'abcdef0123456789'
assert payload['text'] == 'I am speaking to you'
assert topic == 'hermes/dialogueManager/endSession'
@asyncio.coroutine
def test_snips_unknown_intent(hass, mqtt_mock):
"""Test calling unknown Intent via Snips."""
event = 'call_service'
events = []
@callback
def record_event(event):
"""Add recorded event to set."""
events.append(event)
result = yield from async_setup_component(hass, "snips", {
"snips": {},
})
assert result
payload = """
{
"input": "what to do",
"intent": {
"intentName": "unknownIntent"
},
"slots": []
}
"""
intents = async_mock_intent(hass, 'knownIntent')
hass.bus.async_listen(event, record_event)
async_fire_mqtt_message(hass, 'hermes/intent/unknownIntent',
payload)
yield from hass.async_block_till_done()
assert len(intents) == 0
assert len(events) == 1
assert events[0].data['domain'] == 'mqtt'
assert events[0].data['service'] == 'publish'
payload = json.loads(events[0].data['service_data']['payload'])
topic = events[0].data['service_data']['topic']
assert payload['text'] == 'Unknown Intent'
assert topic == 'hermes/dialogueManager/endSession'