Report missing entities/areas instead of failing to match in Assist (#107151)
* Report missing entities/areas instead of failing * Fix test * Update assist pipeline test snapshots * Test complete match failure * Fix conflict
This commit is contained in:
parent
1a7b06f66a
commit
269500cb29
11 changed files with 1200 additions and 608 deletions
|
@ -19,7 +19,12 @@ from hassil.intents import (
|
|||
TextSlotList,
|
||||
WildcardSlotList,
|
||||
)
|
||||
from hassil.recognize import RecognizeResult, recognize_all
|
||||
from hassil.recognize import (
|
||||
RecognizeResult,
|
||||
UnmatchedEntity,
|
||||
UnmatchedTextEntity,
|
||||
recognize_all,
|
||||
)
|
||||
from hassil.util import merge_dict
|
||||
from home_assistant_intents import get_domains_and_languages, get_intents
|
||||
import yaml
|
||||
|
@ -213,6 +218,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||
lang_intents = self._lang_intents.get(language)
|
||||
|
||||
if result is None:
|
||||
# Intent was not recognized
|
||||
_LOGGER.debug("No intent was matched for '%s'", user_input.text)
|
||||
return _make_error_result(
|
||||
language,
|
||||
|
@ -221,6 +227,28 @@ class DefaultAgent(AbstractConversationAgent):
|
|||
conversation_id,
|
||||
)
|
||||
|
||||
if result.unmatched_entities:
|
||||
# Intent was recognized, but not entity/area names, etc.
|
||||
_LOGGER.debug(
|
||||
"Recognized intent '%s' for template '%s' but had unmatched: %s",
|
||||
result.intent.name,
|
||||
result.intent_sentence.text
|
||||
if result.intent_sentence is not None
|
||||
else "",
|
||||
result.unmatched_entities_list,
|
||||
)
|
||||
error_response_type, error_response_args = _get_unmatched_response(
|
||||
result.unmatched_entities
|
||||
)
|
||||
return _make_error_result(
|
||||
language,
|
||||
intent.IntentResponseErrorCode.NO_VALID_TARGETS,
|
||||
self._get_error_text(
|
||||
error_response_type, lang_intents, **error_response_args
|
||||
),
|
||||
conversation_id,
|
||||
)
|
||||
|
||||
# Will never happen because result will be None when no intents are
|
||||
# loaded in async_recognize.
|
||||
assert lang_intents is not None
|
||||
|
@ -302,7 +330,35 @@ class DefaultAgent(AbstractConversationAgent):
|
|||
# Keep looking in case an entity has the same name
|
||||
maybe_result = result
|
||||
|
||||
return maybe_result
|
||||
if maybe_result is not None:
|
||||
# Successful strict match
|
||||
return maybe_result
|
||||
|
||||
# Try again with missing entities enabled
|
||||
for result in recognize_all(
|
||||
user_input.text,
|
||||
lang_intents.intents,
|
||||
slot_lists=slot_lists,
|
||||
intent_context=intent_context,
|
||||
allow_unmatched_entities=True,
|
||||
):
|
||||
if maybe_result is None:
|
||||
# First result
|
||||
maybe_result = result
|
||||
elif len(result.unmatched_entities) < len(maybe_result.unmatched_entities):
|
||||
# Fewer unmatched entities
|
||||
maybe_result = result
|
||||
elif len(result.unmatched_entities) == len(maybe_result.unmatched_entities):
|
||||
if result.text_chunks_matched > maybe_result.text_chunks_matched:
|
||||
# More literal text chunks matched
|
||||
maybe_result = result
|
||||
|
||||
if (maybe_result is not None) and maybe_result.unmatched_entities:
|
||||
# Failed to match, but we have more information about why in unmatched_entities
|
||||
return maybe_result
|
||||
|
||||
# Complete match failure
|
||||
return None
|
||||
|
||||
async def _build_speech(
|
||||
self,
|
||||
|
@ -655,15 +711,22 @@ class DefaultAgent(AbstractConversationAgent):
|
|||
return {"area": device_area.id}
|
||||
|
||||
def _get_error_text(
|
||||
self, response_type: ResponseType, lang_intents: LanguageIntents | None
|
||||
self,
|
||||
response_type: ResponseType,
|
||||
lang_intents: LanguageIntents | None,
|
||||
**response_args,
|
||||
) -> str:
|
||||
"""Get response error text by type."""
|
||||
if lang_intents is None:
|
||||
return _DEFAULT_ERROR_TEXT
|
||||
|
||||
response_key = response_type.value
|
||||
response_str = lang_intents.error_responses.get(response_key)
|
||||
return response_str or _DEFAULT_ERROR_TEXT
|
||||
response_str = (
|
||||
lang_intents.error_responses.get(response_key) or _DEFAULT_ERROR_TEXT
|
||||
)
|
||||
response_template = template.Template(response_str, self.hass)
|
||||
|
||||
return response_template.async_render(response_args)
|
||||
|
||||
def register_trigger(
|
||||
self,
|
||||
|
@ -783,6 +846,27 @@ def _make_error_result(
|
|||
return ConversationResult(response, conversation_id)
|
||||
|
||||
|
||||
def _get_unmatched_response(
|
||||
unmatched_entities: dict[str, UnmatchedEntity],
|
||||
) -> tuple[ResponseType, dict[str, Any]]:
|
||||
error_response_type = ResponseType.NO_INTENT
|
||||
error_response_args: dict[str, Any] = {}
|
||||
|
||||
if unmatched_name := unmatched_entities.get("name"):
|
||||
# Unmatched device or entity
|
||||
assert isinstance(unmatched_name, UnmatchedTextEntity)
|
||||
error_response_type = ResponseType.NO_ENTITY
|
||||
error_response_args["entity"] = unmatched_name.text
|
||||
|
||||
elif unmatched_area := unmatched_entities.get("area"):
|
||||
# Unmatched area
|
||||
assert isinstance(unmatched_area, UnmatchedTextEntity)
|
||||
error_response_type = ResponseType.NO_AREA
|
||||
error_response_args["area"] = unmatched_area.text
|
||||
|
||||
return error_response_type, error_response_args
|
||||
|
||||
|
||||
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
|
||||
"""Collect list reference names recursively."""
|
||||
if isinstance(expression, Sequence):
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
"integration_type": "system",
|
||||
"iot_class": "local_push",
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["hassil==1.5.1", "home-assistant-intents==2024.1.2"]
|
||||
"requirements": ["hassil==1.5.2", "home-assistant-intents==2024.1.2"]
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ ha-av==10.1.1
|
|||
ha-ffmpeg==3.1.0
|
||||
habluetooth==2.0.2
|
||||
hass-nabucasa==0.75.1
|
||||
hassil==1.5.1
|
||||
hassil==1.5.2
|
||||
home-assistant-bluetooth==1.11.0
|
||||
home-assistant-frontend==20240104.0
|
||||
home-assistant-intents==2024.1.2
|
||||
|
|
|
@ -1007,7 +1007,7 @@ hass-nabucasa==0.75.1
|
|||
hass-splunk==0.1.1
|
||||
|
||||
# homeassistant.components.conversation
|
||||
hassil==1.5.1
|
||||
hassil==1.5.2
|
||||
|
||||
# homeassistant.components.jewish_calendar
|
||||
hdate==0.10.4
|
||||
|
|
|
@ -809,7 +809,7 @@ habluetooth==2.0.2
|
|||
hass-nabucasa==0.75.1
|
||||
|
||||
# homeassistant.components.conversation
|
||||
hassil==1.5.1
|
||||
hassil==1.5.2
|
||||
|
||||
# homeassistant.components.jewish_calendar
|
||||
hdate==0.10.4
|
||||
|
|
|
@ -48,14 +48,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -67,7 +67,7 @@
|
|||
'data': dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'james_earl_jones',
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_START: 'tts-start'>,
|
||||
|
@ -75,9 +75,9 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3',
|
||||
}),
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_END: 'tts-end'>,
|
||||
|
@ -137,14 +137,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en-US',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -156,7 +156,7 @@
|
|||
'data': dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'Arnold Schwarzenegger',
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_START: 'tts-start'>,
|
||||
|
@ -164,9 +164,9 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=Arnold+Schwarzenegger",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=Arnold+Schwarzenegger',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_2657c1a8ee_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_2657c1a8ee_test.mp3',
|
||||
}),
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_END: 'tts-end'>,
|
||||
|
@ -226,14 +226,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en-US',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -245,7 +245,7 @@
|
|||
'data': dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'Arnold Schwarzenegger',
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_START: 'tts-start'>,
|
||||
|
@ -253,9 +253,9 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=Arnold+Schwarzenegger",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=Arnold+Schwarzenegger',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_2657c1a8ee_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_2657c1a8ee_test.mp3',
|
||||
}),
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_END: 'tts-end'>,
|
||||
|
@ -338,14 +338,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -357,7 +357,7 @@
|
|||
'data': dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'james_earl_jones',
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_START: 'tts-start'>,
|
||||
|
@ -365,9 +365,9 @@
|
|||
dict({
|
||||
'data': dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3',
|
||||
}),
|
||||
}),
|
||||
'type': <PipelineEventType.TTS_END: 'tts-end'>,
|
||||
|
|
|
@ -46,14 +46,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -64,16 +64,16 @@
|
|||
dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'james_earl_jones',
|
||||
})
|
||||
# ---
|
||||
# name: test_audio_pipeline.6
|
||||
dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
@ -127,14 +127,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -145,16 +145,16 @@
|
|||
dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'james_earl_jones',
|
||||
})
|
||||
# ---
|
||||
# name: test_audio_pipeline_debug.6
|
||||
dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
@ -220,14 +220,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -238,16 +238,16 @@
|
|||
dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'james_earl_jones',
|
||||
})
|
||||
# ---
|
||||
# name: test_audio_pipeline_with_enhancements.6
|
||||
dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
@ -421,14 +421,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No device or entity named test transcript',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -439,16 +439,16 @@
|
|||
dict({
|
||||
'engine': 'test',
|
||||
'language': 'en-US',
|
||||
'tts_input': "Sorry, I couldn't understand that",
|
||||
'tts_input': 'No device or entity named test transcript',
|
||||
'voice': 'james_earl_jones',
|
||||
})
|
||||
# ---
|
||||
# name: test_audio_pipeline_with_wake_word_no_timeout.8
|
||||
dict({
|
||||
'tts_output': dict({
|
||||
'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
|
||||
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones',
|
||||
'mime_type': 'audio/mpeg',
|
||||
'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
|
||||
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
|
@ -771,14 +771,14 @@
|
|||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
'speech': 'No area named are',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
|
|
@ -1,4 +1,104 @@
|
|||
# serializer version: 1
|
||||
# name: test_custom_agent
|
||||
dict({
|
||||
'conversation_id': 'test-conv-id',
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'test-language',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Test response',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_custom_sentences
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en-us',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'You ordered a stout',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_custom_sentences.1
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en-us',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'You ordered a lager',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_custom_sentences_config
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Stealth mode engaged',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_get_agent_info
|
||||
dict({
|
||||
'id': 'homeassistant',
|
||||
|
@ -225,6 +325,686 @@
|
|||
]),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_api_handle_failure
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'failed_to_handle',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'An unexpected error occurred while handling the intent',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_api_no_match
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No device or entity named do something',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_api_unexpected_failure
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'unknown',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'An unexpected error occurred while handling the intent',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent[None]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent[homeassistant]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_alias_added_removed
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_alias_added_removed.1
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_alias_added_removed.2
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No device or entity named late added alias',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_conversion_not_expose_new
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No area named kitchen',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_conversion_not_expose_new.1
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_added_removed
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_added_removed.1
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.late',
|
||||
'name': 'friendly light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_added_removed.2
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.late',
|
||||
'name': 'friendly light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_added_removed.3
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No area named late added',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_exposed
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_exposed.1
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_exposed.2
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No area named kitchen',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_exposed.3
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No area named my cool',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_exposed.4
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_exposed.5
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_renamed
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_renamed.1
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'renamed light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_renamed.2
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No area named kitchen',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_renamed.3
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen light',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_entity_renamed.4
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No area named renamed',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_http_processing_intent_target_ha_agent
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'failed': list([
|
||||
]),
|
||||
'success': list([
|
||||
dict({
|
||||
'id': 'light.kitchen',
|
||||
'name': 'kitchen',
|
||||
'type': 'entity',
|
||||
}),
|
||||
]),
|
||||
'targets': list([
|
||||
]),
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'action_done',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_turn_on_intent[None-turn kitchen on-None]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
|
@ -339,7 +1119,7 @@
|
|||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on light',
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -369,7 +1149,7 @@
|
|||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on light',
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -399,7 +1179,7 @@
|
|||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on light',
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -429,7 +1209,7 @@
|
|||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'Turned on light',
|
||||
'speech': 'Turned on the light',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
@ -465,6 +1245,126 @@
|
|||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_api[payload0]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No device or entity named test text',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_api[payload1]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
}),
|
||||
'language': 'test-language',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_api[payload2]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No device or entity named test text',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_api[payload3]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No device or entity named test text',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_api[payload4]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_intent_match',
|
||||
}),
|
||||
'language': 'test-language',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': "Sorry, I couldn't understand that",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_api[payload5]
|
||||
dict({
|
||||
'conversation_id': None,
|
||||
'response': dict({
|
||||
'card': dict({
|
||||
}),
|
||||
'data': dict({
|
||||
'code': 'no_valid_targets',
|
||||
}),
|
||||
'language': 'en',
|
||||
'response_type': 'error',
|
||||
'speech': dict({
|
||||
'plain': dict({
|
||||
'extra_data': None,
|
||||
'speech': 'No device or entity named test text',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_ws_get_agent_info
|
||||
dict({
|
||||
'attribution': None,
|
||||
|
@ -590,7 +1490,23 @@
|
|||
}),
|
||||
}),
|
||||
}),
|
||||
None,
|
||||
dict({
|
||||
'details': dict({
|
||||
'domain': dict({
|
||||
'name': 'domain',
|
||||
'text': '',
|
||||
'value': 'script',
|
||||
}),
|
||||
}),
|
||||
'intent': dict({
|
||||
'name': 'HassTurnOn',
|
||||
}),
|
||||
'slots': dict({
|
||||
'domain': 'script',
|
||||
}),
|
||||
'targets': dict({
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
# ---
|
||||
|
|
|
@ -57,7 +57,7 @@ async def test_hidden_entities_skipped(
|
|||
|
||||
assert len(calls) == 0
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
|
||||
|
||||
async def test_exposed_domains(hass: HomeAssistant, init_components) -> None:
|
||||
|
@ -70,10 +70,10 @@ async def test_exposed_domains(hass: HomeAssistant, init_components) -> None:
|
|||
hass, "turn on test media player", None, Context(), None
|
||||
)
|
||||
|
||||
# This is an intent match failure instead of a handle failure because the
|
||||
# media player domain is not exposed.
|
||||
# This is a match failure instead of a handle failure because the media
|
||||
# player domain is not exposed.
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
|
||||
|
||||
async def test_exposed_areas(
|
||||
|
@ -127,9 +127,9 @@ async def test_exposed_areas(
|
|||
hass, "turn on lights in the bedroom", None, Context(), None
|
||||
)
|
||||
|
||||
# This should be an intent match failure because the area isn't in the slot list
|
||||
# This should be a match failure because the area isn't in the slot list
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
|
||||
|
||||
async def test_conversation_agent(
|
||||
|
@ -417,6 +417,48 @@ async def test_device_area_context(
|
|||
result = await conversation.async_converse(
|
||||
hass, f"turn {command} all lights", None, Context(), None
|
||||
)
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert (
|
||||
result.response.error_code
|
||||
== intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
)
|
||||
|
||||
|
||||
async def test_error_missing_entity(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test error message when entity is missing."""
|
||||
result = await conversation.async_converse(
|
||||
hass, "turn on missing entity", None, Context(), None
|
||||
)
|
||||
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
assert (
|
||||
result.response.speech["plain"]["speech"]
|
||||
== "No device or entity named missing entity"
|
||||
)
|
||||
|
||||
|
||||
async def test_error_missing_area(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test error message when area is missing."""
|
||||
result = await conversation.async_converse(
|
||||
hass, "turn on the lights in missing area", None, Context(), None
|
||||
)
|
||||
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
assert result.response.speech["plain"]["speech"] == "No area named missing area"
|
||||
|
||||
|
||||
async def test_error_match_failure(hass: HomeAssistant, init_components) -> None:
|
||||
"""Test response with complete match failure."""
|
||||
with patch(
|
||||
"homeassistant.components.conversation.default_agent.recognize_all",
|
||||
return_value=[],
|
||||
):
|
||||
result = await conversation.async_converse(
|
||||
hass, "do something", None, Context(), None
|
||||
)
|
||||
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert (
|
||||
result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
||||
|
|
|
@ -58,6 +58,7 @@ async def test_http_processing_intent(
|
|||
hass_admin_user: MockUser,
|
||||
agent_id,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent via HTTP API."""
|
||||
# Add an alias
|
||||
|
@ -78,27 +79,7 @@ async def test_http_processing_intent(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
|
||||
|
||||
async def test_http_processing_intent_target_ha_agent(
|
||||
|
@ -108,6 +89,7 @@ async def test_http_processing_intent_target_ha_agent(
|
|||
hass_admin_user: MockUser,
|
||||
mock_agent,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent can be processed via HTTP API with picking agent."""
|
||||
# Add an alias
|
||||
|
@ -127,28 +109,8 @@ async def test_http_processing_intent_target_ha_agent(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
|
||||
async def test_http_processing_intent_entity_added_removed(
|
||||
|
@ -157,6 +119,7 @@ async def test_http_processing_intent_entity_added_removed(
|
|||
hass_client: ClientSessionGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent via HTTP API with entities added later.
|
||||
|
||||
|
@ -179,27 +142,8 @@ async def test_http_processing_intent_entity_added_removed(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Add an entity
|
||||
entity_registry.async_get_or_create(
|
||||
|
@ -215,27 +159,8 @@ async def test_http_processing_intent_entity_added_removed(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.late", "name": "friendly light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Now add an alias
|
||||
entity_registry.async_update_entity("light.late", aliases={"late added light"})
|
||||
|
@ -248,27 +173,8 @@ async def test_http_processing_intent_entity_added_removed(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.late", "name": "friendly light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Now delete the entity
|
||||
hass.states.async_remove("light.late")
|
||||
|
@ -280,21 +186,8 @@ async def test_http_processing_intent_entity_added_removed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
|
||||
async def test_http_processing_intent_alias_added_removed(
|
||||
|
@ -303,6 +196,7 @@ async def test_http_processing_intent_alias_added_removed(
|
|||
hass_client: ClientSessionGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
entity_registry: er.EntityRegistry,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent via HTTP API with aliases added later.
|
||||
|
||||
|
@ -324,27 +218,8 @@ async def test_http_processing_intent_alias_added_removed(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Add an alias
|
||||
entity_registry.async_update_entity("light.kitchen", aliases={"late added alias"})
|
||||
|
@ -357,27 +232,8 @@ async def test_http_processing_intent_alias_added_removed(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Now remove the alieas
|
||||
entity_registry.async_update_entity("light.kitchen", aliases={})
|
||||
|
@ -389,21 +245,8 @@ async def test_http_processing_intent_alias_added_removed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
|
||||
async def test_http_processing_intent_entity_renamed(
|
||||
|
@ -413,6 +256,7 @@ async def test_http_processing_intent_entity_renamed(
|
|||
hass_admin_user: MockUser,
|
||||
entity_registry: er.EntityRegistry,
|
||||
enable_custom_integrations: None,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent via HTTP API with entities renamed later.
|
||||
|
||||
|
@ -442,27 +286,8 @@ async def test_http_processing_intent_entity_renamed(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Rename the entity
|
||||
entity_registry.async_update_entity("light.kitchen", name="renamed light")
|
||||
|
@ -476,27 +301,8 @@ async def test_http_processing_intent_entity_renamed(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "renamed light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
client = await hass_client()
|
||||
resp = await client.post(
|
||||
|
@ -505,21 +311,8 @@ async def test_http_processing_intent_entity_renamed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
# Now clear the custom name
|
||||
entity_registry.async_update_entity("light.kitchen", name=None)
|
||||
|
@ -533,27 +326,8 @@ async def test_http_processing_intent_entity_renamed(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
client = await hass_client()
|
||||
resp = await client.post(
|
||||
|
@ -562,21 +336,8 @@ async def test_http_processing_intent_entity_renamed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
|
||||
async def test_http_processing_intent_entity_exposed(
|
||||
|
@ -586,6 +347,7 @@ async def test_http_processing_intent_entity_exposed(
|
|||
hass_admin_user: MockUser,
|
||||
entity_registry: er.EntityRegistry,
|
||||
enable_custom_integrations: None,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent via HTTP API with manual expose.
|
||||
|
||||
|
@ -617,27 +379,8 @@ async def test_http_processing_intent_entity_exposed(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
calls = async_mock_service(hass, LIGHT_DOMAIN, "turn_on")
|
||||
client = await hass_client()
|
||||
|
@ -649,27 +392,8 @@ async def test_http_processing_intent_entity_exposed(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
# Unexpose the entity
|
||||
expose_entity(hass, "light.kitchen", False)
|
||||
|
@ -682,21 +406,8 @@ async def test_http_processing_intent_entity_exposed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
client = await hass_client()
|
||||
resp = await client.post(
|
||||
|
@ -705,21 +416,8 @@ async def test_http_processing_intent_entity_exposed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
# Now expose the entity
|
||||
expose_entity(hass, "light.kitchen", True)
|
||||
|
@ -733,27 +431,8 @@ async def test_http_processing_intent_entity_exposed(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
client = await hass_client()
|
||||
resp = await client.post(
|
||||
|
@ -762,27 +441,8 @@ async def test_http_processing_intent_entity_exposed(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
|
||||
async def test_http_processing_intent_conversion_not_expose_new(
|
||||
|
@ -792,6 +452,7 @@ async def test_http_processing_intent_conversion_not_expose_new(
|
|||
hass_admin_user: MockUser,
|
||||
entity_registry: er.EntityRegistry,
|
||||
enable_custom_integrations: None,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test processing intent via HTTP API when not exposing new entities."""
|
||||
# Disable exposing new entities to the default agent
|
||||
|
@ -820,21 +481,8 @@ async def test_http_processing_intent_conversion_not_expose_new(
|
|||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
assert data == {
|
||||
"conversation_id": None,
|
||||
"response": {
|
||||
"card": {},
|
||||
"data": {"code": "no_intent_match"},
|
||||
"language": hass.config.language,
|
||||
"response_type": "error",
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
# Expose the entity
|
||||
expose_entity(hass, "light.kitchen", True)
|
||||
|
@ -848,27 +496,8 @@ async def test_http_processing_intent_conversion_not_expose_new(
|
|||
assert len(calls) == 1
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Turned on the light",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [
|
||||
{"id": "light.kitchen", "name": "kitchen light", "type": "entity"}
|
||||
],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS)
|
||||
|
@ -936,7 +565,10 @@ async def test_turn_off_intent(hass: HomeAssistant, init_components, sentence) -
|
|||
|
||||
|
||||
async def test_http_api_no_match(
|
||||
hass: HomeAssistant, init_components, hass_client: ClientSessionGenerator
|
||||
hass: HomeAssistant,
|
||||
init_components,
|
||||
hass_client: ClientSessionGenerator,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the HTTP conversation API with an intent match failure."""
|
||||
client = await hass_client()
|
||||
|
@ -947,25 +579,15 @@ async def test_http_api_no_match(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "error",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
"extra_data": None,
|
||||
},
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {"code": "no_intent_match"},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
|
||||
|
||||
async def test_http_api_handle_failure(
|
||||
hass: HomeAssistant, init_components, hass_client: ClientSessionGenerator
|
||||
hass: HomeAssistant,
|
||||
init_components,
|
||||
hass_client: ClientSessionGenerator,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the HTTP conversation API with an error during handling."""
|
||||
client = await hass_client()
|
||||
|
@ -984,29 +606,16 @@ async def test_http_api_handle_failure(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "error",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "An unexpected error occurred while handling the intent",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"code": "failed_to_handle",
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
assert data["response"]["data"]["code"] == "failed_to_handle"
|
||||
|
||||
|
||||
async def test_http_api_unexpected_failure(
|
||||
hass: HomeAssistant,
|
||||
init_components,
|
||||
hass_client: ClientSessionGenerator,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the HTTP conversation API with an unexpected error during handling."""
|
||||
client = await hass_client()
|
||||
|
@ -1025,23 +634,9 @@ async def test_http_api_unexpected_failure(
|
|||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"response_type": "error",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "An unexpected error occurred while handling the intent",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"data": {
|
||||
"code": "unknown",
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "error"
|
||||
assert data["response"]["data"]["code"] == "unknown"
|
||||
|
||||
|
||||
async def test_http_api_wrong_data(
|
||||
|
@ -1062,6 +657,7 @@ async def test_custom_agent(
|
|||
hass_client: ClientSessionGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
mock_agent,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test a custom conversation agent."""
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
|
@ -1079,21 +675,11 @@ async def test_custom_agent(
|
|||
|
||||
resp = await client.post("/api/conversation/process", json=data)
|
||||
assert resp.status == HTTPStatus.OK
|
||||
assert await resp.json() == {
|
||||
"response": {
|
||||
"response_type": "action_done",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Test response",
|
||||
}
|
||||
},
|
||||
"language": "test-language",
|
||||
"data": {"targets": [], "success": [], "failed": []},
|
||||
},
|
||||
"conversation_id": "test-conv-id",
|
||||
}
|
||||
data = await resp.json()
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
assert data["response"]["speech"]["plain"]["speech"] == "Test response"
|
||||
assert data["conversation_id"] == "test-conv-id"
|
||||
|
||||
assert len(mock_agent.calls) == 1
|
||||
assert mock_agent.calls[0].text == "Test Text"
|
||||
|
@ -1136,7 +722,10 @@ async def test_custom_agent(
|
|||
],
|
||||
)
|
||||
async def test_ws_api(
|
||||
hass: HomeAssistant, hass_ws_client: WebSocketGenerator, payload
|
||||
hass: HomeAssistant,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
payload,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test the Websocket conversation API."""
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
|
@ -1148,21 +737,7 @@ async def test_ws_api(
|
|||
msg = await client.receive_json()
|
||||
|
||||
assert msg["success"]
|
||||
assert msg["result"] == {
|
||||
"response": {
|
||||
"response_type": "error",
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Sorry, I couldn't understand that",
|
||||
}
|
||||
},
|
||||
"language": payload.get("language", hass.config.language),
|
||||
"data": {"code": "no_intent_match"},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert msg["result"] == snapshot
|
||||
|
||||
|
||||
@pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS)
|
||||
|
@ -1198,7 +773,10 @@ async def test_ws_prepare(
|
|||
|
||||
|
||||
async def test_custom_sentences(
|
||||
hass: HomeAssistant, hass_client: ClientSessionGenerator, hass_admin_user: MockUser
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test custom sentences with a custom intent."""
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
|
@ -1223,30 +801,19 @@ async def test_custom_sentences(
|
|||
)
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": f"You ordered a {beer_style}",
|
||||
}
|
||||
},
|
||||
"language": language,
|
||||
"response_type": "action_done",
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
assert (
|
||||
data["response"]["speech"]["plain"]["speech"]
|
||||
== f"You ordered a {beer_style}"
|
||||
)
|
||||
|
||||
|
||||
async def test_custom_sentences_config(
|
||||
hass: HomeAssistant, hass_client: ClientSessionGenerator, hass_admin_user: MockUser
|
||||
hass: HomeAssistant,
|
||||
hass_client: ClientSessionGenerator,
|
||||
hass_admin_user: MockUser,
|
||||
snapshot: SnapshotAssertion,
|
||||
) -> None:
|
||||
"""Test custom sentences with a custom intent in config."""
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
|
@ -1274,26 +841,9 @@ async def test_custom_sentences_config(
|
|||
)
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
assert data == {
|
||||
"response": {
|
||||
"card": {},
|
||||
"speech": {
|
||||
"plain": {
|
||||
"extra_data": None,
|
||||
"speech": "Stealth mode engaged",
|
||||
}
|
||||
},
|
||||
"language": hass.config.language,
|
||||
"response_type": "action_done",
|
||||
"data": {
|
||||
"targets": [],
|
||||
"success": [],
|
||||
"failed": [],
|
||||
},
|
||||
},
|
||||
"conversation_id": None,
|
||||
}
|
||||
assert data == snapshot
|
||||
assert data["response"]["response_type"] == "action_done"
|
||||
assert data["response"]["speech"]["plain"]["speech"] == "Stealth mode engaged"
|
||||
|
||||
|
||||
async def test_prepare_reload(hass: HomeAssistant) -> None:
|
||||
|
|
|
@ -192,7 +192,7 @@ async def test_cant_turn_on_lock(hass: HomeAssistant) -> None:
|
|||
)
|
||||
|
||||
assert result.response.response_type == intent.IntentResponseType.ERROR
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||
|
||||
|
||||
def test_async_register(hass: HomeAssistant) -> None:
|
||||
|
|
Loading…
Add table
Reference in a new issue