-
-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Snips sounds #13746
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Snips sounds #13746
Changes from 5 commits
7bef9db
2bb36ba
11b4dcc
fb00907
26258a5
a9caf7e
3753a25
3a597fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,6 @@ | |
| For more details about this component, please refer to the documentation at | ||
| https://home-assistant.io/components/snips/ | ||
| """ | ||
| import asyncio | ||
| import json | ||
| import logging | ||
| from datetime import timedelta | ||
|
|
@@ -19,11 +18,18 @@ | |
|
|
||
| CONF_INTENTS = 'intents' | ||
| CONF_ACTION = 'action' | ||
| CONF_FEEDBACK = 'feedback_sounds' | ||
| CONF_PROBABILITY = 'probability_threshold' | ||
| CONF_SITE_IDS = 'site_ids' | ||
|
|
||
| SERVICE_SAY = 'say' | ||
| SERVICE_SAY_ACTION = 'say_action' | ||
| SERVICE_FEEDBACK_ON = 'feedback_on' | ||
| SERVICE_FEEDBACK_OFF = 'feedback_off' | ||
|
|
||
| INTENT_TOPIC = 'hermes/intent/#' | ||
| FEEDBACK_ON_TOPIC = 'hermes/feedback/sound/toggleOn' | ||
| FEEDBACK_OFF_TOPIC = 'hermes/feedback/sound/toggleOff' | ||
|
|
||
| ATTR_TEXT = 'text' | ||
| ATTR_SITE_ID = 'site_id' | ||
|
|
@@ -34,7 +40,12 @@ | |
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| CONFIG_SCHEMA = vol.Schema({ | ||
| DOMAIN: {} | ||
| DOMAIN: vol.Schema({ | ||
| vol.Optional(CONF_FEEDBACK, default=False): cv.boolean, | ||
| vol.Optional(CONF_PROBABILITY, default=0): vol.Coerce(float), | ||
| vol.Optional(CONF_SITE_IDS, default=['default']): | ||
| vol.All(cv.ensure_list, [cv.string]), | ||
| }), | ||
| }, extra=vol.ALLOW_EXTRA) | ||
|
|
||
| INTENT_SCHEMA = vol.Schema({ | ||
|
|
@@ -57,21 +68,36 @@ | |
| 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), | ||
| }) | ||
| SERVICE_SCHEMA_FEEDBACK = vol.Schema({ | ||
| vol.Optional(ATTR_SITE_ID, default='default'): str | ||
| }) | ||
|
|
||
|
|
||
| @asyncio.coroutine | ||
| def async_setup(hass, config): | ||
| async def async_setup(hass, config): | ||
| """Activate Snips component.""" | ||
| @asyncio.coroutine | ||
| def message_received(topic, payload, qos): | ||
| def set_feedback(site_ids, state): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is called from inside async context, plase add |
||
| """Set Feedback sound state.""" | ||
| site_ids = (site_ids if site_ids | ||
| else config[DOMAIN].get(CONF_SITE_IDS)) | ||
| topic = (FEEDBACK_ON_TOPIC if state | ||
| else FEEDBACK_OFF_TOPIC) | ||
| for site_id in site_ids: | ||
| payload = json.dumps({'siteId': site_id}) | ||
| hass.components.mqtt.async_publish( | ||
| FEEDBACK_ON_TOPIC, None, qos=0, retain=False) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are you doing here? Is this to clear the retained message?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't you clear the retained message on the off topic if setting the on topic?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Off isn't set to be retained since that is the default if snips restarts Edit: Doh, I see what you mean I didn't have it set before, fixed. |
||
| hass.components.mqtt.async_publish( | ||
| topic, payload, qos=1, retain=True) | ||
|
|
||
| set_feedback(None, config[DOMAIN].get(CONF_FEEDBACK)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this should have a default. If no value is given, we should not send any messages. That way it can be controlled by the outside.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, was back and forth there and could see it either way but ok.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, there has to be a better way to do this. I think it should be if nothing is configured we don't send anything, if false is configured we do. But this seems hacky but how to test for not set? |
||
|
|
||
| async def message_received(topic, payload, qos): | ||
| """Handle new messages on MQTT.""" | ||
| _LOGGER.debug("New intent: %s", payload) | ||
|
|
||
|
|
@@ -81,6 +107,13 @@ def message_received(topic, payload, qos): | |
| _LOGGER.error('Received invalid JSON: %s', payload) | ||
| return | ||
|
|
||
| if (request['intent']['probability'] | ||
| < config[DOMAIN].get(CONF_PROBABILITY)): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. default for probability is 0. So if none is configured, nothing will ever pass? Better to not have a probability default and just check if probability is None or intentProb < probability
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probability (editL was: default) is checked for greater than, so 0 matches all. It's a lower limit not upper.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The config option defaults to 0. If not configured, probability defaults to 0. So you're checking here things like Also, if you know a key exists, prefer to use square brackets over
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 0.8 < 0 is false yes, so we accept the intent. If I set the default to 1 then we would by default not accept any intents.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see it now. My bad. |
||
| _LOGGER.warning("Intent below probaility threshold %s < %s", | ||
| request['intent']['probability'], | ||
| config[DOMAIN].get(CONF_PROBABILITY)) | ||
| return | ||
|
|
||
| try: | ||
| request = INTENT_SCHEMA(request) | ||
| except vol.Invalid as err: | ||
|
|
@@ -97,7 +130,7 @@ def message_received(topic, payload, qos): | |
| slots[slot['slotName']] = {'value': resolve_slot_values(slot)} | ||
|
|
||
| try: | ||
| intent_response = yield from intent.async_handle( | ||
| intent_response = await intent.async_handle( | ||
| hass, DOMAIN, intent_type, slots, request['input']) | ||
| if 'plain' in intent_response.speech: | ||
| snips_response = intent_response.speech['plain']['speech'] | ||
|
|
@@ -115,11 +148,10 @@ def message_received(topic, payload, qos): | |
| mqtt.async_publish(hass, 'hermes/dialogueManager/endSession', | ||
| json.dumps(notification)) | ||
|
|
||
| yield from hass.components.mqtt.async_subscribe( | ||
| await hass.components.mqtt.async_subscribe( | ||
| INTENT_TOPIC, message_received) | ||
|
|
||
| @asyncio.coroutine | ||
| def snips_say(call): | ||
| async 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, ''), | ||
|
|
@@ -129,8 +161,7 @@ def snips_say(call): | |
| json.dumps(notification)) | ||
| return | ||
|
|
||
| @asyncio.coroutine | ||
| def snips_say_action(call): | ||
| async 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, ''), | ||
|
|
@@ -144,12 +175,26 @@ def snips_say_action(call): | |
| json.dumps(notification)) | ||
| return | ||
|
|
||
| async def feedback_on(call): | ||
| """Turn feedback sounds on.""" | ||
| set_feedback(call.data.get(ATTR_SITE_ID), True) | ||
|
|
||
| async def feedback_off(call): | ||
| """Turn feedback sounds off.""" | ||
| set_feedback(call.data.get(ATTR_SITE_ID), False) | ||
|
|
||
| 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) | ||
| hass.services.async_register( | ||
| DOMAIN, SERVICE_FEEDBACK_ON, feedback_on, | ||
| schema=SERVICE_SCHEMA_FEEDBACK) | ||
| hass.services.async_register( | ||
| DOMAIN, SERVICE_FEEDBACK_OFF, feedback_off, | ||
| schema=SERVICE_SCHEMA_FEEDBACK) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be configurable per site id?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Figured that was enough of an edge case it wasn't worth complicating the config for. They can turn on or off a site via the service but I can't imagine there are many cases where anyone wants to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to limit this functionality to just the service? You don't need to change it all the time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, it's pretty minor to add it and I think a lot of users will like to set it here, especially considering snips doesn't have the option yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Especially for something like hassio where you reboot and it's nice to have it back on if you want it.