Snips add say and say_actions services (new) (#11596)
* Added snips.say and snips.say_action services * Added snips.say and snips.say_action services * Merged services.yaml changes I missed * added tests for new service configs * Woof * Woof Woof * Changed attribute names to follow hass standards. * updated test_snips with new attribute names
This commit is contained in:
parent
cc236529c4
commit
b8e4c2ff69
3 changed files with 158 additions and 1 deletions
|
@ -364,6 +364,38 @@ abode:
|
||||||
description: Entity id of the quick action to trigger.
|
description: Entity id of the quick action to trigger.
|
||||||
example: 'binary_sensor.home_quick_action'
|
example: 'binary_sensor.home_quick_action'
|
||||||
|
|
||||||
|
snips:
|
||||||
|
say:
|
||||||
|
description: Send a TTS message to Snips.
|
||||||
|
fields:
|
||||||
|
text:
|
||||||
|
description: Text to say.
|
||||||
|
example: My name is snips
|
||||||
|
site_id:
|
||||||
|
description: Site to use to start session, defaults to default (optional)
|
||||||
|
example: bedroom
|
||||||
|
custom_data:
|
||||||
|
description: custom data that will be included with all messages in this session
|
||||||
|
example: user=UserName
|
||||||
|
say_action:
|
||||||
|
description: Send a TTS message to Snips to listen for a response.
|
||||||
|
fields:
|
||||||
|
text:
|
||||||
|
description: Text to say
|
||||||
|
example: My name is snips
|
||||||
|
site_id:
|
||||||
|
description: Site to use to start session, defaults to default (optional)
|
||||||
|
example: bedroom
|
||||||
|
custom_data:
|
||||||
|
description: custom data that will be included with all messages in this session
|
||||||
|
example: user=UserName
|
||||||
|
can_be_enqueued:
|
||||||
|
description: If True, session waits for an open session to end, if False session is dropped if one is running
|
||||||
|
example: True
|
||||||
|
intent_filter:
|
||||||
|
description: Optional Array of Strings - A list of intents names to restrict the NLU resolution to on the first query.
|
||||||
|
example: turnOnLights, turnOffLights
|
||||||
|
|
||||||
input_boolean:
|
input_boolean:
|
||||||
toggle:
|
toggle:
|
||||||
description: Toggles an input boolean.
|
description: Toggles an input boolean.
|
||||||
|
|
|
@ -8,17 +8,29 @@ import asyncio
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.helpers import intent, config_validation as cv
|
from homeassistant.helpers import intent, config_validation as cv
|
||||||
import homeassistant.components.mqtt as mqtt
|
import homeassistant.components.mqtt as mqtt
|
||||||
|
|
||||||
DOMAIN = 'snips'
|
DOMAIN = 'snips'
|
||||||
DEPENDENCIES = ['mqtt']
|
DEPENDENCIES = ['mqtt']
|
||||||
|
|
||||||
CONF_INTENTS = 'intents'
|
CONF_INTENTS = 'intents'
|
||||||
CONF_ACTION = 'action'
|
CONF_ACTION = 'action'
|
||||||
|
|
||||||
|
SERVICE_SAY = 'say'
|
||||||
|
SERVICE_SAY_ACTION = 'say_action'
|
||||||
|
|
||||||
INTENT_TOPIC = 'hermes/intent/#'
|
INTENT_TOPIC = 'hermes/intent/#'
|
||||||
|
|
||||||
|
ATTR_TEXT = 'text'
|
||||||
|
ATTR_SITE_ID = 'site_id'
|
||||||
|
ATTR_CUSTOM_DATA = 'custom_data'
|
||||||
|
ATTR_CAN_BE_ENQUEUED = 'can_be_enqueued'
|
||||||
|
ATTR_INTENT_FILTER = 'intent_filter'
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema({
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
@ -40,6 +52,20 @@ INTENT_SCHEMA = vol.Schema({
|
||||||
}]
|
}]
|
||||||
}, extra=vol.ALLOW_EXTRA)
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
SERVICE_SCHEMA_SAY = vol.Schema({
|
||||||
|
vol.Required(ATTR_TEXT): str,
|
||||||
|
vol.Optional(ATTR_SITE_ID, default='default'): str,
|
||||||
|
vol.Optional(ATTR_CUSTOM_DATA, default=''): str
|
||||||
|
})
|
||||||
|
|
||||||
|
SERVICE_SCHEMA_SAY_ACTION = vol.Schema({
|
||||||
|
vol.Required(ATTR_TEXT): str,
|
||||||
|
vol.Optional(ATTR_SITE_ID, default='default'): str,
|
||||||
|
vol.Optional(ATTR_CUSTOM_DATA, default=''): str,
|
||||||
|
vol.Optional(ATTR_CAN_BE_ENQUEUED, default=True): cv.boolean,
|
||||||
|
vol.Optional(ATTR_INTENT_FILTER): vol.All(cv.ensure_list),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_setup(hass, config):
|
def async_setup(hass, config):
|
||||||
|
@ -93,6 +119,39 @@ def async_setup(hass, config):
|
||||||
yield from hass.components.mqtt.async_subscribe(
|
yield from hass.components.mqtt.async_subscribe(
|
||||||
INTENT_TOPIC, message_received)
|
INTENT_TOPIC, message_received)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def snips_say(call):
|
||||||
|
"""Send a Snips notification message."""
|
||||||
|
notification = {'siteId': call.data.get(ATTR_SITE_ID, 'default'),
|
||||||
|
'customData': call.data.get(ATTR_CUSTOM_DATA, ''),
|
||||||
|
'init': {'type': 'notification',
|
||||||
|
'text': call.data.get(ATTR_TEXT)}}
|
||||||
|
mqtt.async_publish(hass, 'hermes/dialogueManager/startSession',
|
||||||
|
json.dumps(notification))
|
||||||
|
return
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def snips_say_action(call):
|
||||||
|
"""Send a Snips action message."""
|
||||||
|
notification = {'siteId': call.data.get(ATTR_SITE_ID, 'default'),
|
||||||
|
'customData': call.data.get(ATTR_CUSTOM_DATA, ''),
|
||||||
|
'init': {'type': 'action',
|
||||||
|
'text': call.data.get(ATTR_TEXT),
|
||||||
|
'canBeEnqueued': call.data.get(
|
||||||
|
ATTR_CAN_BE_ENQUEUED, True),
|
||||||
|
'intentFilter':
|
||||||
|
call.data.get(ATTR_INTENT_FILTER, [])}}
|
||||||
|
mqtt.async_publish(hass, 'hermes/dialogueManager/startSession',
|
||||||
|
json.dumps(notification))
|
||||||
|
return
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN, SERVICE_SAY, snips_say,
|
||||||
|
schema=SERVICE_SCHEMA_SAY)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN, SERVICE_SAY_ACTION, snips_say_action,
|
||||||
|
schema=SERVICE_SCHEMA_SAY_ACTION)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,10 @@ import json
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.bootstrap import async_setup_component
|
from homeassistant.bootstrap import async_setup_component
|
||||||
from tests.common import async_fire_mqtt_message, async_mock_intent
|
from tests.common import (async_fire_mqtt_message, async_mock_intent,
|
||||||
|
async_mock_service)
|
||||||
|
from homeassistant.components.snips import (SERVICE_SCHEMA_SAY,
|
||||||
|
SERVICE_SCHEMA_SAY_ACTION)
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
|
@ -238,3 +241,66 @@ def test_snips_intent_username(hass, mqtt_mock):
|
||||||
intent = intents[0]
|
intent = intents[0]
|
||||||
assert intent.platform == 'snips'
|
assert intent.platform == 'snips'
|
||||||
assert intent.intent_type == 'Lights'
|
assert intent.intent_type == 'Lights'
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_snips_say(hass, caplog):
|
||||||
|
"""Test snips say with invalid config."""
|
||||||
|
calls = async_mock_service(hass, 'snips', 'say',
|
||||||
|
SERVICE_SCHEMA_SAY)
|
||||||
|
|
||||||
|
data = {'text': 'Hello'}
|
||||||
|
yield from hass.services.async_call('snips', 'say', data)
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].domain == 'snips'
|
||||||
|
assert calls[0].service == 'say'
|
||||||
|
assert calls[0].data['text'] == 'Hello'
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_snips_say_action(hass, caplog):
|
||||||
|
"""Test snips say_action with invalid config."""
|
||||||
|
calls = async_mock_service(hass, 'snips', 'say_action',
|
||||||
|
SERVICE_SCHEMA_SAY_ACTION)
|
||||||
|
|
||||||
|
data = {'text': 'Hello', 'intent_filter': ['myIntent']}
|
||||||
|
yield from hass.services.async_call('snips', 'say_action', data)
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(calls) == 1
|
||||||
|
assert calls[0].domain == 'snips'
|
||||||
|
assert calls[0].service == 'say_action'
|
||||||
|
assert calls[0].data['text'] == 'Hello'
|
||||||
|
assert calls[0].data['intent_filter'] == ['myIntent']
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_snips_say_invalid_config(hass, caplog):
|
||||||
|
"""Test snips say with invalid config."""
|
||||||
|
calls = async_mock_service(hass, 'snips', 'say',
|
||||||
|
SERVICE_SCHEMA_SAY)
|
||||||
|
|
||||||
|
data = {'text': 'Hello', 'badKey': 'boo'}
|
||||||
|
yield from hass.services.async_call('snips', 'say', data)
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(calls) == 0
|
||||||
|
assert 'ERROR' in caplog.text
|
||||||
|
assert 'Invalid service data' in caplog.text
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_snips_say_action_invalid_config(hass, caplog):
|
||||||
|
"""Test snips say_action with invalid config."""
|
||||||
|
calls = async_mock_service(hass, 'snips', 'say_action',
|
||||||
|
SERVICE_SCHEMA_SAY_ACTION)
|
||||||
|
|
||||||
|
data = {'text': 'Hello', 'can_be_enqueued': 'notabool'}
|
||||||
|
yield from hass.services.async_call('snips', 'say_action', data)
|
||||||
|
yield from hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert len(calls) == 0
|
||||||
|
assert 'ERROR' in caplog.text
|
||||||
|
assert 'Invalid service data' in caplog.text
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue