core/tests/components/snips/test_init.py

543 lines
15 KiB
Python

"""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