From e2ee623d23bab84118c40aadbda9c284a6cd29a2 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 25 Mar 2024 19:16:50 +0100 Subject: [PATCH] Add restrictions for listening to event_reported events (#114183) * Add restrictions for listening to event_reported events * Update homeassistant/core.py Co-authored-by: Martin Hjelmare --------- Co-authored-by: Martin Hjelmare --- homeassistant/core.py | 9 +++++++++ tests/test_core.py | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/homeassistant/core.py b/homeassistant/core.py index 533bddb6ceb..561cb45d39f 100644 --- a/homeassistant/core.py +++ b/homeassistant/core.py @@ -1511,6 +1511,15 @@ class EventBus: """ if event_filter is not None and not is_callback_check_partial(event_filter): raise HomeAssistantError(f"Event filter {event_filter} is not a callback") + if event_type == EVENT_STATE_REPORTED: + if not event_filter: + raise HomeAssistantError( + f"Event filter is required for event {event_type}" + ) + if not run_immediately: + raise HomeAssistantError( + f"Run immediately must be set to True for event {event_type}" + ) return self._async_listen_filterable_job( event_type, ( diff --git a/tests/test_core.py b/tests/test_core.py index 61852c69e8b..ab6f0b11270 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3262,9 +3262,22 @@ async def test_eventbus_lazy_object_creation(hass: HomeAssistant) -> None: async def test_statemachine_report_state(hass: HomeAssistant) -> None: """Test report state event.""" + + @ha.callback + def filter(event_data): + """Mock filter.""" + return True + + @callback + def listener(event: ha.Event) -> None: + state_reported_events.append(event) + hass.states.async_set("light.bowl", "on", {}) state_changed_events = async_capture_events(hass, EVENT_STATE_CHANGED) - state_reported_events = async_capture_events(hass, EVENT_STATE_REPORTED) + state_reported_events = [] + hass.bus.async_listen( + EVENT_STATE_REPORTED, listener, event_filter=filter, run_immediately=True + ) hass.states.async_set("light.bowl", "on") await hass.async_block_till_done() @@ -3285,3 +3298,29 @@ async def test_statemachine_report_state(hass: HomeAssistant) -> None: await hass.async_block_till_done() assert len(state_changed_events) == 3 assert len(state_reported_events) == 4 + + +async def test_report_state_listener_restrictions(hass: HomeAssistant) -> None: + """Test we enforce requirements for EVENT_STATE_REPORTED listeners.""" + + @ha.callback + def listener(event): + """Mock listener.""" + + @ha.callback + def filter(event_data): + """Mock filter.""" + return False + + # run_immediately not set + with pytest.raises(HomeAssistantError): + hass.bus.async_listen(EVENT_STATE_REPORTED, listener, event_filter=filter) + + # no filter + with pytest.raises(HomeAssistantError): + hass.bus.async_listen(EVENT_STATE_REPORTED, listener, run_immediately=True) + + # Both filter and run_immediately + hass.bus.async_listen( + EVENT_STATE_REPORTED, listener, event_filter=filter, run_immediately=True + )