diff --git a/homeassistant/helpers/intent.py b/homeassistant/helpers/intent.py index 5aa53f17e7b..4357c4109eb 100644 --- a/homeassistant/helpers/intent.py +++ b/homeassistant/helpers/intent.py @@ -137,7 +137,8 @@ class IntentHandler: if self._slot_schema is None: self._slot_schema = vol.Schema({ key: SLOT_SCHEMA.extend({'value': validator}) - for key, validator in self.slot_schema.items()}) + for key, validator in self.slot_schema.items()}, + extra=vol.ALLOW_EXTRA) return self._slot_schema(slots) diff --git a/tests/components/test_snips.py b/tests/components/test_snips.py index d9238336768..baeda2c49a8 100644 --- a/tests/components/test_snips.py +++ b/tests/components/test_snips.py @@ -5,6 +5,7 @@ import logging from homeassistant.bootstrap import async_setup_component from homeassistant.components.mqtt import MQTT_PUBLISH_SCHEMA import homeassistant.components.snips as snips +from homeassistant.helpers.intent import (ServiceIntentHandler, async_register) from tests.common import (async_fire_mqtt_message, async_mock_intent, async_mock_service) @@ -124,6 +125,49 @@ async def test_snips_intent(hass, mqtt_mock): assert intent.text_input == 'turn the lights green' +async def test_snips_service_intent(hass, mqtt_mock): + """Test ServiceIntentHandler via Snips.""" + hass.states.async_set('light.kitchen', 'off') + calls = async_mock_service(hass, 'light', 'turn_on') + result = await async_setup_component(hass, "snips", { + "snips": {}, + }) + assert result + payload = """ + { + "input": "turn the light on", + "intent": { + "intentName": "Lights", + "probability": 0.85 + }, + "siteId": "default", + "slots": [ + { + "slotName": "name", + "value": { + "kind": "Custom", + "value": "kitchen" + } + } + ] + } + """ + + async_register(hass, ServiceIntentHandler( + "Lights", "light", 'turn_on', "Turned {} on")) + + async_fire_mqtt_message(hass, 'hermes/intent/Lights', + payload) + await hass.async_block_till_done() + + assert len(calls) == 1 + assert calls[0].domain == 'light' + assert calls[0].service == 'turn_on' + assert calls[0].data['entity_id'] == 'light.kitchen' + assert 'probability' not in calls[0].data + assert 'site_id' not in calls[0].data + + async def test_snips_intent_with_duration(hass, mqtt_mock): """Test intent with Snips duration.""" result = await async_setup_component(hass, "snips", { diff --git a/tests/helpers/test_intent.py b/tests/helpers/test_intent.py index a8d37a249bc..707129ae531 100644 --- a/tests/helpers/test_intent.py +++ b/tests/helpers/test_intent.py @@ -1,6 +1,18 @@ """Tests for the intent helpers.""" + +import unittest +import voluptuous as vol + from homeassistant.core import State -from homeassistant.helpers import intent +from homeassistant.helpers import (intent, config_validation as cv) + + +class MockIntentHandler(intent.IntentHandler): + """Provide a mock intent handler.""" + + def __init__(self, slot_schema): + """Initialize the mock handler.""" + self.slot_schema = slot_schema def test_async_match_state(): @@ -10,3 +22,25 @@ def test_async_match_state(): state = intent.async_match_state(None, 'kitch', [state1, state2]) assert state is state1 + + +class TestIntentHandler(unittest.TestCase): + """Test the Home Assistant event helpers.""" + + def test_async_validate_slots(self): + """Test async_validate_slots of IntentHandler.""" + handler1 = MockIntentHandler({ + vol.Required('name'): cv.string, + }) + + self.assertRaises(vol.error.MultipleInvalid, + handler1.async_validate_slots, {}) + self.assertRaises(vol.error.MultipleInvalid, + handler1.async_validate_slots, {'name': 1}) + self.assertRaises(vol.error.MultipleInvalid, + handler1.async_validate_slots, {'name': 'kitchen'}) + handler1.async_validate_slots({'name': {'value': 'kitchen'}}) + handler1.async_validate_slots({ + 'name': {'value': 'kitchen'}, + 'probability': {'value': '0.5'} + })