From 8deefeadaf758fbeef044d11931d8a4acbc30832 Mon Sep 17 00:00:00 2001 From: Adam Weiss Date: Wed, 13 Oct 2021 15:37:08 -0400 Subject: [PATCH 1/3] #26998 Add device condition support to the camera integration --- .../components/camera/device_condition.py | 82 ++++++++++ homeassistant/components/camera/strings.json | 9 +- .../components/camera/translations/en.json | 7 + .../camera/test_device_condition.py | 140 ++++++++++++++++++ 4 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/camera/device_condition.py create mode 100644 tests/components/camera/test_device_condition.py diff --git a/homeassistant/components/camera/device_condition.py b/homeassistant/components/camera/device_condition.py new file mode 100644 index 00000000000000..c97b9d4cb6dcda --- /dev/null +++ b/homeassistant/components/camera/device_condition.py @@ -0,0 +1,82 @@ +"""Provide the device conditions for Camera.""" +from __future__ import annotations + +import voluptuous as vol + +from homeassistant.const import ( + ATTR_ENTITY_ID, + CONF_CONDITION, + CONF_DEVICE_ID, + CONF_DOMAIN, + CONF_ENTITY_ID, + CONF_TYPE, + STATE_IDLE, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import condition, config_validation as cv, entity_registry +from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA +from homeassistant.helpers.typing import ConfigType, TemplateVarsType + +from . import DOMAIN + +# TODO specify your supported condition types. +CONDITION_TYPES = { + "is_on", + "is_off", +} + +CONDITION_SCHEMA = DEVICE_CONDITION_BASE_SCHEMA.extend( + { + vol.Required(CONF_ENTITY_ID): cv.entity_id, + vol.Required(CONF_TYPE): vol.In(CONDITION_TYPES), + } +) + + +async def async_get_conditions( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: + """List device conditions for Camera devices.""" + registry = await entity_registry.async_get_registry(hass) + conditions = [] + + # Get all the integrations entities for this device + for entry in entity_registry.async_entries_for_device(registry, device_id): + if entry.domain != DOMAIN: + continue + + # Add conditions for each entity that belongs to this integration + base_condition = { + CONF_CONDITION: "device", + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + } + + conditions += [{**base_condition, CONF_TYPE: cond} for cond in CONDITION_TYPES] + + return conditions + + +@callback +def async_condition_from_config( + config: ConfigType, config_validation: bool +) -> condition.ConditionCheckerType: + """Create a function to test a device condition.""" + if config_validation: + config = CONDITION_SCHEMA(config) + if config[CONF_TYPE] == "is_on": + state = STATE_ON + elif config[CONF_TYPE] == "is_idle": + state = STATE_IDLE + else: + state = STATE_OFF + + @callback + def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: + """Test if an entity is a certain state.""" + return condition.state(hass, config[ATTR_ENTITY_ID], state) + + return test_is_state diff --git a/homeassistant/components/camera/strings.json b/homeassistant/components/camera/strings.json index 3b8767ec8cd785..001cb5b05549c3 100644 --- a/homeassistant/components/camera/strings.json +++ b/homeassistant/components/camera/strings.json @@ -6,5 +6,12 @@ "streaming": "Streaming", "idle": "[%key:common::state::idle%]" } + }, + "device_automation": { + "condition_type": { + "is_on": "{entity_name} is on", + "is_off": "{entity_name} is off", + "is_idle": "{entity_name} is idle" + } } -} +} \ No newline at end of file diff --git a/homeassistant/components/camera/translations/en.json b/homeassistant/components/camera/translations/en.json index f0e1ec40a9c926..489a8f22486829 100644 --- a/homeassistant/components/camera/translations/en.json +++ b/homeassistant/components/camera/translations/en.json @@ -1,4 +1,11 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} is off", + "is_on": "{entity_name} is on", + "is_idle": "{entity_name} is idle" + } + }, "state": { "_": { "idle": "Idle", diff --git a/tests/components/camera/test_device_condition.py b/tests/components/camera/test_device_condition.py new file mode 100644 index 00000000000000..c55ea340dfbbf0 --- /dev/null +++ b/tests/components/camera/test_device_condition.py @@ -0,0 +1,140 @@ +"""The tests for Camera device conditions.""" +from __future__ import annotations + +import pytest + +from homeassistant.components import automation +from homeassistant.components.camera import DOMAIN +from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers import device_registry, entity_registry +from homeassistant.setup import async_setup_component + +from tests.common import ( + MockConfigEntry, + assert_lists_same, + async_get_device_automations, + async_mock_service, + mock_device_registry, + mock_registry, +) + + +@pytest.fixture +def device_reg(hass: HomeAssistant) -> device_registry.DeviceRegistry: + """Return an empty, loaded, registry.""" + return mock_device_registry(hass) + + +@pytest.fixture +def entity_reg(hass: HomeAssistant) -> entity_registry.EntityRegistry: + """Return an empty, loaded, registry.""" + return mock_registry(hass) + + +@pytest.fixture +def calls(hass: HomeAssistant) -> list[ServiceCall]: + """Track calls to a mock service.""" + return async_mock_service(hass, "test", "automation") + + +async def test_get_conditions( + hass: HomeAssistant, + device_reg: device_registry.DeviceRegistry, + entity_reg: entity_registry.EntityRegistry, +) -> None: + """Test we get the expected conditions from a camera.""" + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) + expected_conditions = [ + { + "condition": "device", + "domain": DOMAIN, + "type": "is_off", + "device_id": device_entry.id, + "entity_id": f"{DOMAIN}.test_5678", + }, + { + "condition": "device", + "domain": DOMAIN, + "type": "is_on", + "device_id": device_entry.id, + "entity_id": f"{DOMAIN}.test_5678", + }, + ] + conditions = await async_get_device_automations(hass, "condition", device_entry.id) + assert_lists_same(conditions, expected_conditions) + + +async def test_if_state(hass: HomeAssistant, calls: list[ServiceCall]) -> None: + """Test for turn_on and turn_off conditions.""" + hass.states.async_set("camera.entity", STATE_ON) + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": {"platform": "event", "event_type": "test_event1"}, + "condition": [ + { + "condition": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": "camera.entity", + "type": "is_on", + } + ], + "action": { + "service": "test.automation", + "data_template": { + "some": "is_on - {{ trigger.platform }} - {{ trigger.event.event_type }}" + }, + }, + }, + { + "trigger": {"platform": "event", "event_type": "test_event2"}, + "condition": [ + { + "condition": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": "camera.entity", + "type": "is_off", + } + ], + "action": { + "service": "test.automation", + "data_template": { + "some": "is_off - {{ trigger.platform }} - {{ trigger.event.event_type }}" + }, + }, + }, + ] + }, + ) + hass.bus.async_fire("test_event1") + hass.bus.async_fire("test_event2") + await hass.async_block_till_done() + assert len(calls) == 1 + assert calls[0].data["some"] == "is_on - event - test_event1" + + hass.states.async_set("camera.entity", STATE_OFF) + hass.bus.async_fire("test_event1") + hass.bus.async_fire("test_event2") + await hass.async_block_till_done() + assert len(calls) == 2 + assert calls[1].data["some"] == "is_off - event - test_event2" + + hass.states.async_set("camera.entity", STATE_IDLE) + hass.bus.async_fire("test_event1") + hass.bus.async_fire("test_event2") + await hass.async_block_till_done() + assert len(calls) == 2 + assert calls[1].data["some"] == "is_off - event - test_event2" From 89901b62ec0c5d2fadfa6440b1c7fb7e0a800c27 Mon Sep 17 00:00:00 2001 From: Adam Weiss Date: Sun, 2 Oct 2022 14:07:30 -0400 Subject: [PATCH 2/3] Add device condition support to camera integration --- .../components/camera/device_condition.py | 27 +++---- homeassistant/components/camera/strings.json | 7 +- .../components/camera/translations/en.json | 4 +- .../camera/test_device_condition.py | 73 +++++++++++-------- 4 files changed, 58 insertions(+), 53 deletions(-) diff --git a/homeassistant/components/camera/device_condition.py b/homeassistant/components/camera/device_condition.py index c97b9d4cb6dcda..0c1b8d598a601f 100644 --- a/homeassistant/components/camera/device_condition.py +++ b/homeassistant/components/camera/device_condition.py @@ -10,22 +10,15 @@ CONF_DOMAIN, CONF_ENTITY_ID, CONF_TYPE, - STATE_IDLE, - STATE_OFF, - STATE_ON, ) from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import condition, config_validation as cv, entity_registry from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA from homeassistant.helpers.typing import ConfigType, TemplateVarsType -from . import DOMAIN +from . import DOMAIN, STATE_IDLE, STATE_RECORDING, STATE_STREAMING -# TODO specify your supported condition types. -CONDITION_TYPES = { - "is_on", - "is_off", -} +CONDITION_TYPES = {"is_recording", "is_streaming", "is_idle"} CONDITION_SCHEMA = DEVICE_CONDITION_BASE_SCHEMA.extend( { @@ -62,17 +55,17 @@ async def async_get_conditions( @callback def async_condition_from_config( - config: ConfigType, config_validation: bool + hass: HomeAssistant, config: ConfigType ) -> condition.ConditionCheckerType: """Create a function to test a device condition.""" - if config_validation: - config = CONDITION_SCHEMA(config) - if config[CONF_TYPE] == "is_on": - state = STATE_ON - elif config[CONF_TYPE] == "is_idle": - state = STATE_IDLE + # if config_validation: + # config = CONDITION_SCHEMA(config) + if config[CONF_TYPE] == "is_recording": + state = STATE_RECORDING + elif config[CONF_TYPE] == "is_streaming": + state = STATE_STREAMING else: - state = STATE_OFF + state = STATE_IDLE @callback def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: diff --git a/homeassistant/components/camera/strings.json b/homeassistant/components/camera/strings.json index 001cb5b05549c3..231ef13b56aab8 100644 --- a/homeassistant/components/camera/strings.json +++ b/homeassistant/components/camera/strings.json @@ -9,9 +9,8 @@ }, "device_automation": { "condition_type": { - "is_on": "{entity_name} is on", - "is_off": "{entity_name} is off", - "is_idle": "{entity_name} is idle" + "is_recording": "{entity_name} is recording", + "is_streaming": "{entity_name} is streaming" } } -} \ No newline at end of file +} diff --git a/homeassistant/components/camera/translations/en.json b/homeassistant/components/camera/translations/en.json index 489a8f22486829..4795d8f271349b 100644 --- a/homeassistant/components/camera/translations/en.json +++ b/homeassistant/components/camera/translations/en.json @@ -1,8 +1,8 @@ { "device_automation": { "condition_type": { - "is_off": "{entity_name} is off", - "is_on": "{entity_name} is on", + "is_recording": "{entity_name} is recording", + "is_streaming": "{entity_name} is streaming", "is_idle": "{entity_name} is idle" } }, diff --git a/tests/components/camera/test_device_condition.py b/tests/components/camera/test_device_condition.py index c55ea340dfbbf0..61625577c8ef9c 100644 --- a/tests/components/camera/test_device_condition.py +++ b/tests/components/camera/test_device_condition.py @@ -3,9 +3,9 @@ import pytest -from homeassistant.components import automation +from homeassistant.components import automation, camera from homeassistant.components.camera import DOMAIN -from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_ON +from homeassistant.components.device_automation import DeviceAutomationType from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import device_registry, entity_registry from homeassistant.setup import async_setup_component @@ -55,31 +55,47 @@ async def test_get_conditions( { "condition": "device", "domain": DOMAIN, - "type": "is_off", + "type": condition, "device_id": device_entry.id, "entity_id": f"{DOMAIN}.test_5678", - }, - { - "condition": "device", - "domain": DOMAIN, - "type": "is_on", - "device_id": device_entry.id, - "entity_id": f"{DOMAIN}.test_5678", - }, + "metadata": {"secondary": False}, + } + for condition in ["is_recording", "is_streaming"] ] - conditions = await async_get_device_automations(hass, "condition", device_entry.id) + conditions = await async_get_device_automations( + hass, DeviceAutomationType.CONDITION, device_entry.id + ) assert_lists_same(conditions, expected_conditions) async def test_if_state(hass: HomeAssistant, calls: list[ServiceCall]) -> None: - """Test for turn_on and turn_off conditions.""" - hass.states.async_set("camera.entity", STATE_ON) + """Test for is_recording and is_streaming conditions.""" + camera_entity = "camera.entity" + hass.states.async_set(camera_entity, camera.STATE_IDLE) assert await async_setup_component( hass, automation.DOMAIN, { automation.DOMAIN: [ + { + "trigger": {"platform": "event", "event_type": "test_event0"}, + "condition": [ + { + "condition": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": camera_entity, + "type": "is_idle", + } + ], + "action": { + "service": "test.automation", + "data_template": { + "some": "is_idle - {{ trigger.platform }} - {{ trigger.event.event_type }}" + }, + }, + }, { "trigger": {"platform": "event", "event_type": "test_event1"}, "condition": [ @@ -87,14 +103,14 @@ async def test_if_state(hass: HomeAssistant, calls: list[ServiceCall]) -> None: "condition": "device", "domain": DOMAIN, "device_id": "", - "entity_id": "camera.entity", - "type": "is_on", + "entity_id": camera_entity, + "type": "is_recording", } ], "action": { "service": "test.automation", "data_template": { - "some": "is_on - {{ trigger.platform }} - {{ trigger.event.event_type }}" + "some": "is_recording - {{ trigger.platform }} - {{ trigger.event.event_type }}" }, }, }, @@ -105,36 +121,33 @@ async def test_if_state(hass: HomeAssistant, calls: list[ServiceCall]) -> None: "condition": "device", "domain": DOMAIN, "device_id": "", - "entity_id": "camera.entity", - "type": "is_off", + "entity_id": camera_entity, + "type": "is_streaming", } ], "action": { "service": "test.automation", "data_template": { - "some": "is_off - {{ trigger.platform }} - {{ trigger.event.event_type }}" + "some": "is_streaming - {{ trigger.platform }} - {{ trigger.event.event_type }}" }, }, }, ] }, ) - hass.bus.async_fire("test_event1") - hass.bus.async_fire("test_event2") + hass.bus.async_fire("test_event0") await hass.async_block_till_done() assert len(calls) == 1 - assert calls[0].data["some"] == "is_on - event - test_event1" + assert calls[0].data["some"] == "is_idle - event - test_event0" - hass.states.async_set("camera.entity", STATE_OFF) + hass.states.async_set(camera_entity, camera.STATE_RECORDING) hass.bus.async_fire("test_event1") - hass.bus.async_fire("test_event2") await hass.async_block_till_done() assert len(calls) == 2 - assert calls[1].data["some"] == "is_off - event - test_event2" + assert calls[1].data["some"] == "is_recording - event - test_event1" - hass.states.async_set("camera.entity", STATE_IDLE) - hass.bus.async_fire("test_event1") + hass.states.async_set(camera_entity, camera.STATE_STREAMING) hass.bus.async_fire("test_event2") await hass.async_block_till_done() - assert len(calls) == 2 - assert calls[1].data["some"] == "is_off - event - test_event2" + assert len(calls) == 3 + assert calls[2].data["some"] == "is_streaming - event - test_event2" From 03993815161efca095fc2465610aa93ddc8897e5 Mon Sep 17 00:00:00 2001 From: Adam Weiss Date: Sun, 2 Oct 2022 14:54:29 -0400 Subject: [PATCH 3/3] Add calendar device condition support --- .../components/calendar/device_condition.py | 72 +++++++++ .../components/calendar/strings.json | 6 + .../components/calendar/translations/en.json | 6 + .../calendar/test_device_condition.py | 138 ++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 homeassistant/components/calendar/device_condition.py create mode 100644 tests/components/calendar/test_device_condition.py diff --git a/homeassistant/components/calendar/device_condition.py b/homeassistant/components/calendar/device_condition.py new file mode 100644 index 00000000000000..717a849d10825c --- /dev/null +++ b/homeassistant/components/calendar/device_condition.py @@ -0,0 +1,72 @@ +"""Provide the device conditions for Calendar.""" +from __future__ import annotations + +import voluptuous as vol + +from homeassistant.const import ( + ATTR_ENTITY_ID, + CONF_CONDITION, + CONF_DEVICE_ID, + CONF_DOMAIN, + CONF_ENTITY_ID, + CONF_TYPE, + STATE_OFF, + STATE_ON, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import condition, config_validation as cv, entity_registry +from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA +from homeassistant.helpers.typing import ConfigType, TemplateVarsType + +from . import DOMAIN + +CONDITION_TYPES = {"is_on", "is_off"} + +CONDITION_SCHEMA = DEVICE_CONDITION_BASE_SCHEMA.extend( + { + vol.Required(CONF_ENTITY_ID): cv.entity_id, + vol.Required(CONF_TYPE): vol.In(CONDITION_TYPES), + } +) + + +async def async_get_conditions( + hass: HomeAssistant, device_id: str +) -> list[dict[str, str]]: + """List device conditions for Calendar devices.""" + registry = entity_registry.async_get(hass) + conditions = [] + + # Get all the integrations entities for this device + for entry in entity_registry.async_entries_for_device(registry, device_id): + if entry.domain != DOMAIN: + continue + + base_condition = { + CONF_CONDITION: "device", + CONF_DEVICE_ID: device_id, + CONF_DOMAIN: DOMAIN, + CONF_ENTITY_ID: entry.entity_id, + } + + conditions += [{**base_condition, CONF_TYPE: cond} for cond in CONDITION_TYPES] + + return conditions + + +@callback +def async_condition_from_config( + hass: HomeAssistant, config: ConfigType +) -> condition.ConditionCheckerType: + """Create a function to test a device condition.""" + if config[CONF_TYPE] == "is_on": + state = STATE_ON + else: + state = STATE_OFF + + @callback + def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool: + """Test if an entity is a certain state.""" + return condition.state(hass, config[ATTR_ENTITY_ID], state) + + return test_is_state diff --git a/homeassistant/components/calendar/strings.json b/homeassistant/components/calendar/strings.json index 3af9a78e60717d..3d8603daa06fa7 100644 --- a/homeassistant/components/calendar/strings.json +++ b/homeassistant/components/calendar/strings.json @@ -5,5 +5,11 @@ "off": "[%key:common::state::off%]", "on": "[%key:common::state::on%]" } + }, + "device_automation": { + "condition_type": { + "is_on": "{entity_name} is on", + "is_off": "{entity_name} is off" + } } } diff --git a/homeassistant/components/calendar/translations/en.json b/homeassistant/components/calendar/translations/en.json index 1a454c483cdc74..82ad9058394edc 100644 --- a/homeassistant/components/calendar/translations/en.json +++ b/homeassistant/components/calendar/translations/en.json @@ -1,4 +1,10 @@ { + "device_automation": { + "condition_type": { + "is_off": "{entity_name} is off", + "is_on": "{entity_name} is on" + } + }, "state": { "_": { "off": "Off", diff --git a/tests/components/calendar/test_device_condition.py b/tests/components/calendar/test_device_condition.py new file mode 100644 index 00000000000000..30e4e95f7844b7 --- /dev/null +++ b/tests/components/calendar/test_device_condition.py @@ -0,0 +1,138 @@ +"""The tests for Calendar device conditions.""" +from __future__ import annotations + +import pytest + +from homeassistant.components import automation +from homeassistant.components.calendar import DOMAIN +from homeassistant.components.device_automation import DeviceAutomationType +from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.core import HomeAssistant, ServiceCall +from homeassistant.helpers import device_registry, entity_registry +from homeassistant.setup import async_setup_component + +from tests.common import ( + MockConfigEntry, + assert_lists_same, + async_get_device_automations, + async_mock_service, + mock_device_registry, + mock_registry, +) + + +@pytest.fixture +def device_reg(hass: HomeAssistant) -> device_registry.DeviceRegistry: + """Return an empty, loaded, registry.""" + return mock_device_registry(hass) + + +@pytest.fixture +def entity_reg(hass: HomeAssistant) -> entity_registry.EntityRegistry: + """Return an empty, loaded, registry.""" + return mock_registry(hass) + + +@pytest.fixture +def calls(hass: HomeAssistant) -> list[ServiceCall]: + """Track calls to a mock service.""" + return async_mock_service(hass, "test", "automation") + + +async def test_get_conditions( + hass: HomeAssistant, + device_reg: device_registry.DeviceRegistry, + entity_reg: entity_registry.EntityRegistry, +) -> None: + """Test we get the expected conditions from a calendar.""" + config_entry = MockConfigEntry(domain="test", data={}) + config_entry.add_to_hass(hass) + device_entry = device_reg.async_get_or_create( + config_entry_id=config_entry.entry_id, + connections={(device_registry.CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")}, + ) + entity_reg.async_get_or_create(DOMAIN, "test", "5678", device_id=device_entry.id) + expected_conditions = [ + { + "condition": "device", + "domain": DOMAIN, + "type": "is_off", + "device_id": device_entry.id, + "entity_id": f"{DOMAIN}.test_5678", + "metadata": {"secondary": False}, + }, + { + "condition": "device", + "domain": DOMAIN, + "type": "is_on", + "device_id": device_entry.id, + "entity_id": f"{DOMAIN}.test_5678", + "metadata": {"secondary": False}, + }, + ] + conditions = await async_get_device_automations( + hass, DeviceAutomationType.CONDITION, device_entry.id + ) + assert_lists_same(conditions, expected_conditions) + + +async def test_if_state(hass: HomeAssistant, calls: list[ServiceCall]) -> None: + """Test for turn_on and turn_off conditions.""" + hass.states.async_set("calendar.entity", STATE_ON) + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": {"platform": "event", "event_type": "test_event1"}, + "condition": [ + { + "condition": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": "calendar.entity", + "type": "is_on", + } + ], + "action": { + "service": "test.automation", + "data_template": { + "some": "is_on - {{ trigger.platform }} - {{ trigger.event.event_type }}" + }, + }, + }, + { + "trigger": {"platform": "event", "event_type": "test_event2"}, + "condition": [ + { + "condition": "device", + "domain": DOMAIN, + "device_id": "", + "entity_id": "calendar.entity", + "type": "is_off", + } + ], + "action": { + "service": "test.automation", + "data_template": { + "some": "is_off - {{ trigger.platform }} - {{ trigger.event.event_type }}" + }, + }, + }, + ] + }, + ) + hass.bus.async_fire("test_event1") + hass.bus.async_fire("test_event2") + await hass.async_block_till_done() + assert len(calls) == 1 + assert calls[0].data["some"] == "is_on - event - test_event1" + + hass.states.async_set("calendar.entity", STATE_OFF) + hass.bus.async_fire("test_event1") + hass.bus.async_fire("test_event2") + await hass.async_block_till_done() + assert len(calls) == 2 + assert calls[1].data["some"] == "is_off - event - test_event2"