mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Fix deserialize bug + add test coverage (#92382)
This commit is contained in:
parent
5ed41d3d51
commit
1baa4d5109
@ -358,13 +358,15 @@ class ExposedEntities(StorageCollection[ExposedEntity, SerializedExposedEntities
|
|||||||
|
|
||||||
def _create_item(self, item_id: str, data: dict) -> ExposedEntity:
|
def _create_item(self, item_id: str, data: dict) -> ExposedEntity:
|
||||||
"""Create an item from validated config."""
|
"""Create an item from validated config."""
|
||||||
del data["entity_id"]
|
return ExposedEntity(
|
||||||
return ExposedEntity(**data)
|
assistants=data["assistants"],
|
||||||
|
)
|
||||||
|
|
||||||
def _deserialize_item(self, data: dict) -> ExposedEntity:
|
def _deserialize_item(self, data: dict) -> ExposedEntity:
|
||||||
"""Create an item from its serialized representation."""
|
"""Create an item from its serialized representation."""
|
||||||
del data["entity_id"]
|
return ExposedEntity(
|
||||||
return ExposedEntity(**data)
|
assistants=data["assistants"],
|
||||||
|
)
|
||||||
|
|
||||||
def _serialize_item(self, item_id: str, item: ExposedEntity) -> dict:
|
def _serialize_item(self, item_id: str, item: ExposedEntity) -> dict:
|
||||||
"""Return the serialized representation of an item for storing."""
|
"""Return the serialized representation of an item for storing."""
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_get_assistant_settings
|
||||||
|
dict({
|
||||||
|
'climate.test_unique1': mappingproxy({
|
||||||
|
'should_expose': True,
|
||||||
|
}),
|
||||||
|
'light.not_in_registry': dict({
|
||||||
|
'should_expose': True,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_get_assistant_settings.1
|
||||||
|
dict({
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_listeners
|
||||||
|
dict({
|
||||||
|
'light.kitchen': dict({
|
||||||
|
'should_expose': True,
|
||||||
|
}),
|
||||||
|
'switch.test_unique1': mappingproxy({
|
||||||
|
'should_expose': True,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
# ---
|
@ -1,16 +1,19 @@
|
|||||||
"""Test Home Assistant exposed entities helper."""
|
"""Test Home Assistant exposed entities helper."""
|
||||||
import pytest
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
from homeassistant.components.homeassistant.exposed_entities import (
|
from homeassistant.components.homeassistant.exposed_entities import (
|
||||||
DATA_EXPOSED_ENTITIES,
|
DATA_EXPOSED_ENTITIES,
|
||||||
ExposedEntities,
|
ExposedEntities,
|
||||||
ExposedEntity,
|
ExposedEntity,
|
||||||
|
async_expose_entity,
|
||||||
async_get_assistant_settings,
|
async_get_assistant_settings,
|
||||||
async_listen_entity_updates,
|
async_listen_entity_updates,
|
||||||
async_should_expose,
|
async_should_expose,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, EntityCategory
|
from homeassistant.const import CLOUD_NEVER_EXPOSED_ENTITIES, EntityCategory
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import entity_registry as er
|
from homeassistant.helpers import entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
@ -30,11 +33,18 @@ async def test_load_preferences(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert list(exposed_entities._assistants) == ["test1", "test2"]
|
assert list(exposed_entities._assistants) == ["test1", "test2"]
|
||||||
|
|
||||||
exposed_entities2 = ExposedEntities(hass)
|
await exposed_entities.async_expose_entity("test1", "light.kitchen", True)
|
||||||
|
await exposed_entities.async_expose_entity("test1", "light.living_room", True)
|
||||||
|
await exposed_entities.async_expose_entity("test2", "light.kitchen", True)
|
||||||
|
await exposed_entities.async_expose_entity("test2", "light.kitchen", True)
|
||||||
|
|
||||||
await flush_store(exposed_entities.store)
|
await flush_store(exposed_entities.store)
|
||||||
|
|
||||||
|
exposed_entities2 = ExposedEntities(hass)
|
||||||
await exposed_entities2.async_load()
|
await exposed_entities2.async_load()
|
||||||
|
|
||||||
assert exposed_entities._assistants == exposed_entities2._assistants
|
assert exposed_entities._assistants == exposed_entities2._assistants
|
||||||
|
assert exposed_entities.data == exposed_entities2.data
|
||||||
|
|
||||||
|
|
||||||
async def test_expose_entity(
|
async def test_expose_entity(
|
||||||
@ -282,6 +292,7 @@ async def test_listen_updates(
|
|||||||
async def test_get_assistant_settings(
|
async def test_get_assistant_settings(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test get assistant settings."""
|
"""Test get assistant settings."""
|
||||||
assert await async_setup_component(hass, "homeassistant", {})
|
assert await async_setup_component(hass, "homeassistant", {})
|
||||||
@ -294,16 +305,22 @@ async def test_get_assistant_settings(
|
|||||||
assert async_get_assistant_settings(hass, "cloud.alexa") == {}
|
assert async_get_assistant_settings(hass, "cloud.alexa") == {}
|
||||||
|
|
||||||
await 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") == {
|
await exposed_entities.async_expose_entity(
|
||||||
"climate.test_unique1": {"should_expose": True}
|
"cloud.alexa", "light.not_in_registry", True
|
||||||
}
|
)
|
||||||
assert async_get_assistant_settings(hass, "cloud.google_assistant") == {}
|
assert async_get_assistant_settings(hass, "cloud.alexa") == snapshot
|
||||||
|
assert async_get_assistant_settings(hass, "cloud.google_assistant") == snapshot
|
||||||
|
|
||||||
|
with pytest.raises(HomeAssistantError):
|
||||||
|
exposed_entities.async_get_entity_settings("light.unknown")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("use_registry", [True, False])
|
||||||
async def test_should_expose(
|
async def test_should_expose(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry: er.EntityRegistry,
|
entity_registry: er.EntityRegistry,
|
||||||
hass_ws_client: WebSocketGenerator,
|
hass_ws_client: WebSocketGenerator,
|
||||||
|
use_registry: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test expose entity."""
|
"""Test expose entity."""
|
||||||
ws_client = await hass_ws_client(hass)
|
ws_client = await hass_ws_client(hass)
|
||||||
@ -325,68 +342,96 @@ async def test_should_expose(
|
|||||||
assert await 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
|
# Blocked entity is not exposed
|
||||||
entry_blocked = entity_registry.async_get_or_create(
|
if use_registry:
|
||||||
"group", "test", "unique", suggested_object_id="all_locks"
|
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 (
|
assert entry_blocked.entity_id == "group.all_locks"
|
||||||
await async_should_expose(hass, "cloud.alexa", entry_blocked.entity_id) is False
|
assert CLOUD_NEVER_EXPOSED_ENTITIES[0] == "group.all_locks"
|
||||||
)
|
assert await async_should_expose(hass, "cloud.alexa", "group.all_locks") is False
|
||||||
|
|
||||||
# Lock is exposed
|
# Lock is exposed
|
||||||
lock1 = entity_registry.async_get_or_create("lock", "test", "unique1")
|
if use_registry:
|
||||||
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
|
entity_registry.async_get_or_create("lock", "test", "unique1")
|
||||||
assert await async_should_expose(hass, "cloud.alexa", lock1.entity_id) is True
|
assert await async_should_expose(hass, "cloud.alexa", "lock.test_unique1") is True
|
||||||
|
|
||||||
# Hidden entity is not exposed
|
# Hidden entity is not exposed
|
||||||
lock2 = entity_registry.async_get_or_create(
|
if use_registry:
|
||||||
"lock", "test", "unique2", hidden_by=er.RegistryEntryHider.USER
|
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 await async_should_expose(hass, "cloud.alexa", lock2.entity_id) is False
|
assert (
|
||||||
|
await async_should_expose(hass, "cloud.alexa", "lock.test_unique2") is False
|
||||||
|
)
|
||||||
|
|
||||||
# Entity with category is not exposed
|
# Entity with category is not exposed
|
||||||
lock3 = entity_registry.async_get_or_create(
|
entity_registry.async_get_or_create(
|
||||||
"lock", "test", "unique3", entity_category=EntityCategory.CONFIG
|
"lock", "test", "unique3", entity_category=EntityCategory.CONFIG
|
||||||
)
|
)
|
||||||
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
|
assert (
|
||||||
assert await async_should_expose(hass, "cloud.alexa", lock3.entity_id) is False
|
await async_should_expose(hass, "cloud.alexa", "lock.test_unique3") is False
|
||||||
|
)
|
||||||
|
|
||||||
# Binary sensor without device class is not exposed
|
# Binary sensor without device class is not exposed
|
||||||
binarysensor1 = entity_registry.async_get_or_create(
|
if use_registry:
|
||||||
"binary_sensor", "test", "unique1"
|
entity_registry.async_get_or_create("binary_sensor", "test", "unique1")
|
||||||
)
|
else:
|
||||||
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
|
hass.states.async_set("binary_sensor.test_unique1", "on", {})
|
||||||
assert (
|
assert (
|
||||||
await async_should_expose(hass, "cloud.alexa", binarysensor1.entity_id) is False
|
await async_should_expose(hass, "cloud.alexa", "binary_sensor.test_unique1")
|
||||||
|
is False
|
||||||
)
|
)
|
||||||
|
|
||||||
# Binary sensor with certain device class is exposed
|
# Binary sensor with certain device class is exposed
|
||||||
binarysensor2 = entity_registry.async_get_or_create(
|
if use_registry:
|
||||||
"binary_sensor",
|
entity_registry.async_get_or_create(
|
||||||
"test",
|
"binary_sensor",
|
||||||
"unique2",
|
"test",
|
||||||
original_device_class="door",
|
"unique2",
|
||||||
)
|
original_device_class="door",
|
||||||
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
|
)
|
||||||
|
else:
|
||||||
|
hass.states.async_set(
|
||||||
|
"binary_sensor.test_unique2", "on", {"device_class": "door"}
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
await async_should_expose(hass, "cloud.alexa", binarysensor2.entity_id) is True
|
await async_should_expose(hass, "cloud.alexa", "binary_sensor.test_unique2")
|
||||||
|
is True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Sensor without device class is not exposed
|
# Sensor without device class is not exposed
|
||||||
sensor1 = entity_registry.async_get_or_create("sensor", "test", "unique1")
|
if use_registry:
|
||||||
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
|
entity_registry.async_get_or_create("sensor", "test", "unique1")
|
||||||
assert await async_should_expose(hass, "cloud.alexa", sensor1.entity_id) is False
|
else:
|
||||||
|
hass.states.async_set("sensor.test_unique1", "on", {})
|
||||||
|
assert (
|
||||||
|
await async_should_expose(hass, "cloud.alexa", "sensor.test_unique1") is False
|
||||||
|
)
|
||||||
|
|
||||||
# Sensor with certain device class is exposed
|
# Sensor with certain device class is exposed
|
||||||
sensor2 = entity_registry.async_get_or_create(
|
if use_registry:
|
||||||
"sensor",
|
entity_registry.async_get_or_create(
|
||||||
"test",
|
"sensor",
|
||||||
"unique2",
|
"test",
|
||||||
original_device_class="temperature",
|
"unique2",
|
||||||
|
original_device_class="temperature",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.test_unique2", "on", {"device_class": "temperature"}
|
||||||
|
)
|
||||||
|
assert await async_should_expose(hass, "cloud.alexa", "sensor.test_unique2") is True
|
||||||
|
# The second time we check, it should load it from storage
|
||||||
|
assert await async_should_expose(hass, "cloud.alexa", "sensor.test_unique2") is True
|
||||||
|
# Check with a different assistant
|
||||||
|
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
|
||||||
|
exposed_entities.async_set_expose_new_entities("cloud.no_default_expose", False)
|
||||||
|
assert (
|
||||||
|
await async_should_expose(
|
||||||
|
hass, "cloud.no_default_expose", "sensor.test_unique2"
|
||||||
|
)
|
||||||
|
is False
|
||||||
)
|
)
|
||||||
assert entry_blocked.entity_id == CLOUD_NEVER_EXPOSED_ENTITIES[0]
|
|
||||||
assert await async_should_expose(hass, "cloud.alexa", sensor2.entity_id) is True
|
|
||||||
|
|
||||||
|
|
||||||
async def test_list_exposed_entities(
|
async def test_list_exposed_entities(
|
||||||
@ -441,3 +486,21 @@ async def test_list_exposed_entities(
|
|||||||
"test.test_unique2": {"cloud.alexa": True, "cloud.google_assistant": True},
|
"test.test_unique2": {"cloud.alexa": True, "cloud.google_assistant": True},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_listeners(
|
||||||
|
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||||
|
) -> None:
|
||||||
|
"""Make sure we call entity listeners."""
|
||||||
|
assert await async_setup_component(hass, "homeassistant", {})
|
||||||
|
|
||||||
|
exposed_entities: ExposedEntities = hass.data[DATA_EXPOSED_ENTITIES]
|
||||||
|
|
||||||
|
callbacks = []
|
||||||
|
exposed_entities.async_listen_entity_updates("test1", lambda: callbacks.append(1))
|
||||||
|
|
||||||
|
await async_expose_entity(hass, "test1", "light.kitchen", True)
|
||||||
|
assert len(callbacks) == 1
|
||||||
|
|
||||||
|
entry1 = entity_registry.async_get_or_create("switch", "test", "unique1")
|
||||||
|
await async_expose_entity(hass, "test1", entry1.entity_id, True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user