diff --git a/homeassistant/components/alexa/state_report.py b/homeassistant/components/alexa/state_report.py index 9c640d76dd48d..978879b5d8865 100644 --- a/homeassistant/components/alexa/state_report.py +++ b/homeassistant/components/alexa/state_report.py @@ -13,10 +13,16 @@ import aiohttp from homeassistant.components import event -from homeassistant.const import MATCH_ALL, STATE_ON -from homeassistant.core import CALLBACK_TYPE, HomeAssistant, State, callback +from homeassistant.const import EVENT_STATE_CHANGED, STATE_ON +from homeassistant.core import ( + CALLBACK_TYPE, + Event, + EventStateChangedData, + HomeAssistant, + State, + callback, +) from homeassistant.helpers.aiohttp_client import async_get_clientsession -from homeassistant.helpers.event import async_track_state_change from homeassistant.helpers.significant_change import create_checker import homeassistant.util.dt as dt_util from homeassistant.util.json import JsonObjectType, json_loads_object @@ -265,28 +271,35 @@ def extra_significant_check( checker = await create_checker(hass, DOMAIN, extra_significant_check) - async def async_entity_state_listener( - changed_entity: str, - old_state: State | None, - new_state: State | None, - ) -> None: + @callback + def _async_entity_state_filter(data: EventStateChangedData) -> bool: if not hass.is_running: - return + return False - if not new_state: - return + if not (new_state := data["new_state"]): + return False if new_state.domain not in ENTITY_ADAPTERS: - return + return False + changed_entity = data["entity_id"] if not smart_home_config.should_expose(changed_entity): _LOGGER.debug("Not exposing %s because filtered by config", changed_entity) - return + return False + + return True + + async def _async_entity_state_listener( + event_: Event[EventStateChangedData], + ) -> None: + data = event_.data + new_state = data["new_state"] + if TYPE_CHECKING: + assert new_state is not None alexa_changed_entity: AlexaEntity = ENTITY_ADAPTERS[new_state.domain]( hass, smart_home_config, new_state ) - # Determine how entity should be reported on should_report = False should_doorbell = False @@ -303,6 +316,7 @@ async def async_entity_state_listener( return if should_doorbell: + old_state = data["old_state"] if ( new_state.domain == event.DOMAIN or new_state.state == STATE_ON @@ -324,7 +338,12 @@ async def async_entity_state_listener( hass, smart_home_config, alexa_changed_entity, alexa_properties ) - return async_track_state_change(hass, MATCH_ALL, async_entity_state_listener) + return hass.bus.async_listen( + EVENT_STATE_CHANGED, + _async_entity_state_listener, + event_filter=_async_entity_state_filter, + run_immediately=True, + ) async def async_send_changereport_message( diff --git a/tests/components/alexa/test_state_report.py b/tests/components/alexa/test_state_report.py index 6bd7caccc38b6..92410ae9de9bf 100644 --- a/tests/components/alexa/test_state_report.py +++ b/tests/components/alexa/test_state_report.py @@ -185,14 +185,14 @@ async def test_report_state_unsets_authorized_on_error( config = get_default_config(hass) await state_report.async_enable_proactive_mode(hass, config) + config._store.set_authorized.assert_not_called() + hass.states.async_set( "binary_sensor.test_contact", "off", {"friendly_name": "Test Contact Sensor", "device_class": "door"}, ) - config._store.set_authorized.assert_not_called() - # To trigger event listener await hass.async_block_till_done() config._store.set_authorized.assert_called_once_with(False) @@ -215,15 +215,15 @@ async def test_report_state_unsets_authorized_on_access_token_error( await state_report.async_enable_proactive_mode(hass, config) - hass.states.async_set( - "binary_sensor.test_contact", - "off", - {"friendly_name": "Test Contact Sensor", "device_class": "door"}, - ) - config._store.set_authorized.assert_not_called() with patch.object(config, "async_get_access_token", AsyncMock(side_effect=exc)): + hass.states.async_set( + "binary_sensor.test_contact", + "off", + {"friendly_name": "Test Contact Sensor", "device_class": "door"}, + ) + # To trigger event listener await hass.async_block_till_done() config._store.set_authorized.assert_called_once_with(False) @@ -731,39 +731,39 @@ async def test_proactive_mode_filter_states( assert len(aioclient_mock.mock_calls) == 0 # hass not running should not report + current_state = hass.state + hass.set_state(core.CoreState.stopping) + await hass.async_block_till_done() + await hass.async_block_till_done() hass.states.async_set( "binary_sensor.test_contact", "off", {"friendly_name": "Test Contact Sensor", "device_class": "door"}, ) - current_state = hass.state - hass.set_state(core.CoreState.stopping) - await hass.async_block_till_done() - await hass.async_block_till_done() hass.set_state(current_state) assert len(aioclient_mock.mock_calls) == 0 # unsupported entity should not report - hass.states.async_set( - "binary_sensor.test_contact", - "on", - {"friendly_name": "Test Contact Sensor", "device_class": "door"}, - ) with patch.dict( "homeassistant.components.alexa.state_report.ENTITY_ADAPTERS", {}, clear=True ): + hass.states.async_set( + "binary_sensor.test_contact", + "on", + {"friendly_name": "Test Contact Sensor", "device_class": "door"}, + ) await hass.async_block_till_done() await hass.async_block_till_done() assert len(aioclient_mock.mock_calls) == 0 # Not exposed by config should not report - hass.states.async_set( - "binary_sensor.test_contact", - "off", - {"friendly_name": "Test Contact Sensor", "device_class": "door"}, - ) with patch.object(config, "should_expose", return_value=False): + hass.states.async_set( + "binary_sensor.test_contact", + "off", + {"friendly_name": "Test Contact Sensor", "device_class": "door"}, + ) await hass.async_block_till_done() await hass.async_block_till_done() assert len(aioclient_mock.mock_calls) == 0