Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions homeassistant/components/nest/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Support for Nest devices."""
from __future__ import annotations

from collections.abc import Awaitable, Callable
from http import HTTPStatus
import logging

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
63 changes: 63 additions & 0 deletions tests/components/nest/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import datetime
from unittest.mock import patch

from google_nest_sdm.device import Device
from google_nest_sdm.event import EventMessage
Expand Down Expand Up @@ -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")