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 namespull/11589/head
parent
b8dfa4c3d2
commit
179d99381d
|
@ -364,6 +364,38 @@ abode:
|
|||
description: Entity id of the quick action to trigger.
|
||||
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:
|
||||
toggle:
|
||||
description: Toggles an input boolean.
|
||||
|
|
|
@ -8,17 +8,29 @@ 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']
|
||||
|
||||
CONF_INTENTS = 'intents'
|
||||
CONF_ACTION = 'action'
|
||||
|
||||
SERVICE_SAY = 'say'
|
||||
SERVICE_SAY_ACTION = 'say_action'
|
||||
|
||||
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__)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
|
@ -40,6 +52,20 @@ INTENT_SCHEMA = vol.Schema({
|
|||
}]
|
||||
}, 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
|
||||
def async_setup(hass, config):
|
||||
|
@ -93,6 +119,39 @@ def async_setup(hass, config):
|
|||
yield from hass.components.mqtt.async_subscribe(
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ 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
|
||||
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
|
||||
|
@ -238,3 +241,66 @@ def test_snips_intent_username(hass, mqtt_mock):
|
|||
intent = intents[0]
|
||||
assert intent.platform == 'snips'
|
||||
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…
Reference in New Issue