Add an event_filter to google_assistant state reporting (#115160)

* adjust

* fix test since it happens sooner now

* remove debug

* remove unneeded test change

* reduce

* reduce
This commit is contained in:
J. Nick Koston 2024-04-07 15:30:03 -10:00 committed by GitHub
parent 89a2c89fe2
commit af1023074e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 29 deletions

View File

@ -4,12 +4,19 @@ from __future__ import annotations
from collections import deque from collections import deque
import logging import logging
from typing import Any from typing import TYPE_CHECKING, Any
from uuid import uuid4 from uuid import uuid4
from homeassistant.const import MATCH_ALL from homeassistant.const import EVENT_STATE_CHANGED
from homeassistant.core import CALLBACK_TYPE, HassJob, HomeAssistant, State, callback from homeassistant.core import (
from homeassistant.helpers.event import async_call_later, async_track_state_change CALLBACK_TYPE,
Event,
EventStateChangedData,
HassJob,
HomeAssistant,
callback,
)
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.significant_change import create_checker from homeassistant.helpers.significant_change import create_checker
from .const import DOMAIN from .const import DOMAIN
@ -31,7 +38,9 @@ _LOGGER = logging.getLogger(__name__)
@callback @callback
def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig): def async_enable_report_state(
hass: HomeAssistant, google_config: AbstractConfig
) -> CALLBACK_TYPE:
"""Enable state and notification reporting.""" """Enable state and notification reporting."""
checker = None checker = None
unsub_pending: CALLBACK_TYPE | None = None unsub_pending: CALLBACK_TYPE | None = None
@ -60,33 +69,36 @@ def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig
report_states_job = HassJob(report_states) report_states_job = HassJob(report_states)
async def async_entity_state_listener( @callback
changed_entity: str, old_state: State | None, new_state: State | None def _async_entity_state_filter(data: EventStateChangedData) -> bool:
) -> None: return bool(
nonlocal unsub_pending, checker hass.is_running
and (new_state := data["new_state"])
if not hass.is_running: and google_config.should_expose(new_state)
return and async_get_google_entity_if_supported_cached(
if not new_state:
return
if not google_config.should_expose(new_state):
return
if not (
entity := async_get_google_entity_if_supported_cached(
hass, google_config, new_state hass, google_config, new_state
) )
): )
return
async def _async_entity_state_listener(event: Event[EventStateChangedData]) -> None:
"""Handle state changes."""
nonlocal unsub_pending, checker
data = event.data
new_state = data["new_state"]
if TYPE_CHECKING:
assert new_state is not None # verified in filter
entity = async_get_google_entity_if_supported_cached(
hass, google_config, new_state
)
if TYPE_CHECKING:
assert entity is not None # verified in filter
# We only trigger notifications on changes in the state value, not attributes. # We only trigger notifications on changes in the state value, not attributes.
# This is mainly designed for our event entity types # This is mainly designed for our event entity types
# We need to synchronize notifications using a `SYNC` response, # We need to synchronize notifications using a `SYNC` response,
# together with other state changes. # together with other state changes.
if ( if (
old_state (old_state := data["old_state"])
and old_state.state != new_state.state and old_state.state != new_state.state
and (notifications := entity.notifications_serialize()) is not None and (notifications := entity.notifications_serialize()) is not None
): ):
@ -106,6 +118,7 @@ def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig
result, result,
) )
changed_entity = data["entity_id"]
try: try:
entity_data = entity.query_serialize() entity_data = entity.query_serialize()
except SmartHomeError as err: except SmartHomeError as err:
@ -173,7 +186,12 @@ def async_enable_report_state(hass: HomeAssistant, google_config: AbstractConfig
await google_config.async_report_state_all({"devices": {"states": entities}}) await google_config.async_report_state_all({"devices": {"states": entities}})
unsub = async_track_state_change(hass, MATCH_ALL, async_entity_state_listener) unsub = hass.bus.async_listen(
EVENT_STATE_CHANGED,
_async_entity_state_listener,
event_filter=_async_entity_state_filter,
run_immediately=True,
)
unsub = async_call_later( unsub = async_call_later(
hass, INITIAL_REPORT_DELAY, HassJob(initial_report, cancel_on_shutdown=True) hass, INITIAL_REPORT_DELAY, HassJob(initial_report, cancel_on_shutdown=True)

View File

@ -216,6 +216,10 @@ async def test_report_notifications(
hass, datetime.fromisoformat("2023-08-01T01:01:00+00:00") hass, datetime.fromisoformat("2023-08-01T01:01:00+00:00")
) )
await hass.async_block_till_done() await hass.async_block_till_done()
for call in mock_report_state.mock_calls:
if "states" in call[1][0]["devices"]:
states = call[1][0]["devices"]["states"]
assert states["event.doorbell"] == {"online": True}
# Test the notification request failed # Test the notification request failed
caplog.clear() caplog.clear()
@ -233,12 +237,10 @@ async def test_report_notifications(
hass, datetime.fromisoformat("2023-08-01T01:03:00+00:00") hass, datetime.fromisoformat("2023-08-01T01:03:00+00:00")
) )
await hass.async_block_till_done() await hass.async_block_till_done()
assert len(mock_report_state.mock_calls) == 2 assert len(mock_report_state.mock_calls) == 1
for call in mock_report_state.mock_calls: for call in mock_report_state.mock_calls:
if "notifications" in call[1][0]["devices"]: if "notifications" in call[1][0]["devices"]:
notifications = call[1][0]["devices"]["notifications"] notifications = call[1][0]["devices"]["notifications"]
elif "states" in call[1][0]["devices"]:
states = call[1][0]["devices"]["states"]
assert notifications["event.doorbell"] == { assert notifications["event.doorbell"] == {
"ObjectDetection": { "ObjectDetection": {
"objects": {"unclassified": 1}, "objects": {"unclassified": 1},
@ -246,7 +248,6 @@ async def test_report_notifications(
"detectionTimestamp": epoc_event_time * 1000, "detectionTimestamp": epoc_event_time * 1000,
} }
} }
assert states["event.doorbell"] == {"online": True}
assert "Sending event notification for entity event.doorbell" in caplog.text assert "Sending event notification for entity event.doorbell" in caplog.text
assert ( assert (
"Unable to send notification with result code: 500, check log for more info" "Unable to send notification with result code: 500, check log for more info"