core/tests/components/conversation/test_default_agent.py

174 lines
6.3 KiB
Python
Raw Normal View History

"""Test for the default agent."""
from unittest.mock import patch
import pytest
from homeassistant.components import conversation
from homeassistant.components.homeassistant.exposed_entities import (
async_get_assistant_settings,
)
from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.core import DOMAIN as HASS_DOMAIN, Context, HomeAssistant
from homeassistant.helpers import (
area_registry as ar,
device_registry as dr,
entity,
entity_registry as er,
intent,
)
from homeassistant.setup import async_setup_component
from . import expose_entity
from tests.common import async_mock_service
@pytest.fixture
async def init_components(hass):
"""Initialize relevant components with empty configs."""
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(hass, "conversation", {})
assert await async_setup_component(hass, "intent", {})
@pytest.mark.parametrize(
"er_kwargs",
[
{"hidden_by": er.RegistryEntryHider.USER},
{"hidden_by": er.RegistryEntryHider.INTEGRATION},
{"entity_category": entity.EntityCategory.CONFIG},
{"entity_category": entity.EntityCategory.DIAGNOSTIC},
],
)
async def test_hidden_entities_skipped(
hass: HomeAssistant, init_components, er_kwargs, entity_registry: er.EntityRegistry
) -> None:
"""Test we skip hidden entities."""
entity_registry.async_get_or_create(
"light", "demo", "1234", suggested_object_id="Test light", **er_kwargs
)
hass.states.async_set("light.test_light", "off")
calls = async_mock_service(hass, HASS_DOMAIN, "turn_on")
result = await conversation.async_converse(
hass, "turn on test light", None, Context(), None
)
assert len(calls) == 0
assert result.response.response_type == intent.IntentResponseType.ERROR
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
async def test_exposed_domains(hass: HomeAssistant, init_components) -> None:
"""Test that we can't interact with entities that aren't exposed."""
hass.states.async_set(
"media_player.test", "off", attributes={ATTR_FRIENDLY_NAME: "Test Media Player"}
)
result = await conversation.async_converse(
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.
assert result.response.response_type == intent.IntentResponseType.ERROR
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
async def test_exposed_areas(
hass: HomeAssistant,
init_components,
area_registry: ar.AreaRegistry,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
) -> None:
"""Test that only expose areas with an exposed entity/device."""
area_kitchen = area_registry.async_get_or_create("kitchen")
area_bedroom = area_registry.async_get_or_create("bedroom")
kitchen_device = device_registry.async_get_or_create(
config_entry_id="1234", connections=set(), identifiers={("demo", "id-1234")}
)
device_registry.async_update_device(kitchen_device.id, area_id=area_kitchen.id)
kitchen_light = entity_registry.async_get_or_create(
"light", "demo", "1234", original_name="kitchen light"
)
entity_registry.async_update_entity(
kitchen_light.entity_id, device_id=kitchen_device.id
)
hass.states.async_set(kitchen_light.entity_id, "on")
bedroom_light = entity_registry.async_get_or_create(
"light", "demo", "5678", original_name="bedroom light"
)
entity_registry.async_update_entity(
bedroom_light.entity_id, area_id=area_bedroom.id
)
hass.states.async_set(bedroom_light.entity_id, "on")
# Hide the bedroom light
expose_entity(hass, bedroom_light.entity_id, False)
result = await conversation.async_converse(
hass, "turn on lights in the kitchen", None, Context(), None
)
# All is well for the exposed kitchen light
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
# Bedroom is not exposed because it has no exposed entities
result = await conversation.async_converse(
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
assert result.response.response_type == intent.IntentResponseType.ERROR
assert result.response.error_code == intent.IntentResponseErrorCode.NO_INTENT_MATCH
async def test_conversation_agent(
hass: HomeAssistant,
init_components,
) -> None:
"""Test DefaultAgent."""
agent = await conversation._get_agent_manager(hass).async_get_agent(
conversation.HOME_ASSISTANT_AGENT
)
with patch(
"homeassistant.components.conversation.default_agent.get_domains_and_languages",
return_value={"homeassistant": ["dwarvish", "elvish", "entish"]},
):
assert agent.supported_languages == ["dwarvish", "elvish", "entish"]
async def test_expose_flag_automatically_set(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
) -> None:
"""Test DefaultAgent sets the expose flag on all entities automatically."""
assert await async_setup_component(hass, "homeassistant", {})
light = entity_registry.async_get_or_create("light", "demo", "1234")
test = entity_registry.async_get_or_create("test", "demo", "1234")
assert async_get_assistant_settings(hass, conversation.DOMAIN) == {}
assert await async_setup_component(hass, "conversation", {})
await hass.async_block_till_done()
# After setting up conversation, the expose flag should now be set on all entities
assert async_get_assistant_settings(hass, conversation.DOMAIN) == {
light.entity_id: {"should_expose": True},
test.entity_id: {"should_expose": False},
}
# New entities will automatically have the expose flag set
new_light = entity_registry.async_get_or_create("light", "demo", "2345")
await hass.async_block_till_done()
assert async_get_assistant_settings(hass, conversation.DOMAIN) == {
light.entity_id: {"should_expose": True},
new_light.entity_id: {"should_expose": True},
test.entity_id: {"should_expose": False},
}