From 2df0adfbc74ce3e9d9bc16f39a3974c4a56b9275 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Wed, 29 Dec 2021 03:20:55 -0800 Subject: [PATCH] Reload nest integration when new devices are added (#62976) --- homeassistant/components/nest/__init__.py | 17 +++++- tests/components/nest/test_events.py | 63 +++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py index 3cad979165a..bb5edefc2ca 100644 --- a/homeassistant/components/nest/__init__.py +++ b/homeassistant/components/nest/__init__.py @@ -1,6 +1,7 @@ """Support for Nest devices.""" from __future__ import annotations +from collections.abc import Awaitable, Callable from http import HTTPStatus import logging @@ -178,12 +179,21 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: class SignalUpdateCallback: """An EventCallback invoked when new events arrive from subscriber.""" - def __init__(self, hass: HomeAssistant) -> None: + def __init__( + self, hass: HomeAssistant, config_reload_cb: Callable[[], Awaitable[None]] + ) -> None: """Initialize EventCallback.""" self._hass = hass + self._config_reload_cb = config_reload_cb async def async_handle_event(self, event_message: EventMessage) -> None: """Process an incoming EventMessage.""" + if event_message.relation_update: + # A device was added/removed or a home was added/removed. Reload the integration + # in order to detect any changes. + _LOGGER.info("Devices or homes have changed; Reloading") + self._hass.async_create_task(self._config_reload_cb()) + return if not event_message.resource_update_name: return device_id = event_message.resource_update_name @@ -221,7 +231,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Use disk backed event media store subscriber.cache_policy.store = await async_get_media_event_store(hass, subscriber) - callback = SignalUpdateCallback(hass) + async def async_config_reload() -> None: + await hass.config_entries.async_reload(entry.entry_id) + + callback = SignalUpdateCallback(hass, async_config_reload) subscriber.set_update_callback(callback.async_handle_event) try: await subscriber.start_async() diff --git a/tests/components/nest/test_events.py b/tests/components/nest/test_events.py index 4a625999155..0c4496603bd 100644 --- a/tests/components/nest/test_events.py +++ b/tests/components/nest/test_events.py @@ -5,6 +5,7 @@ pubsub subscriber. """ import datetime +from unittest.mock import patch from google_nest_sdm.device import Device from google_nest_sdm.event import EventMessage @@ -446,3 +447,65 @@ async def test_doorbell_event_session_update(hass): "timestamp": timestamp2.replace(microsecond=0), "nest_event_id": EVENT_SESSION_ID, } + + +async def test_structure_update_event(hass): + """Test a pubsub message for a new device being added.""" + events = async_capture_events(hass, NEST_EVENT) + subscriber = await async_setup_devices( + hass, + "sdm.devices.types.DOORBELL", + create_device_traits(["sdm.devices.traits.DoorbellChime"]), + ) + + # Entity for first device is registered + registry = er.async_get(hass) + assert registry.async_get("camera.front") + + new_device = Device.MakeDevice( + { + "name": "device-id-2", + "type": "sdm.devices.types.CAMERA", + "traits": { + "sdm.devices.traits.Info": { + "customName": "Back", + }, + "sdm.devices.traits.CameraLiveStream": {}, + }, + }, + auth=None, + ) + device_manager = await subscriber.async_get_device_manager() + device_manager.add_device(new_device) + + # Entity for new devie has not yet been loaded + assert not registry.async_get("camera.back") + + # Send a message that triggers the device to be loaded + message = EventMessage( + { + "eventId": "some-event-id", + "timestamp": utcnow().isoformat(timespec="seconds"), + "relationUpdate": { + "type": "CREATED", + "subject": "enterprise/example/foo", + "object": "enterprise/example/devices/some-device-id2", + }, + }, + auth=None, + ) + with patch( + "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation" + ), patch("homeassistant.components.nest.PLATFORMS", [PLATFORM]), patch( + "homeassistant.components.nest.api.GoogleNestSubscriber", + return_value=subscriber, + ): + await subscriber.async_receive_event(message) + await hass.async_block_till_done() + + # No home assistant events published + assert not events + + # Both enties now exist + assert registry.async_get("camera.front") + assert registry.async_get("camera.back")