Files
core/tests/helpers/test_entity_component.py
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

729 lines
24 KiB
Python
Raw Normal View History

2016-03-09 11:15:04 +01:00
"""The tests for the Entity component helper."""
2023-01-22 17:26:24 +01:00
2016-01-30 18:55:52 -08:00
from collections import OrderedDict
from datetime import timedelta
import logging
import re
2021-01-01 22:31:56 +01:00
from unittest.mock import AsyncMock, Mock, patch
2016-01-30 18:55:52 -08:00
from freezegun import freeze_time
import pytest
import voluptuous as vol
from homeassistant.const import (
ENTITY_MATCH_ALL,
ENTITY_MATCH_NONE,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.core import (
HomeAssistant,
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import HomeAssistantError, PlatformNotReady
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.entity_component import EntityComponent, async_update_entity
2023-03-16 11:10:56 +01:00
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.service import async_get_all_descriptions
2023-03-16 11:10:56 +01:00
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
2019-04-30 18:20:38 +02:00
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
2016-01-30 18:55:52 -08:00
2016-01-31 00:55:46 -08:00
from tests.common import (
MockConfigEntry,
MockEntity,
MockModule,
MockPlatform,
async_fire_time_changed,
mock_integration,
mock_platform,
)
2016-01-30 18:55:52 -08:00
_LOGGER = logging.getLogger(__name__)
DOMAIN = "test_domain"
async def test_setup_loads_platforms(hass: HomeAssistant) -> None:
2019-04-30 18:20:38 +02:00
"""Test the loading of the platforms."""
component_setup = Mock(return_value=True)
platform_setup = Mock(return_value=None)
mock_integration(hass, MockModule("test_component", setup=component_setup))
# mock the dependencies
mock_integration(hass, MockModule("mod2", dependencies=["test_component"]))
mock_platform(hass, "mod2.test_domain", MockPlatform(setup_platform=platform_setup))
2019-04-30 18:20:38 +02:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
assert not component_setup.called
assert not platform_setup.called
component.setup({DOMAIN: {"platform": "mod2"}})
await hass.async_block_till_done()
assert component_setup.called
assert platform_setup.called
async def test_setup_recovers_when_setup_raises(hass: HomeAssistant) -> None:
2019-04-30 18:20:38 +02:00
"""Test the setup if exceptions are happening."""
platform1_setup = Mock(side_effect=Exception("Broken"))
platform2_setup = Mock(return_value=None)
mock_platform(
hass, "mod1.test_domain", MockPlatform(setup_platform=platform1_setup)
)
mock_platform(
hass, "mod2.test_domain", MockPlatform(setup_platform=platform2_setup)
)
2019-04-30 18:20:38 +02:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
assert not platform1_setup.called
assert not platform2_setup.called
component.setup(
OrderedDict(
[
(DOMAIN, {"platform": "mod1"}),
2020-01-03 15:47:06 +02:00
(f"{DOMAIN} 2", {"platform": "non_exist"}),
(f"{DOMAIN} 3", {"platform": "mod2"}),
2019-04-30 18:20:38 +02:00
]
)
2019-07-31 12:25:30 -07:00
)
2019-04-30 18:20:38 +02:00
await hass.async_block_till_done()
assert platform1_setup.called
assert platform2_setup.called
2020-08-27 13:56:20 +02:00
@patch(
"homeassistant.helpers.entity_component.EntityComponent.async_setup_platform",
)
2020-04-30 13:29:50 -07:00
@patch("homeassistant.setup.async_setup_component", return_value=True)
2023-02-20 11:42:56 +01:00
async def test_setup_does_discovery(
2023-03-16 11:10:56 +01:00
mock_setup_component: AsyncMock, mock_setup: AsyncMock, hass: HomeAssistant
2023-02-20 11:42:56 +01:00
) -> None:
2019-04-30 18:20:38 +02:00
"""Test setup for discovery."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
component.setup({})
discovery.load_platform(
hass, DOMAIN, "platform_test", {"msg": "discovery_info"}, {DOMAIN: {}}
)
await hass.async_block_till_done()
assert mock_setup.called
2024-06-28 10:46:02 +02:00
assert mock_setup.call_args[0] == ("platform_test", {}, {"msg": "discovery_info"})
2019-04-30 18:20:38 +02:00
async def test_set_scan_interval_via_config(hass: HomeAssistant) -> None:
2019-04-30 18:20:38 +02:00
"""Test the setting of the scan interval via configuration."""
2019-07-31 12:25:30 -07:00
2023-03-16 11:10:56 +01:00
def platform_setup(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
2019-04-30 18:20:38 +02:00
"""Test the platform setup."""
add_entities([MockEntity(should_poll=True)])
mock_platform(
hass, "platform.test_domain", MockPlatform(setup_platform=platform_setup)
)
2019-04-30 18:20:38 +02:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
with patch.object(hass.loop, "call_later") as mock_track:
component.setup(
{DOMAIN: {"platform": "platform", "scan_interval": timedelta(seconds=30)}}
)
2019-04-30 18:20:38 +02:00
await hass.async_block_till_done()
2019-04-30 18:20:38 +02:00
assert mock_track.called
assert mock_track.call_args[0][0] == 30.0
2019-04-30 18:20:38 +02:00
async def test_set_entity_namespace_via_config(hass: HomeAssistant) -> None:
2019-04-30 18:20:38 +02:00
"""Test setting an entity namespace."""
2019-07-31 12:25:30 -07:00
2023-03-16 11:10:56 +01:00
def platform_setup(
hass: HomeAssistant,
config: ConfigType,
add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None,
) -> None:
2019-04-30 18:20:38 +02:00
"""Test the platform setup."""
add_entities([MockEntity(name="beer"), MockEntity(name=None)])
platform = MockPlatform(setup_platform=platform_setup)
2019-04-30 18:20:38 +02:00
mock_platform(hass, "platform.test_domain", platform)
2019-04-30 18:20:38 +02:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
component.setup({DOMAIN: {"platform": "platform", "entity_namespace": "yummy"}})
await hass.async_block_till_done()
assert sorted(hass.states.async_entity_ids()) == [
"test_domain.yummy_beer",
"test_domain.yummy_unnamed_device",
]
async def test_extract_from_service_available_device(hass: HomeAssistant) -> None:
"""Test the extraction of entity from service and device is available."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
2019-04-30 18:20:38 +02:00
await component.async_add_entities(
[
2018-02-08 03:16:51 -08:00
MockEntity(name="test_1"),
MockEntity(name="test_2", available=False),
MockEntity(name="test_3"),
MockEntity(name="test_4", available=False),
]
)
call_1 = ServiceCall(hass, "test", "service", data={"entity_id": ENTITY_MATCH_ALL})
2024-06-28 10:46:02 +02:00
assert sorted(
2019-04-30 18:20:38 +02:00
ent.entity_id for ent in (await component.async_extract_from_service(call_1))
2024-06-28 10:46:02 +02:00
) == ["test_domain.test_1", "test_domain.test_3"]
call_2 = ServiceCall(
hass,
"test",
"service",
data={"entity_id": ["test_domain.test_3", "test_domain.test_4"]},
)
2024-06-28 10:46:02 +02:00
assert sorted(
2019-04-30 18:20:38 +02:00
ent.entity_id for ent in (await component.async_extract_from_service(call_2))
2024-06-28 10:46:02 +02:00
) == ["test_domain.test_3"]
async def test_platform_not_ready(hass: HomeAssistant) -> None:
"""Test that we retry when platform not ready."""
platform1_setup = Mock(side_effect=[PlatformNotReady, PlatformNotReady, None])
2019-04-14 19:07:05 -07:00
mock_integration(hass, MockModule("mod1"))
mock_platform(
hass, "mod1.test_domain", MockPlatform(setup_platform=platform1_setup)
)
component = EntityComponent(_LOGGER, DOMAIN, hass)
utcnow = dt_util.utcnow()
with freeze_time(utcnow):
await component.async_setup({DOMAIN: {"platform": "mod1"}})
await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 1
2023-12-05 08:43:58 +01:00
assert "mod1.test_domain" not in hass.config.components
# Should not trigger attempt 2
async_fire_time_changed(hass, utcnow + timedelta(seconds=29))
2019-04-30 18:20:38 +02:00
await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 1
# Should trigger attempt 2
async_fire_time_changed(hass, utcnow + timedelta(seconds=30))
2019-04-30 18:20:38 +02:00
await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 2
2023-12-05 08:43:58 +01:00
assert "mod1.test_domain" not in hass.config.components
# This should not trigger attempt 3
async_fire_time_changed(hass, utcnow + timedelta(seconds=59))
2019-04-30 18:20:38 +02:00
await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 2
# Trigger attempt 3, which succeeds
async_fire_time_changed(hass, utcnow + timedelta(seconds=60))
2019-04-30 18:20:38 +02:00
await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 3
2023-12-05 08:43:58 +01:00
assert "mod1.test_domain" in hass.config.components
2017-10-19 10:56:25 +02:00
async def test_extract_from_service_fails_if_no_entity_id(hass: HomeAssistant) -> None:
2018-02-08 03:16:51 -08:00
"""Test the extraction of everything from service."""
2017-10-19 10:56:25 +02:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
2019-04-30 18:20:38 +02:00
await component.async_add_entities(
2018-02-08 03:16:51 -08:00
[MockEntity(name="test_1"), MockEntity(name="test_2")]
)
2017-10-19 10:56:25 +02:00
assert (
await component.async_extract_from_service(ServiceCall(hass, "test", "service"))
== []
)
assert (
await component.async_extract_from_service(
ServiceCall(hass, "test", "service", {"entity_id": ENTITY_MATCH_NONE})
)
== []
)
assert (
await component.async_extract_from_service(
ServiceCall(hass, "test", "service", {"area_id": ENTITY_MATCH_NONE})
)
== []
2019-07-31 12:25:30 -07:00
)
2018-02-08 03:16:51 -08:00
async def test_extract_from_service_filter_out_non_existing_entities(
hass: HomeAssistant,
) -> None:
2018-02-08 03:16:51 -08:00
"""Test the extraction of non existing entities from service."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
2019-04-30 18:20:38 +02:00
await component.async_add_entities(
2018-02-08 03:16:51 -08:00
[MockEntity(name="test_1"), MockEntity(name="test_2")]
)
call = ServiceCall(
hass,
2018-02-08 03:16:51 -08:00
"test",
"service",
{"entity_id": ["test_domain.test_2", "test_domain.non_exist"]},
2017-10-19 10:56:25 +02:00
)
2024-06-28 10:46:02 +02:00
assert [
2019-04-30 18:20:38 +02:00
ent.entity_id for ent in await component.async_extract_from_service(call)
2024-06-28 10:46:02 +02:00
] == ["test_domain.test_2"]
2017-10-19 10:56:25 +02:00
async def test_extract_from_service_no_group_expand(hass: HomeAssistant) -> None:
2018-02-08 03:16:51 -08:00
"""Test not expanding a group."""
2017-10-19 10:56:25 +02:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities([MockEntity(entity_id="group.test_group")])
2017-10-19 10:56:25 +02:00
call = ServiceCall(hass, "test", "service", {"entity_id": ["group.test_group"]})
2017-10-19 10:56:25 +02:00
2019-04-30 18:20:38 +02:00
extracted = await component.async_extract_from_service(call, expand_group=False)
assert len(extracted) == 1
assert extracted[0].entity_id == "group.test_group"
2018-02-11 23:26:52 -08:00
async def test_setup_dependencies_platform(hass: HomeAssistant) -> None:
2018-02-11 23:26:52 -08:00
"""Test we setup the dependencies of a platform.
2020-01-31 17:33:00 +01:00
We're explicitly testing that we process dependencies even if a component
2018-02-11 23:26:52 -08:00
with the same name has already been loaded.
"""
2019-04-14 19:07:05 -07:00
mock_integration(
hass, MockModule("test_component", dependencies=["test_component2"])
)
mock_integration(hass, MockModule("test_component2"))
mock_platform(hass, "test_component.test_domain", MockPlatform())
2018-02-11 23:26:52 -08:00
component = EntityComponent(_LOGGER, DOMAIN, hass)
2019-04-30 18:20:38 +02:00
await component.async_setup({DOMAIN: {"platform": "test_component"}})
await hass.async_block_till_done()
2018-02-11 23:26:52 -08:00
assert "test_component" in hass.config.components
assert "test_component2" in hass.config.components
2023-12-05 08:43:58 +01:00
assert "test_component.test_domain" in hass.config.components
async def test_setup_entry(hass: HomeAssistant) -> None:
"""Test setup entry calls async_setup_entry on platform."""
2020-04-30 13:29:50 -07:00
mock_setup_entry = AsyncMock(return_value=True)
mock_platform(
2018-05-01 14:57:30 -04:00
hass,
"entry_domain.test_domain",
MockPlatform(
async_setup_entry=mock_setup_entry, scan_interval=timedelta(seconds=5)
2019-07-31 12:25:30 -07:00
),
)
component = EntityComponent(_LOGGER, DOMAIN, hass)
entry = MockConfigEntry(domain="entry_domain")
assert await component.async_setup_entry(entry)
assert len(mock_setup_entry.mock_calls) == 1
2019-04-30 18:20:38 +02:00
p_hass, p_entry, _ = mock_setup_entry.mock_calls[0][1]
assert p_hass is hass
assert p_entry is entry
assert component._platforms[entry.entry_id].scan_interval == timedelta(seconds=5)
async def test_setup_entry_platform_not_exist(hass: HomeAssistant) -> None:
2020-01-31 17:33:00 +01:00
"""Test setup entry fails if platform does not exist."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
entry = MockConfigEntry(domain="non_existing")
assert (await component.async_setup_entry(entry)) is False
async def test_setup_entry_fails_duplicate(hass: HomeAssistant) -> None:
"""Test we don't allow setting up a config entry twice."""
2020-04-30 13:29:50 -07:00
mock_setup_entry = AsyncMock(return_value=True)
mock_platform(
2018-05-01 14:57:30 -04:00
hass,
"entry_domain.test_domain",
MockPlatform(async_setup_entry=mock_setup_entry),
)
component = EntityComponent(_LOGGER, DOMAIN, hass)
entry = MockConfigEntry(domain="entry_domain")
assert await component.async_setup_entry(entry)
with pytest.raises(
ValueError,
match=re.escape(
f"Config entry Mock Title ({entry.entry_id}) for "
"entry_domain.test_domain has already been setup!"
),
):
await component.async_setup_entry(entry)
2018-04-12 08:28:54 -04:00
async def test_unload_entry_resets_platform(hass: HomeAssistant) -> None:
2018-04-12 08:28:54 -04:00
"""Test unloading an entry removes all entities."""
2020-04-30 13:29:50 -07:00
mock_setup_entry = AsyncMock(return_value=True)
mock_platform(
2018-05-01 14:57:30 -04:00
hass,
"entry_domain.test_domain",
2018-04-12 08:28:54 -04:00
MockPlatform(async_setup_entry=mock_setup_entry),
)
component = EntityComponent(_LOGGER, DOMAIN, hass)
entry = MockConfigEntry(domain="entry_domain")
assert await component.async_setup_entry(entry)
assert len(mock_setup_entry.mock_calls) == 1
add_entities = mock_setup_entry.mock_calls[0][1][2]
add_entities([MockEntity()])
await hass.async_block_till_done()
assert len(hass.states.async_entity_ids()) == 1
assert await component.async_unload_entry(entry)
assert len(hass.states.async_entity_ids()) == 0
async def test_unload_entry_fails_if_never_loaded(hass: HomeAssistant) -> None:
2018-04-12 08:28:54 -04:00
"""."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
entry = MockConfigEntry(domain="entry_domain")
with pytest.raises(ValueError):
await component.async_unload_entry(entry)
2018-10-09 16:54:38 +02:00
async def test_update_entity(hass: HomeAssistant) -> None:
2018-10-09 16:54:38 +02:00
"""Test that we can update an entity with the helper."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
2018-10-09 16:54:38 +02:00
entity = MockEntity()
2020-04-03 00:34:50 -07:00
entity.async_write_ha_state = Mock()
2020-04-30 13:29:50 -07:00
entity.async_update_ha_state = AsyncMock(return_value=None)
2018-10-09 16:54:38 +02:00
await component.async_add_entities([entity])
# Called as part of async_add_entities
2020-04-03 00:34:50 -07:00
assert len(entity.async_write_ha_state.mock_calls) == 1
2018-10-09 16:54:38 +02:00
await async_update_entity(hass, entity.entity_id)
2018-10-09 16:54:38 +02:00
2020-04-03 00:34:50 -07:00
assert len(entity.async_update_ha_state.mock_calls) == 1
2018-10-09 16:54:38 +02:00
assert entity.async_update_ha_state.mock_calls[-1][1][0] is True
2018-11-29 22:26:06 +01:00
async def test_set_service_race(hass: HomeAssistant) -> None:
2018-11-29 22:26:06 +01:00
"""Test race condition on setting service."""
exception = False
def async_loop_exception_handler(_, _2) -> None:
"""Handle all exception inside the core loop."""
nonlocal exception
exception = True
hass.loop.set_exception_handler(async_loop_exception_handler)
await async_setup_component(hass, "group", {})
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
2018-11-29 22:26:06 +01:00
2019-04-30 18:20:38 +02:00
for _ in range(2):
2018-11-29 22:26:06 +01:00
hass.async_create_task(component.async_add_entities([MockEntity()]))
await hass.async_block_till_done()
assert not exception
2023-02-20 11:42:56 +01:00
async def test_extract_all_omit_entity_id(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test extract all with None and *."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities(
[MockEntity(name="test_1"), MockEntity(name="test_2")]
)
call = ServiceCall(hass, "test", "service")
2024-06-28 10:46:02 +02:00
assert (
sorted(
ent.entity_id for ent in await component.async_extract_from_service(call)
)
== []
2019-07-31 12:25:30 -07:00
)
2023-02-20 11:42:56 +01:00
async def test_extract_all_use_match_all(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test extract all with None and *."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities(
[MockEntity(name="test_1"), MockEntity(name="test_2")]
)
call = ServiceCall(hass, "test", "service", {"entity_id": "all"})
2024-06-28 10:46:02 +02:00
assert sorted(
ent.entity_id for ent in await component.async_extract_from_service(call)
2024-06-28 10:46:02 +02:00
) == ["test_domain.test_1", "test_domain.test_2"]
assert (
"Not passing an entity ID to a service to target all entities is deprecated"
) not in caplog.text
@pytest.mark.parametrize(
("schema", "service_data"),
[
({"some": str}, {"some": "data"}),
({}, {}),
(None, {}),
],
)
async def test_register_entity_service(
hass: HomeAssistant,
schema: dict | None,
service_data: dict,
) -> None:
"""Test registering an entity service and calling it."""
entity = MockEntity(entity_id=f"{DOMAIN}.entity")
calls = []
@callback
def appender(**kwargs):
calls.append(kwargs)
entity.async_called_by_service = appender
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities([entity])
component.async_register_entity_service(
"hello",
schema,
"async_called_by_service",
description_placeholders={"test_placeholder": "beer"},
)
descriptions = await async_get_all_descriptions(hass)
assert descriptions["test_domain"]["hello"]["description_placeholders"] == {
"test_placeholder": "beer"
}
with pytest.raises(vol.Invalid):
await hass.services.async_call(
DOMAIN,
"hello",
{"entity_id": entity.entity_id, "invalid": "data"},
blocking=True,
)
2024-06-08 17:59:08 +02:00
assert len(calls) == 0
await hass.services.async_call(
DOMAIN, "hello", {"entity_id": entity.entity_id} | service_data, blocking=True
)
assert len(calls) == 1
assert calls[0] == service_data
await hass.services.async_call(
DOMAIN, "hello", {"entity_id": ENTITY_MATCH_ALL} | service_data, blocking=True
)
assert len(calls) == 2
assert calls[1] == service_data
await hass.services.async_call(
DOMAIN, "hello", {"entity_id": ENTITY_MATCH_NONE} | service_data, blocking=True
)
assert len(calls) == 2
await hass.services.async_call(
DOMAIN, "hello", {"area_id": ENTITY_MATCH_NONE} | service_data, blocking=True
)
assert len(calls) == 2
async def test_register_entity_service_non_entity_service_schema(
hass: HomeAssistant,
) -> None:
"""Test attempting to register a service with a non entity service schema."""
component = EntityComponent(_LOGGER, DOMAIN, hass)
for idx, schema in enumerate(
(
vol.Schema({"some": str}),
vol.All(vol.Schema({"some": str})),
vol.Any(vol.Schema({"some": str})),
)
):
expected_message = (
f"The test_domain.hello_{idx} service registers "
"an entity service with a non entity service schema"
)
with pytest.raises(HomeAssistantError, match=expected_message):
component.async_register_entity_service(f"hello_{idx}", schema, Mock())
for idx, schema in enumerate(
(
cv.make_entity_service_schema({"some": str}),
vol.Schema(cv.make_entity_service_schema({"some": str})),
vol.All(cv.make_entity_service_schema({"some": str})),
)
):
component.async_register_entity_service(f"test_service_{idx}", schema, Mock())
async def test_register_entity_service_response_data(hass: HomeAssistant) -> None:
"""Test an entity service that does support response data."""
entity = MockEntity(entity_id=f"{DOMAIN}.entity")
async def generate_response(
target: MockEntity, call: ServiceCall
) -> ServiceResponse:
assert call.return_response
return {"response-key": "response-value"}
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities([entity])
component.async_register_entity_service(
"hello",
{"some": str},
generate_response,
supports_response=SupportsResponse.ONLY,
)
response_data = await hass.services.async_call(
DOMAIN,
"hello",
service_data={"some": "data"},
target={"entity_id": [entity.entity_id]},
blocking=True,
return_response=True,
)
assert response_data == {f"{DOMAIN}.entity": {"response-key": "response-value"}}
async def test_register_entity_service_response_data_multiple_matches(
hass: HomeAssistant,
) -> None:
"""Test asking for service response data and matching many entities."""
entity1 = MockEntity(entity_id=f"{DOMAIN}.entity1")
entity2 = MockEntity(entity_id=f"{DOMAIN}.entity2")
async def generate_response(
target: MockEntity, call: ServiceCall
) -> ServiceResponse:
return {"response-key": f"response-value-{target.entity_id}"}
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities([entity1, entity2])
component.async_register_entity_service(
"hello",
{"some": str},
generate_response,
supports_response=SupportsResponse.ONLY,
)
response_data = await hass.services.async_call(
DOMAIN,
"hello",
service_data={"some": "data"},
target={"entity_id": [entity1.entity_id, entity2.entity_id]},
blocking=True,
return_response=True,
)
assert response_data == {
f"{DOMAIN}.entity1": {"response-key": f"response-value-{DOMAIN}.entity1"},
f"{DOMAIN}.entity2": {"response-key": f"response-value-{DOMAIN}.entity2"},
}
async def test_register_entity_service_response_data_multiple_matches_raises(
hass: HomeAssistant,
) -> None:
"""Test asking for service response data and matching many entities raises exceptions."""
entity1 = MockEntity(entity_id=f"{DOMAIN}.entity1")
entity2 = MockEntity(entity_id=f"{DOMAIN}.entity2")
async def generate_response(
target: MockEntity, call: ServiceCall
) -> ServiceResponse:
if target.entity_id == f"{DOMAIN}.entity1":
raise RuntimeError("Something went wrong")
return {"response-key": f"response-value-{target.entity_id}"}
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({})
await component.async_add_entities([entity1, entity2])
component.async_register_entity_service(
"hello",
{"some": str},
generate_response,
supports_response=SupportsResponse.ONLY,
)
with pytest.raises(RuntimeError, match="Something went wrong"):
await hass.services.async_call(
DOMAIN,
"hello",
service_data={"some": "data"},
target={"entity_id": [entity1.entity_id, entity2.entity_id]},
blocking=True,
return_response=True,
)
async def test_platforms_shutdown_on_stop(hass: HomeAssistant) -> None:
"""Test that we shutdown platforms on stop."""
platform1_setup = Mock(side_effect=[PlatformNotReady, PlatformNotReady, None])
mock_integration(hass, MockModule("mod1"))
mock_platform(
hass, "mod1.test_domain", MockPlatform(setup_platform=platform1_setup)
)
component = EntityComponent(_LOGGER, DOMAIN, hass)
await component.async_setup({DOMAIN: {"platform": "mod1"}})
await hass.async_block_till_done()
assert len(platform1_setup.mock_calls) == 1
2023-12-05 08:43:58 +01:00
assert "mod1.test_domain" not in hass.config.components
with patch.object(
component._platforms[DOMAIN], "async_shutdown"
) as mock_async_shutdown:
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
await hass.async_block_till_done()
assert mock_async_shutdown.called