mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 02:07:09 +00:00
Migrate to new intent error response keys (#109269)
This commit is contained in:
parent
c2525d53dd
commit
a1eaa5cbf2
@ -12,22 +12,15 @@ import re
|
|||||||
from typing import IO, Any
|
from typing import IO, Any
|
||||||
|
|
||||||
from hassil.expression import Expression, ListReference, Sequence
|
from hassil.expression import Expression, ListReference, Sequence
|
||||||
from hassil.intents import (
|
from hassil.intents import Intents, SlotList, TextSlotList, WildcardSlotList
|
||||||
Intents,
|
|
||||||
ResponseType,
|
|
||||||
SlotList,
|
|
||||||
TextSlotList,
|
|
||||||
WildcardSlotList,
|
|
||||||
)
|
|
||||||
from hassil.recognize import (
|
from hassil.recognize import (
|
||||||
MISSING_ENTITY,
|
MISSING_ENTITY,
|
||||||
RecognizeResult,
|
RecognizeResult,
|
||||||
UnmatchedEntity,
|
|
||||||
UnmatchedTextEntity,
|
UnmatchedTextEntity,
|
||||||
recognize_all,
|
recognize_all,
|
||||||
)
|
)
|
||||||
from hassil.util import merge_dict
|
from hassil.util import merge_dict
|
||||||
from home_assistant_intents import get_intents, get_languages
|
from home_assistant_intents import ErrorKey, get_intents, get_languages
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from homeassistant import core, setup
|
from homeassistant import core, setup
|
||||||
@ -262,7 +255,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||||||
return _make_error_result(
|
return _make_error_result(
|
||||||
language,
|
language,
|
||||||
intent.IntentResponseErrorCode.NO_INTENT_MATCH,
|
intent.IntentResponseErrorCode.NO_INTENT_MATCH,
|
||||||
self._get_error_text(ResponseType.NO_INTENT, lang_intents),
|
self._get_error_text(ErrorKey.NO_INTENT, lang_intents),
|
||||||
conversation_id,
|
conversation_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -276,9 +269,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||||||
else "",
|
else "",
|
||||||
result.unmatched_entities_list,
|
result.unmatched_entities_list,
|
||||||
)
|
)
|
||||||
error_response_type, error_response_args = _get_unmatched_response(
|
error_response_type, error_response_args = _get_unmatched_response(result)
|
||||||
result.unmatched_entities
|
|
||||||
)
|
|
||||||
return _make_error_result(
|
return _make_error_result(
|
||||||
language,
|
language,
|
||||||
intent.IntentResponseErrorCode.NO_VALID_TARGETS,
|
intent.IntentResponseErrorCode.NO_VALID_TARGETS,
|
||||||
@ -328,7 +319,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||||||
return _make_error_result(
|
return _make_error_result(
|
||||||
language,
|
language,
|
||||||
intent.IntentResponseErrorCode.FAILED_TO_HANDLE,
|
intent.IntentResponseErrorCode.FAILED_TO_HANDLE,
|
||||||
self._get_error_text(ResponseType.HANDLE_ERROR, lang_intents),
|
self._get_error_text(ErrorKey.HANDLE_ERROR, lang_intents),
|
||||||
conversation_id,
|
conversation_id,
|
||||||
)
|
)
|
||||||
except intent.IntentUnexpectedError:
|
except intent.IntentUnexpectedError:
|
||||||
@ -336,7 +327,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||||||
return _make_error_result(
|
return _make_error_result(
|
||||||
language,
|
language,
|
||||||
intent.IntentResponseErrorCode.UNKNOWN,
|
intent.IntentResponseErrorCode.UNKNOWN,
|
||||||
self._get_error_text(ResponseType.HANDLE_ERROR, lang_intents),
|
self._get_error_text(ErrorKey.HANDLE_ERROR, lang_intents),
|
||||||
conversation_id,
|
conversation_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -798,7 +789,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||||||
|
|
||||||
def _get_error_text(
|
def _get_error_text(
|
||||||
self,
|
self,
|
||||||
response_type: ResponseType,
|
error_key: ErrorKey,
|
||||||
lang_intents: LanguageIntents | None,
|
lang_intents: LanguageIntents | None,
|
||||||
**response_args,
|
**response_args,
|
||||||
) -> str:
|
) -> str:
|
||||||
@ -806,7 +797,7 @@ class DefaultAgent(AbstractConversationAgent):
|
|||||||
if lang_intents is None:
|
if lang_intents is None:
|
||||||
return _DEFAULT_ERROR_TEXT
|
return _DEFAULT_ERROR_TEXT
|
||||||
|
|
||||||
response_key = response_type.value
|
response_key = error_key.value
|
||||||
response_str = (
|
response_str = (
|
||||||
lang_intents.error_responses.get(response_key) or _DEFAULT_ERROR_TEXT
|
lang_intents.error_responses.get(response_key) or _DEFAULT_ERROR_TEXT
|
||||||
)
|
)
|
||||||
@ -919,59 +910,72 @@ def _make_error_result(
|
|||||||
return ConversationResult(response, conversation_id)
|
return ConversationResult(response, conversation_id)
|
||||||
|
|
||||||
|
|
||||||
def _get_unmatched_response(
|
def _get_unmatched_response(result: RecognizeResult) -> tuple[ErrorKey, dict[str, Any]]:
|
||||||
unmatched_entities: dict[str, UnmatchedEntity],
|
"""Get key and template arguments for error when there are unmatched intent entities/slots."""
|
||||||
) -> tuple[ResponseType, dict[str, Any]]:
|
|
||||||
error_response_type = ResponseType.NO_INTENT
|
|
||||||
error_response_args: dict[str, Any] = {}
|
|
||||||
|
|
||||||
if unmatched_name := unmatched_entities.get("name"):
|
# Filter out non-text and missing context entities
|
||||||
# Unmatched device or entity
|
unmatched_text: dict[str, str] = {
|
||||||
assert isinstance(unmatched_name, UnmatchedTextEntity)
|
key: entity.text.strip()
|
||||||
error_response_type = ResponseType.NO_ENTITY
|
for key, entity in result.unmatched_entities.items()
|
||||||
error_response_args["entity"] = unmatched_name.text
|
if isinstance(entity, UnmatchedTextEntity) and entity.text != MISSING_ENTITY
|
||||||
|
}
|
||||||
|
|
||||||
elif unmatched_area := unmatched_entities.get("area"):
|
if unmatched_area := unmatched_text.get("area"):
|
||||||
# Unmatched area
|
# area only
|
||||||
assert isinstance(unmatched_area, UnmatchedTextEntity)
|
return ErrorKey.NO_AREA, {"area": unmatched_area}
|
||||||
error_response_type = ResponseType.NO_AREA
|
|
||||||
error_response_args["area"] = unmatched_area.text
|
|
||||||
|
|
||||||
return error_response_type, error_response_args
|
# Area may still have matched
|
||||||
|
matched_area: str | None = None
|
||||||
|
if matched_area_entity := result.entities.get("area"):
|
||||||
|
matched_area = matched_area_entity.text.strip()
|
||||||
|
|
||||||
|
if unmatched_name := unmatched_text.get("name"):
|
||||||
|
if matched_area:
|
||||||
|
# device in area
|
||||||
|
return ErrorKey.NO_ENTITY_IN_AREA, {
|
||||||
|
"entity": unmatched_name,
|
||||||
|
"area": matched_area,
|
||||||
|
}
|
||||||
|
|
||||||
|
# device only
|
||||||
|
return ErrorKey.NO_ENTITY, {"entity": unmatched_name}
|
||||||
|
|
||||||
|
# Default error
|
||||||
|
return ErrorKey.NO_INTENT, {}
|
||||||
|
|
||||||
|
|
||||||
def _get_no_states_matched_response(
|
def _get_no_states_matched_response(
|
||||||
no_states_error: intent.NoStatesMatchedError,
|
no_states_error: intent.NoStatesMatchedError,
|
||||||
) -> tuple[ResponseType, dict[str, Any]]:
|
) -> tuple[ErrorKey, dict[str, Any]]:
|
||||||
"""Return error response type and template arguments for error."""
|
"""Return key and template arguments for error when intent returns no matching states."""
|
||||||
if not (
|
|
||||||
no_states_error.area
|
|
||||||
and (no_states_error.device_classes or no_states_error.domains)
|
|
||||||
):
|
|
||||||
# Device class and domain must be paired with an area for the error
|
|
||||||
# message.
|
|
||||||
return ResponseType.NO_INTENT, {}
|
|
||||||
|
|
||||||
error_response_args: dict[str, Any] = {"area": no_states_error.area}
|
# Device classes should be checked before domains
|
||||||
|
|
||||||
# Check device classes first, since it's more specific than domain
|
|
||||||
if no_states_error.device_classes:
|
if no_states_error.device_classes:
|
||||||
# No exposed entities of a particular class in an area.
|
device_class = next(iter(no_states_error.device_classes)) # first device class
|
||||||
# Example: "close the bedroom windows"
|
if no_states_error.area:
|
||||||
#
|
# device_class in area
|
||||||
# Only use the first device class for the error message
|
return ErrorKey.NO_DEVICE_CLASS_IN_AREA, {
|
||||||
error_response_args["device_class"] = next(iter(no_states_error.device_classes))
|
"device_class": device_class,
|
||||||
|
"area": no_states_error.area,
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseType.NO_DEVICE_CLASS, error_response_args
|
# device_class only
|
||||||
|
return ErrorKey.NO_DEVICE_CLASS, {"device_class": device_class}
|
||||||
|
|
||||||
# No exposed entities of a domain in an area.
|
if no_states_error.domains:
|
||||||
# Example: "turn on lights in kitchen"
|
domain = next(iter(no_states_error.domains)) # first domain
|
||||||
assert no_states_error.domains
|
if no_states_error.area:
|
||||||
#
|
# domain in area
|
||||||
# Only use the first domain for the error message
|
return ErrorKey.NO_DOMAIN_IN_AREA, {
|
||||||
error_response_args["domain"] = next(iter(no_states_error.domains))
|
"domain": domain,
|
||||||
|
"area": no_states_error.area,
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseType.NO_DOMAIN, error_response_args
|
# domain only
|
||||||
|
return ErrorKey.NO_DOMAIN, {"domain": domain}
|
||||||
|
|
||||||
|
# Default error
|
||||||
|
return ErrorKey.NO_INTENT, {}
|
||||||
|
|
||||||
|
|
||||||
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
|
def _collect_list_references(expression: Expression, list_names: set[str]) -> None:
|
||||||
|
@ -7,5 +7,5 @@
|
|||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["hassil==1.6.0", "home-assistant-intents==2024.1.29"]
|
"requirements": ["hassil==1.6.0", "home-assistant-intents==2024.2.1"]
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ hass-nabucasa==0.76.0
|
|||||||
hassil==1.6.0
|
hassil==1.6.0
|
||||||
home-assistant-bluetooth==1.12.0
|
home-assistant-bluetooth==1.12.0
|
||||||
home-assistant-frontend==20240131.0
|
home-assistant-frontend==20240131.0
|
||||||
home-assistant-intents==2024.1.29
|
home-assistant-intents==2024.2.1
|
||||||
httpx==0.26.0
|
httpx==0.26.0
|
||||||
ifaddr==0.2.0
|
ifaddr==0.2.0
|
||||||
janus==1.0.0
|
janus==1.0.0
|
||||||
|
@ -1062,7 +1062,7 @@ holidays==0.41
|
|||||||
home-assistant-frontend==20240131.0
|
home-assistant-frontend==20240131.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2024.1.29
|
home-assistant-intents==2024.2.1
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.2
|
homeconnect==0.7.2
|
||||||
|
@ -858,7 +858,7 @@ holidays==0.41
|
|||||||
home-assistant-frontend==20240131.0
|
home-assistant-frontend==20240131.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
home-assistant-intents==2024.1.29
|
home-assistant-intents==2024.2.1
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.2
|
homeconnect==0.7.2
|
||||||
|
@ -339,7 +339,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'An unexpected error occurred while handling the intent',
|
'speech': 'An unexpected error occurred',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -379,7 +379,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'An unexpected error occurred while handling the intent',
|
'speech': 'An unexpected error occurred',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -519,7 +519,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called late added alias',
|
'speech': 'Sorry, I am not aware of any device called late added alias',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -539,7 +539,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called kitchen light',
|
'speech': 'Sorry, I am not aware of any device called kitchen light',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -679,7 +679,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called late added light',
|
'speech': 'Sorry, I am not aware of any device called late added light',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -759,7 +759,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called kitchen light',
|
'speech': 'Sorry, I am not aware of any device called kitchen light',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -779,7 +779,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called my cool light',
|
'speech': 'Sorry, I am not aware of any device called my cool light',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -919,7 +919,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called kitchen light',
|
'speech': 'Sorry, I am not aware of any device called kitchen light',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
@ -969,7 +969,7 @@
|
|||||||
'speech': dict({
|
'speech': dict({
|
||||||
'plain': dict({
|
'plain': dict({
|
||||||
'extra_data': None,
|
'extra_data': None,
|
||||||
'speech': 'Sorry, I am not aware of any device or entity called renamed light',
|
'speech': 'Sorry, I am not aware of any device called renamed light',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
from hassil.recognize import Intent, IntentData, MatchEntity, RecognizeResult
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import conversation
|
from homeassistant.components import conversation
|
||||||
@ -430,8 +431,8 @@ async def test_device_area_context(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_error_missing_entity(hass: HomeAssistant, init_components) -> None:
|
async def test_error_no_device(hass: HomeAssistant, init_components) -> None:
|
||||||
"""Test error message when entity is missing."""
|
"""Test error message when device/entity is missing."""
|
||||||
result = await conversation.async_converse(
|
result = await conversation.async_converse(
|
||||||
hass, "turn on missing entity", None, Context(), None
|
hass, "turn on missing entity", None, Context(), None
|
||||||
)
|
)
|
||||||
@ -440,11 +441,11 @@ async def test_error_missing_entity(hass: HomeAssistant, init_components) -> Non
|
|||||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||||
assert (
|
assert (
|
||||||
result.response.speech["plain"]["speech"]
|
result.response.speech["plain"]["speech"]
|
||||||
== "Sorry, I am not aware of any device or entity called missing entity"
|
== "Sorry, I am not aware of any device called missing entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_error_missing_area(hass: HomeAssistant, init_components) -> None:
|
async def test_error_no_area(hass: HomeAssistant, init_components) -> None:
|
||||||
"""Test error message when area is missing."""
|
"""Test error message when area is missing."""
|
||||||
result = await conversation.async_converse(
|
result = await conversation.async_converse(
|
||||||
hass, "turn on the lights in missing area", None, Context(), None
|
hass, "turn on the lights in missing area", None, Context(), None
|
||||||
@ -458,10 +459,60 @@ async def test_error_missing_area(hass: HomeAssistant, init_components) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_error_no_exposed_for_domain(
|
async def test_error_no_device_in_area(
|
||||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test error message when no entities for a domain are exposed in an area."""
|
"""Test error message when area is missing a device/entity."""
|
||||||
|
area_registry.async_get_or_create("kitchen")
|
||||||
|
result = await conversation.async_converse(
|
||||||
|
hass, "turn on missing entity in the kitchen", 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"]
|
||||||
|
== "Sorry, I am not aware of any device called missing entity in the kitchen area"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_error_no_domain(
|
||||||
|
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test error message when no devices/entities exist for a domain."""
|
||||||
|
|
||||||
|
# We don't have a sentence for turning on all fans
|
||||||
|
fan_domain = MatchEntity(name="domain", value="fan", text="")
|
||||||
|
recognize_result = RecognizeResult(
|
||||||
|
intent=Intent("HassTurnOn"),
|
||||||
|
intent_data=IntentData([]),
|
||||||
|
entities={"domain": fan_domain},
|
||||||
|
entities_list=[fan_domain],
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.conversation.default_agent.recognize_all",
|
||||||
|
return_value=[recognize_result],
|
||||||
|
):
|
||||||
|
result = await conversation.async_converse(
|
||||||
|
hass, "turn on the fans", 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"]
|
||||||
|
== "Sorry, I am not aware of any fan"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_error_no_domain_in_area(
|
||||||
|
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test error message when no devices/entities for a domain exist in an area."""
|
||||||
area_registry.async_get_or_create("kitchen")
|
area_registry.async_get_or_create("kitchen")
|
||||||
result = await conversation.async_converse(
|
result = await conversation.async_converse(
|
||||||
hass, "turn on the lights in the kitchen", None, Context(), None
|
hass, "turn on the lights in the kitchen", None, Context(), None
|
||||||
@ -475,10 +526,43 @@ async def test_error_no_exposed_for_domain(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_error_no_exposed_for_device_class(
|
async def test_error_no_device_class(
|
||||||
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test error message when no entities of a device class are exposed in an area."""
|
"""Test error message when no entities of a device class exist."""
|
||||||
|
|
||||||
|
# We don't have a sentence for opening all windows
|
||||||
|
window_class = MatchEntity(name="device_class", value="window", text="")
|
||||||
|
recognize_result = RecognizeResult(
|
||||||
|
intent=Intent("HassTurnOn"),
|
||||||
|
intent_data=IntentData([]),
|
||||||
|
entities={"device_class": window_class},
|
||||||
|
entities_list=[window_class],
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.conversation.default_agent.recognize_all",
|
||||||
|
return_value=[recognize_result],
|
||||||
|
):
|
||||||
|
result = await conversation.async_converse(
|
||||||
|
hass, "open the windows", 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"]
|
||||||
|
== "Sorry, I am not aware of any window"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_error_no_device_class_in_area(
|
||||||
|
hass: HomeAssistant, init_components, area_registry: ar.AreaRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Test error message when no entities of a device class exist in an area."""
|
||||||
area_registry.async_get_or_create("bedroom")
|
area_registry.async_get_or_create("bedroom")
|
||||||
result = await conversation.async_converse(
|
result = await conversation.async_converse(
|
||||||
hass, "open bedroom windows", None, Context(), None
|
hass, "open bedroom windows", None, Context(), None
|
||||||
@ -492,8 +576,8 @@ async def test_error_no_exposed_for_device_class(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_error_match_failure(hass: HomeAssistant, init_components) -> None:
|
async def test_error_no_intent(hass: HomeAssistant, init_components) -> None:
|
||||||
"""Test response with complete match failure."""
|
"""Test response with an intent match failure."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.conversation.default_agent.recognize_all",
|
"homeassistant.components.conversation.default_agent.recognize_all",
|
||||||
return_value=[],
|
return_value=[],
|
||||||
@ -506,6 +590,10 @@ async def test_error_match_failure(hass: HomeAssistant, init_components) -> None
|
|||||||
assert (
|
assert (
|
||||||
result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
|
||||||
)
|
)
|
||||||
|
assert (
|
||||||
|
result.response.speech["plain"]["speech"]
|
||||||
|
== "Sorry, I couldn't understand that"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_no_states_matched_default_error(
|
async def test_no_states_matched_default_error(
|
||||||
@ -601,5 +689,5 @@ async def test_all_domains_loaded(
|
|||||||
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
|
||||||
assert (
|
assert (
|
||||||
result.response.speech["plain"]["speech"]
|
result.response.speech["plain"]["speech"]
|
||||||
== "Sorry, I am not aware of any device or entity called test light"
|
== "Sorry, I am not aware of any device called test light"
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user