Allow exposing entities not in the entity registry to assistants (#92363)

This commit is contained in:
Erik Montnemery 2023-05-02 22:08:09 +02:00 committed by GitHub
parent cc4e741cfa
commit e3c16e634b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 563 additions and 224 deletions

View file

@ -4,13 +4,13 @@ import pytest
from homeassistant.components.homeassistant.exposed_entities import (
DATA_EXPOSED_ENTITIES,
ExposedEntities,
ExposedEntity,
async_get_assistant_settings,
async_listen_entity_updates,
async_should_expose,
)
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
@ -31,7 +31,7 @@ async def test_load_preferences(hass: HomeAssistant) -> None:
assert list(exposed_entities._assistants) == ["test1", "test2"]
exposed_entities2 = ExposedEntities(hass)
await flush_store(exposed_entities._store)
await flush_store(exposed_entities.store)
await exposed_entities2.async_load()
assert exposed_entities._assistants == exposed_entities2._assistants
@ -50,6 +50,9 @@ async def test_expose_entity(
entry1 = entity_registry.async_get_or_create("test", "test", "unique1")
entry2 = entity_registry.async_get_or_create("test", "test", "unique2")
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
assert len(exposed_entities.data) == 0
# Set options
await ws_client.send_json_auto_id(
{
@ -67,6 +70,7 @@ async def test_expose_entity(
assert entry1.options == {"cloud.alexa": {"should_expose": True}}
entry2 = entity_registry.async_get(entry2.entity_id)
assert entry2.options == {}
assert len(exposed_entities.data) == 0
# Update options
await ws_client.send_json_auto_id(
@ -91,6 +95,7 @@ async def test_expose_entity(
"cloud.alexa": {"should_expose": False},
"cloud.google_assistant": {"should_expose": False},
}
assert len(exposed_entities.data) == 0
async def test_expose_entity_unknown(
@ -103,6 +108,7 @@ async def test_expose_entity_unknown(
await hass.async_block_till_done()
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
assert len(exposed_entities.data) == 0
# Set options
await ws_client.send_json_auto_id(
@ -115,14 +121,41 @@ async def test_expose_entity_unknown(
)
response = await ws_client.receive_json()
assert not response["success"]
assert response["error"] == {
"code": "not_found",
"message": "can't expose 'test.test'",
assert response["success"]
assert len(exposed_entities.data) == 1
assert exposed_entities.data == {
"test.test": ExposedEntity({"cloud.alexa": {"should_expose": True}})
}
with pytest.raises(HomeAssistantError):
exposed_entities.async_expose_entity("cloud.alexa", "test.test", True)
# Update options
await ws_client.send_json_auto_id(
{
"type": "homeassistant/expose_entity",
"assistants": ["cloud.alexa", "cloud.google_assistant"],
"entity_ids": ["test.test", "test.test2"],
"should_expose": False,
}
)
response = await ws_client.receive_json()
assert response["success"]
assert len(exposed_entities.data) == 2
assert exposed_entities.data == {
"test.test": ExposedEntity(
{
"cloud.alexa": {"should_expose": False},
"cloud.google_assistant": {"should_expose": False},
}
),
"test.test2": ExposedEntity(
{
"cloud.alexa": {"should_expose": False},
"cloud.google_assistant": {"should_expose": False},
}
),
}
async def test_expose_entity_blocked(
@ -178,7 +211,7 @@ async def test_expose_new_entities(
assert response["result"] == {"expose_new": False}
# Check if exposed - should be False
assert async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False
assert await async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False
# Expose new entities to Alexa
await ws_client.send_json_auto_id(
@ -201,10 +234,12 @@ async def test_expose_new_entities(
assert response["result"] == {"expose_new": expose_new}
# Check again if exposed - should still be False
assert async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False
assert await async_should_expose(hass, "cloud.alexa", entry1.entity_id) is False
# Check if exposed - should be True
assert async_should_expose(hass, "cloud.alexa", entry2.entity_id) == expose_new
assert (
await async_should_expose(hass, "cloud.alexa", entry2.entity_id) == expose_new
)
async def test_listen_updates(
@ -226,21 +261,21 @@ async def test_listen_updates(
entry = entity_registry.async_get_or_create("climate", "test", "unique1")
# Call for another assistant - listener not called
exposed_entities.async_expose_entity(
await exposed_entities.async_expose_entity(
"cloud.google_assistant", entry.entity_id, True
)
assert len(calls) == 0
# Call for our assistant - listener called
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True)
await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True)
assert len(calls) == 1
# Settings not changed - listener not called
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True)
await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True)
assert len(calls) == 1
# Settings changed - listener called
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, False)
await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, False)
assert len(calls) == 2
@ -258,7 +293,7 @@ async def test_get_assistant_settings(
assert async_get_assistant_settings(hass, "cloud.alexa") == {}
exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True)
await exposed_entities.async_expose_entity("cloud.alexa", entry.entity_id, True)
assert async_get_assistant_settings(hass, "cloud.alexa") == {
"climate.test_unique1": {"should_expose": True}
}
@ -287,40 +322,44 @@ async def test_should_expose(
assert response["success"]
# Unknown entity is not exposed
assert async_should_expose(hass, "test.test", "test.test") is False
assert await async_should_expose(hass, "test.test", "test.test") is False
# Blocked entity is not exposed
entry_blocked = entity_registry.async_get_or_create(
"group", "test", "unique", suggested_object_id="all_locks"
)
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", entry_blocked.entity_id) is False
assert (
await async_should_expose(hass, "cloud.alexa", entry_blocked.entity_id) is False
)
# Lock is exposed
lock1 = entity_registry.async_get_or_create("lock", "test", "unique1")
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", lock1.entity_id) is True
assert await async_should_expose(hass, "cloud.alexa", lock1.entity_id) is True
# Hidden entity is not exposed
lock2 = entity_registry.async_get_or_create(
"lock", "test", "unique2", hidden_by=er.RegistryEntryHider.USER
)
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", lock2.entity_id) is False
assert await async_should_expose(hass, "cloud.alexa", lock2.entity_id) is False
# Entity with category is not exposed
lock3 = entity_registry.async_get_or_create(
"lock", "test", "unique3", entity_category=EntityCategory.CONFIG
)
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", lock3.entity_id) is False
assert await async_should_expose(hass, "cloud.alexa", lock3.entity_id) is False
# Binary sensor without device class is not exposed
binarysensor1 = entity_registry.async_get_or_create(
"binary_sensor", "test", "unique1"
)
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", binarysensor1.entity_id) is False
assert (
await async_should_expose(hass, "cloud.alexa", binarysensor1.entity_id) is False
)
# Binary sensor with certain device class is exposed
binarysensor2 = entity_registry.async_get_or_create(
@ -330,12 +369,14 @@ async def test_should_expose(
original_device_class="door",
)
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", binarysensor2.entity_id) is True
assert (
await async_should_expose(hass, "cloud.alexa", binarysensor2.entity_id) is True
)
# Sensor without device class is not exposed
sensor1 = entity_registry.async_get_or_create("sensor", "test", "unique1")
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", sensor1.entity_id) is False
assert await async_should_expose(hass, "cloud.alexa", sensor1.entity_id) is False
# Sensor with certain device class is exposed
sensor2 = entity_registry.async_get_or_create(
@ -345,4 +386,58 @@ async def test_should_expose(
original_device_class="temperature",
)
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
assert async_should_expose(hass, "cloud.alexa", sensor2.entity_id) is True
assert await async_should_expose(hass, "cloud.alexa", sensor2.entity_id) is True
async def test_list_exposed_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test list exposed entities."""
ws_client = await hass_ws_client(hass)
assert await async_setup_component(hass, "homeassistant", {})
await hass.async_block_till_done()
entry1 = entity_registry.async_get_or_create("test", "test", "unique1")
entry2 = entity_registry.async_get_or_create("test", "test", "unique2")
# Set options for registered entities
await ws_client.send_json_auto_id(
{
"type": "homeassistant/expose_entity",
"assistants": ["cloud.alexa", "cloud.google_assistant"],
"entity_ids": [entry1.entity_id, entry2.entity_id],
"should_expose": True,
}
)
response = await ws_client.receive_json()
assert response["success"]
# Set options for entities not in the entity registry
await ws_client.send_json_auto_id(
{
"type": "homeassistant/expose_entity",
"assistants": ["cloud.alexa", "cloud.google_assistant"],
"entity_ids": [
"test.test",
"test.test2",
],
"should_expose": False,
}
)
response = await ws_client.receive_json()
assert response["success"]
# List exposed entities
await ws_client.send_json_auto_id({"type": "homeassistant/expose_entity/list"})
response = await ws_client.receive_json()
assert response["success"]
assert response["result"] == {
"exposed_entities": {
"test.test": {"cloud.alexa": False, "cloud.google_assistant": False},
"test.test2": {"cloud.alexa": False, "cloud.google_assistant": False},
"test.test_unique1": {"cloud.alexa": True, "cloud.google_assistant": True},
"test.test_unique2": {"cloud.alexa": True, "cloud.google_assistant": True},
},
}