"""Test the Snips component.""" import json import logging import pytest import voluptuous as vol 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_mqtt_component, async_mock_service, ) async def test_snips_config(hass): """Test Snips Config.""" await async_mock_mqtt_component(hass) result = await async_setup_component( hass, "snips", { "snips": { "feedback_sounds": True, "probability_threshold": 0.5, "site_ids": ["default", "remote"], } }, ) assert result async def test_snips_bad_config(hass): """Test Snips bad config.""" await async_mock_mqtt_component(hass) result = await async_setup_component( hass, "snips", { "snips": { "feedback_sounds": "on", "probability": "none", "site_ids": "default", } }, ) assert not result async def test_snips_config_feedback_on(hass): """Test Snips Config.""" await async_mock_mqtt_component(hass) calls = async_mock_service(hass, "mqtt", "publish", MQTT_PUBLISH_SCHEMA) result = await async_setup_component( hass, "snips", {"snips": {"feedback_sounds": True}} ) assert result await hass.async_block_till_done() assert len(calls) == 2 topic = calls[0].data["topic"] assert topic == "hermes/feedback/sound/toggleOn" topic = calls[1].data["topic"] assert topic == "hermes/feedback/sound/toggleOn" assert calls[1].data["qos"] == 1 assert calls[1].data["retain"] async def test_snips_config_feedback_off(hass): """Test Snips Config.""" await async_mock_mqtt_component(hass) calls = async_mock_service(hass, "mqtt", "publish", MQTT_PUBLISH_SCHEMA) result = await async_setup_component( hass, "snips", {"snips": {"feedback_sounds": False}} ) assert result await hass.async_block_till_done() assert len(calls) == 2 topic = calls[0].data["topic"] assert topic == "hermes/feedback/sound/toggleOn" topic = calls[1].data["topic"] assert topic == "hermes/feedback/sound/toggleOff" assert calls[1].data["qos"] == 0 assert not calls[1].data["retain"] async def test_snips_config_no_feedback(hass): """Test Snips Config.""" await async_mock_mqtt_component(hass) calls = async_mock_service(hass, "snips", "say") result = await async_setup_component(hass, "snips", {"snips": {}}) assert result await hass.async_block_till_done() assert len(calls) == 0 async def test_snips_intent(hass): """Test intent via Snips.""" await async_mock_mqtt_component(hass) result = await async_setup_component(hass, "snips", {"snips": {}}) assert result payload = """ { "siteId": "default", "sessionId": "1234567890ABCDEF", "input": "turn the lights green", "intent": { "intentName": "Lights", "confidenceScore": 1 }, "slots": [ { "slotName": "light_color", "value": { "kind": "Custom", "value": "green" }, "rawValue": "green" } ] } """ intents = async_mock_intent(hass, "Lights") async_fire_mqtt_message(hass, "hermes/intent/Lights", payload) await hass.async_block_till_done() assert len(intents) == 1 intent = intents[0] assert intent.platform == "snips" assert intent.intent_type == "Lights" assert intent assert intent.slots == { "light_color": {"value": "green"}, "light_color_raw": {"value": "green"}, "confidenceScore": {"value": 1}, "site_id": {"value": "default"}, "session_id": {"value": "1234567890ABCDEF"}, } assert intent.text_input == "turn the lights green" async def test_snips_service_intent(hass): """Test ServiceIntentHandler via Snips.""" await async_mock_mqtt_component(hass) 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", "confidenceScore": 0.85 }, "siteId": "default", "slots": [ { "slotName": "name", "value": { "kind": "Custom", "value": "kitchen" }, "rawValue": "green" } ] } """ 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 "confidenceScore" not in calls[0].data assert "site_id" not in calls[0].data async def test_snips_intent_with_duration(hass): """Test intent with Snips duration.""" await async_mock_mqtt_component(hass) result = await async_setup_component(hass, "snips", {"snips": {}}) assert result payload = """ { "input": "set a timer of five minutes", "intent": { "intentName": "SetTimer", "confidenceScore": 1 }, "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) await hass.async_block_till_done() assert len(intents) == 1 intent = intents[0] assert intent.platform == "snips" assert intent.intent_type == "SetTimer" assert intent.slots == { "confidenceScore": {"value": 1}, "site_id": {"value": None}, "session_id": {"value": None}, "timer_duration": {"value": 300}, "timer_duration_raw": {"value": "five minutes"}, } async def test_intent_speech_response(hass): """Test intent speech response via Snips.""" await async_mock_mqtt_component(hass) calls = async_mock_service(hass, "mqtt", "publish", MQTT_PUBLISH_SCHEMA) result = await async_setup_component(hass, "snips", {"snips": {}}) assert result result = await 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", "confidenceScore": 1 }, "slots": [] } """ async_fire_mqtt_message(hass, "hermes/intent/spokenIntent", payload) await hass.async_block_till_done() assert len(calls) == 1 payload = json.loads(calls[0].data["payload"]) topic = calls[0].data["topic"] assert payload["sessionId"] == "abcdef0123456789" assert payload["text"] == "I am speaking to you" assert topic == "hermes/dialogueManager/endSession" async def test_unknown_intent(hass, caplog): """Test unknown intent.""" await async_mock_mqtt_component(hass) caplog.set_level(logging.WARNING) result = await async_setup_component(hass, "snips", {"snips": {}}) assert result payload = """ { "input": "I don't know what I am supposed to do", "sessionId": "abcdef1234567890", "intent": { "intentName": "unknownIntent", "confidenceScore": 1 }, "slots": [] } """ async_fire_mqtt_message(hass, "hermes/intent/unknownIntent", payload) await hass.async_block_till_done() assert "Received unknown intent unknownIntent" in caplog.text async def test_snips_intent_user(hass): """Test intentName format user_XXX__intentName.""" await async_mock_mqtt_component(hass) result = await async_setup_component(hass, "snips", {"snips": {}}) assert result payload = """ { "input": "what to do", "intent": { "intentName": "user_ABCDEF123__Lights", "confidenceScore": 1 }, "slots": [] } """ intents = async_mock_intent(hass, "Lights") async_fire_mqtt_message(hass, "hermes/intent/user_ABCDEF123__Lights", payload) await hass.async_block_till_done() assert len(intents) == 1 intent = intents[0] assert intent.platform == "snips" assert intent.intent_type == "Lights" async def test_snips_intent_username(hass): """Test intentName format username:intentName.""" await async_mock_mqtt_component(hass) result = await async_setup_component(hass, "snips", {"snips": {}}) assert result payload = """ { "input": "what to do", "intent": { "intentName": "username:Lights", "confidenceScore": 1 }, "slots": [] } """ intents = async_mock_intent(hass, "Lights") async_fire_mqtt_message(hass, "hermes/intent/username:Lights", payload) await hass.async_block_till_done() assert len(intents) == 1 intent = intents[0] assert intent.platform == "snips" assert intent.intent_type == "Lights" async def test_snips_low_probability(hass, caplog): """Test intent via Snips.""" await async_mock_mqtt_component(hass) caplog.set_level(logging.WARNING) result = await async_setup_component( hass, "snips", {"snips": {"probability_threshold": 0.5}} ) assert result payload = """ { "input": "I am not sure what to say", "intent": { "intentName": "LightsMaybe", "confidenceScore": 0.49 }, "slots": [] } """ async_mock_intent(hass, "LightsMaybe") async_fire_mqtt_message(hass, "hermes/intent/LightsMaybe", payload) await hass.async_block_till_done() assert "Intent below probaility threshold 0.49 < 0.5" in caplog.text async def test_intent_special_slots(hass): """Test intent special slot values via Snips.""" await async_mock_mqtt_component(hass) calls = async_mock_service(hass, "light", "turn_on") result = await async_setup_component(hass, "snips", {"snips": {}}) assert result result = await async_setup_component( hass, "intent_script", { "intent_script": { "Lights": { "action": { "service": "light.turn_on", "data_template": { "confidenceScore": "{{ confidenceScore }}", "site_id": "{{ site_id }}", }, } } } }, ) assert result payload = """ { "input": "turn the light on", "intent": { "intentName": "Lights", "confidenceScore": 0.85 }, "siteId": "default", "slots": [] } """ 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["confidenceScore"] == "0.85" assert calls[0].data["site_id"] == "default" async def test_snips_say(hass): """Test snips say with invalid config.""" calls = async_mock_service(hass, "snips", "say", snips.SERVICE_SCHEMA_SAY) data = {"text": "Hello"} await hass.services.async_call("snips", "say", data) await 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" async def test_snips_say_action(hass): """Test snips say_action with invalid config.""" calls = async_mock_service( hass, "snips", "say_action", snips.SERVICE_SCHEMA_SAY_ACTION ) data = {"text": "Hello", "intent_filter": ["myIntent"]} await hass.services.async_call("snips", "say_action", data) await 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"] async def test_snips_say_invalid_config(hass): """Test snips say with invalid config.""" calls = async_mock_service(hass, "snips", "say", snips.SERVICE_SCHEMA_SAY) data = {"text": "Hello", "badKey": "boo"} with pytest.raises(vol.Invalid): await hass.services.async_call("snips", "say", data) await hass.async_block_till_done() assert len(calls) == 0 async def test_snips_say_action_invalid(hass): """Test snips say_action with invalid config.""" calls = async_mock_service( hass, "snips", "say_action", snips.SERVICE_SCHEMA_SAY_ACTION ) data = {"text": "Hello", "can_be_enqueued": "notabool"} with pytest.raises(vol.Invalid): await hass.services.async_call("snips", "say_action", data) await hass.async_block_till_done() assert len(calls) == 0 async def test_snips_feedback_on(hass): """Test snips say with invalid config.""" calls = async_mock_service( hass, "snips", "feedback_on", snips.SERVICE_SCHEMA_FEEDBACK ) data = {"site_id": "remote"} await hass.services.async_call("snips", "feedback_on", data) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].domain == "snips" assert calls[0].service == "feedback_on" assert calls[0].data["site_id"] == "remote" async def test_snips_feedback_off(hass): """Test snips say with invalid config.""" calls = async_mock_service( hass, "snips", "feedback_off", snips.SERVICE_SCHEMA_FEEDBACK ) data = {"site_id": "remote"} await hass.services.async_call("snips", "feedback_off", data) await hass.async_block_till_done() assert len(calls) == 1 assert calls[0].domain == "snips" assert calls[0].service == "feedback_off" assert calls[0].data["site_id"] == "remote" async def test_snips_feedback_config(hass): """Test snips say with invalid config.""" calls = async_mock_service( hass, "snips", "feedback_on", snips.SERVICE_SCHEMA_FEEDBACK ) data = {"site_id": "remote", "test": "test"} with pytest.raises(vol.Invalid): await hass.services.async_call("snips", "feedback_on", data) await hass.async_block_till_done() assert len(calls) == 0