diff --git a/homeassistant/components/snips/__init__.py b/homeassistant/components/snips/__init__.py deleted file mode 100644 index 293caeaedac..00000000000 --- a/homeassistant/components/snips/__init__.py +++ /dev/null @@ -1,257 +0,0 @@ -"""Support for Snips on-device ASR and NLU.""" - -from datetime import timedelta -import json -import logging - -import voluptuous as vol - -from homeassistant.components import mqtt -from homeassistant.core import ( - DOMAIN as HOMEASSISTANT_DOMAIN, - HomeAssistant, - ServiceCall, -) -from homeassistant.helpers import config_validation as cv, intent -from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue -from homeassistant.helpers.typing import ConfigType - -DOMAIN = "snips" -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" -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( - { - DOMAIN: vol.Schema( - { - vol.Optional(CONF_FEEDBACK): 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( - { - vol.Required("input"): str, - vol.Required("intent"): {vol.Required("intentName"): str}, - vol.Optional("slots"): [ - { - vol.Required("slotName"): str, - vol.Required("value"): { - vol.Required("kind"): str, - vol.Optional("value"): cv.match_all, - vol.Optional("rawValue"): cv.match_all, - }, - } - ], - }, - 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), - } -) -SERVICE_SCHEMA_FEEDBACK = vol.Schema( - {vol.Optional(ATTR_SITE_ID, default="default"): str} -) - - -async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: - """Activate Snips component.""" - async_create_issue( - hass, - HOMEASSISTANT_DOMAIN, - f"deprecated_system_packages_yaml_integration_{DOMAIN}", - breaks_in_ha_version="2025.12.0", - is_fixable=False, - issue_domain=DOMAIN, - severity=IssueSeverity.WARNING, - translation_key="deprecated_system_packages_yaml_integration", - translation_placeholders={ - "domain": DOMAIN, - "integration_title": "Snips", - }, - ) - - # Make sure MQTT integration is enabled and the client is available - if not await mqtt.async_wait_for_mqtt_client(hass): - _LOGGER.error("MQTT integration is not available") - return False - - async def async_set_feedback(site_ids, state): - """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}) - await mqtt.async_publish(hass, FEEDBACK_ON_TOPIC, "", qos=0, retain=False) - await mqtt.async_publish(hass, topic, payload, qos=int(state), retain=state) - - if CONF_FEEDBACK in config[DOMAIN]: - await async_set_feedback(None, config[DOMAIN][CONF_FEEDBACK]) - - async def message_received(msg): - """Handle new messages on MQTT.""" - _LOGGER.debug("New intent: %s", msg.payload) - - try: - request = json.loads(msg.payload) - except TypeError: - _LOGGER.error("Received invalid JSON: %s", msg.payload) - return - - if request["intent"]["confidenceScore"] < config[DOMAIN].get(CONF_PROBABILITY): - _LOGGER.warning( - "Intent below probaility threshold %s < %s", - request["intent"]["confidenceScore"], - config[DOMAIN].get(CONF_PROBABILITY), - ) - return - - try: - request = INTENT_SCHEMA(request) - except vol.Invalid as err: - _LOGGER.error("Intent has invalid schema: %s. %s", err, request) - return - - if request["intent"]["intentName"].startswith("user_"): - intent_type = request["intent"]["intentName"].split("__")[-1] - else: - intent_type = request["intent"]["intentName"].split(":")[-1] - slots = {} - for slot in request.get("slots", []): - slots[slot["slotName"]] = {"value": resolve_slot_values(slot)} - slots[f"{slot['slotName']}_raw"] = {"value": slot["rawValue"]} - slots["site_id"] = {"value": request.get("siteId")} - slots["session_id"] = {"value": request.get("sessionId")} - slots["confidenceScore"] = {"value": request["intent"]["confidenceScore"]} - - try: - intent_response = await intent.async_handle( - hass, DOMAIN, intent_type, slots, request["input"] - ) - notification = {"sessionId": request.get("sessionId", "default")} - - if "plain" in intent_response.speech: - notification["text"] = intent_response.speech["plain"]["speech"] - - _LOGGER.debug("send_response %s", json.dumps(notification)) - await mqtt.async_publish( - hass, "hermes/dialogueManager/endSession", json.dumps(notification) - ) - except intent.UnknownIntent: - _LOGGER.warning( - "Received unknown intent %s", request["intent"]["intentName"] - ) - except intent.IntentError: - _LOGGER.exception("Error while handling intent: %s", intent_type) - - await mqtt.async_subscribe(hass, INTENT_TOPIC, message_received) - - async def snips_say(call: ServiceCall) -> None: - """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)}, - } - await mqtt.async_publish( - hass, "hermes/dialogueManager/startSession", json.dumps(notification) - ) - - async def snips_say_action(call: ServiceCall) -> None: - """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, []), - }, - } - await mqtt.async_publish( - hass, "hermes/dialogueManager/startSession", json.dumps(notification) - ) - - async def feedback_on(call: ServiceCall) -> None: - """Turn feedback sounds on.""" - await async_set_feedback(call.data.get(ATTR_SITE_ID), True) - - async def feedback_off(call: ServiceCall) -> None: - """Turn feedback sounds off.""" - await async_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 - - -def resolve_slot_values(slot): - """Convert snips builtin types to usable values.""" - if "value" in slot["value"]: - value = slot["value"]["value"] - else: - value = slot["rawValue"] - - if slot.get("entity") == "snips/duration": - delta = timedelta( - weeks=slot["value"]["weeks"], - days=slot["value"]["days"], - hours=slot["value"]["hours"], - minutes=slot["value"]["minutes"], - seconds=slot["value"]["seconds"], - ) - value = delta.total_seconds() - - return value diff --git a/homeassistant/components/snips/icons.json b/homeassistant/components/snips/icons.json deleted file mode 100644 index 9c86a7ad5b3..00000000000 --- a/homeassistant/components/snips/icons.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "services": { - "feedback_off": { - "service": "mdi:message-alert" - }, - "feedback_on": { - "service": "mdi:message-alert" - }, - "say": { - "service": "mdi:chat" - }, - "say_action": { - "service": "mdi:account-voice" - } - } -} diff --git a/homeassistant/components/snips/manifest.json b/homeassistant/components/snips/manifest.json deleted file mode 100644 index ec768b2b3d4..00000000000 --- a/homeassistant/components/snips/manifest.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "domain": "snips", - "name": "Snips", - "codeowners": [], - "dependencies": ["mqtt"], - "documentation": "https://www.home-assistant.io/integrations/snips", - "iot_class": "local_push", - "quality_scale": "legacy" -} diff --git a/homeassistant/components/snips/services.yaml b/homeassistant/components/snips/services.yaml deleted file mode 100644 index 522e1b5b348..00000000000 --- a/homeassistant/components/snips/services.yaml +++ /dev/null @@ -1,56 +0,0 @@ -feedback_off: - fields: - site_id: - example: bedroom - default: default - selector: - text: -feedback_on: - fields: - site_id: - example: bedroom - default: default - selector: - text: -say: - fields: - custom_data: - example: user=UserName - default: "" - selector: - text: - site_id: - example: bedroom - default: default - selector: - text: - text: - required: true - example: My name is snips - selector: - text: -say_action: - fields: - can_be_enqueued: - default: true - selector: - boolean: - custom_data: - example: user=UserName - default: "" - selector: - text: - intent_filter: - example: "turnOnLights, turnOffLights" - selector: - object: - site_id: - example: bedroom - default: default - selector: - text: - text: - required: true - example: My name is snips - selector: - text: diff --git a/homeassistant/components/snips/strings.json b/homeassistant/components/snips/strings.json deleted file mode 100644 index bfeee86010f..00000000000 --- a/homeassistant/components/snips/strings.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "services": { - "feedback_off": { - "description": "Turns feedback sounds off.", - "fields": { - "site_id": { - "description": "Site to turn sounds on, defaults to all sites.", - "name": "Site ID" - } - }, - "name": "Feedback off" - }, - "feedback_on": { - "description": "Turns feedback sounds on.", - "fields": { - "site_id": { - "description": "[%key:component::snips::services::feedback_off::fields::site_id::description%]", - "name": "Site ID" - } - }, - "name": "Feedback on" - }, - "say": { - "description": "Sends a TTS message to Snips.", - "fields": { - "custom_data": { - "description": "Custom data that will be included with all messages in this session.", - "name": "Custom data" - }, - "site_id": { - "description": "Site to use to start session, defaults to default.", - "name": "Site ID" - }, - "text": { - "description": "Text to say.", - "name": "Text" - } - }, - "name": "Say" - }, - "say_action": { - "description": "Sends a TTS message to Snips to listen for a response.", - "fields": { - "can_be_enqueued": { - "description": "Whether the session should wait for an open session to end. Otherwise it is dropped if another session is already running.", - "name": "Can be enqueued" - }, - "custom_data": { - "description": "[%key:component::snips::services::say::fields::custom_data::description%]", - "name": "[%key:component::snips::services::say::fields::custom_data::name%]" - }, - "intent_filter": { - "description": "Optional Array of Strings - A list of intents names to restrict the NLU resolution to on the first query.", - "name": "Intent filter" - }, - "site_id": { - "description": "[%key:component::snips::services::say::fields::site_id::description%]", - "name": "Site ID" - }, - "text": { - "description": "[%key:component::snips::services::say::fields::text::description%]", - "name": "Text" - } - }, - "name": "Say action" - } - } -} diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 5408ce660bb..87739075005 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -6228,12 +6228,6 @@ "config_flow": true, "iot_class": "local_push" }, - "snips": { - "name": "Snips", - "integration_type": "hub", - "config_flow": false, - "iot_class": "local_push" - }, "snmp": { "name": "SNMP", "integration_type": "hub", diff --git a/script/hassfest/quality_scale.py b/script/hassfest/quality_scale.py index c0c19766d0f..3790d2d773e 100644 --- a/script/hassfest/quality_scale.py +++ b/script/hassfest/quality_scale.py @@ -902,7 +902,6 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [ "sms", "smtp", "snapcast", - "snips", "snmp", "snooz", "solaredge", @@ -1939,7 +1938,6 @@ INTEGRATIONS_WITHOUT_SCALE = [ "sms", "smtp", "snapcast", - "snips", "snmp", "snooz", "solaredge", diff --git a/tests/components/snips/__init__.py b/tests/components/snips/__init__.py deleted file mode 100644 index d7ac8b5f822..00000000000 --- a/tests/components/snips/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for the snips component.""" diff --git a/tests/components/snips/test_init.py b/tests/components/snips/test_init.py deleted file mode 100644 index 2be6d769f08..00000000000 --- a/tests/components/snips/test_init.py +++ /dev/null @@ -1,559 +0,0 @@ -"""Test the Snips component.""" - -import json -import logging - -import pytest -import voluptuous as vol - -from homeassistant.components import snips -from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant -from homeassistant.helpers import issue_registry as ir -from homeassistant.helpers.intent import ServiceIntentHandler, async_register -from homeassistant.setup import async_setup_component - -from tests.common import async_fire_mqtt_message, async_mock_intent, async_mock_service -from tests.typing import MqttMockHAClient - - -async def test_snips_config( - hass: HomeAssistant, - mqtt_mock: MqttMockHAClient, - issue_registry: ir.IssueRegistry, -) -> None: - """Test Snips Config.""" - assert await async_setup_component( - hass, - "snips", - { - "snips": { - "feedback_sounds": True, - "probability_threshold": 0.5, - "site_ids": ["default", "remote"], - } - }, - ) - assert ( - HOMEASSISTANT_DOMAIN, - f"deprecated_system_packages_yaml_integration_{snips.DOMAIN}", - ) in issue_registry.issues - - -async def test_snips_no_mqtt( - hass: HomeAssistant, caplog: pytest.LogCaptureFixture -) -> None: - """Test Snips Config.""" - result = await async_setup_component( - hass, - "snips", - { - "snips": { - "feedback_sounds": True, - "probability_threshold": 0.5, - "site_ids": ["default", "remote"], - } - }, - ) - assert not result - assert "MQTT integration is not available" in caplog.text - - -async def test_snips_bad_config( - hass: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test Snips bad config.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test Snips Config.""" - result = await async_setup_component( - hass, "snips", {"snips": {"feedback_sounds": True}} - ) - assert result - await hass.async_block_till_done() - - assert mqtt_mock.async_publish.call_count == 2 - topic = mqtt_mock.async_publish.call_args_list[0][0][0] - assert topic == "hermes/feedback/sound/toggleOn" - topic = mqtt_mock.async_publish.call_args_list[1][0][0] - assert topic == "hermes/feedback/sound/toggleOn" - assert mqtt_mock.async_publish.call_args_list[1][0][2] == 1 - assert mqtt_mock.async_publish.call_args_list[1][0][3] - - -async def test_snips_config_feedback_off( - hass: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test Snips Config.""" - result = await async_setup_component( - hass, "snips", {"snips": {"feedback_sounds": False}} - ) - assert result - await hass.async_block_till_done() - - assert mqtt_mock.async_publish.call_count == 2 - topic = mqtt_mock.async_publish.call_args_list[0][0][0] - assert topic == "hermes/feedback/sound/toggleOn" - topic = mqtt_mock.async_publish.call_args_list[1][0][0] - assert topic == "hermes/feedback/sound/toggleOff" - assert mqtt_mock.async_publish.call_args_list[1][0][2] == 0 - assert not mqtt_mock.async_publish.call_args_list[1][0][3] - - -async def test_snips_config_no_feedback( - hass: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test Snips Config.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient) -> None: - """Test intent via Snips.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """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", - "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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test intent with Snips duration.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test intent speech response via Snips.""" - 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 mqtt_mock.async_publish.call_count == 1 - payload = json.loads(mqtt_mock.async_publish.call_args[0][1]) - topic = mqtt_mock.async_publish.call_args[0][0] - assert payload["sessionId"] == "abcdef0123456789" - assert payload["text"] == "I am speaking to you" - assert topic == "hermes/dialogueManager/endSession" - - -async def test_unknown_intent( - hass: HomeAssistant, caplog: pytest.LogCaptureFixture, mqtt_mock: MqttMockHAClient -) -> None: - """Test unknown intent.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test intentName format user_XXX__intentName.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test intentName format username:intentName.""" - 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: HomeAssistant, caplog: pytest.LogCaptureFixture, mqtt_mock: MqttMockHAClient -) -> None: - """Test intent via Snips.""" - 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: HomeAssistant, mqtt_mock: MqttMockHAClient -) -> None: - """Test intent special slot values via Snips.""" - 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: HomeAssistant) -> None: - """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: HomeAssistant) -> None: - """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: HomeAssistant) -> None: - """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: HomeAssistant) -> None: - """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: HomeAssistant) -> None: - """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: HomeAssistant) -> None: - """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: HomeAssistant) -> None: - """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