From 769578dc5179a269d80f4c8b0315130c4e4959d8 Mon Sep 17 00:00:00 2001 From: Raphael Hehl <7577984+RaHehl@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:22:39 +0100 Subject: [PATCH] UnifiProtect: Create NVR device before loading platforms to fix via_device references (#158191) --- .../components/unifiprotect/__init__.py | 16 +++++++++ .../unifiprotect/snapshots/test_init.ambr | 36 +++++++++++++++++++ tests/components/unifiprotect/test_init.py | 25 +++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 tests/components/unifiprotect/snapshots/test_init.ambr diff --git a/homeassistant/components/unifiprotect/__init__.py b/homeassistant/components/unifiprotect/__init__.py index e68953d9582..d465429db36 100644 --- a/homeassistant/components/unifiprotect/__init__.py +++ b/homeassistant/components/unifiprotect/__init__.py @@ -158,6 +158,22 @@ async def _async_setup_entry( ) -> None: await async_migrate_data(hass, entry, data_service.api, bootstrap) data_service.async_setup() + + # Create the NVR device before loading platforms + # This ensures via_device references work for all device entities + nvr = bootstrap.nvr + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + connections={(dr.CONNECTION_NETWORK_MAC, nvr.mac)}, + identifiers={(DOMAIN, nvr.mac)}, + manufacturer="Ubiquiti", + name=nvr.display_name, + model=nvr.type, + sw_version=str(nvr.version), + configuration_url=nvr.api.base_url, + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) hass.http.register_view(ThumbnailProxyView(hass)) hass.http.register_view(SnapshotProxyView(hass)) diff --git a/tests/components/unifiprotect/snapshots/test_init.ambr b/tests/components/unifiprotect/snapshots/test_init.ambr new file mode 100644 index 00000000000..49c113e856c --- /dev/null +++ b/tests/components/unifiprotect/snapshots/test_init.ambr @@ -0,0 +1,36 @@ +# serializer version: 1 +# name: test_setup_creates_nvr_device + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'config_entries_subentries': , + 'configuration_url': 'https://127.0.0.1', + 'connections': set({ + tuple( + 'mac', + 'a1:e0:0c:82:69:24', + ), + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'unifiprotect', + 'A1E00C826924', + ), + }), + 'labels': set({ + }), + 'manufacturer': 'Ubiquiti', + 'model': 'UNVR-PRO', + 'model_id': None, + 'name': 'UnifiProtect', + 'name_by_user': None, + 'primary_config_entry': , + 'serial_number': None, + 'sw_version': '6.0.0', + 'via_device_id': None, + }) +# --- diff --git a/tests/components/unifiprotect/test_init.py b/tests/components/unifiprotect/test_init.py index 17f67acdee6..c048a01a84f 100644 --- a/tests/components/unifiprotect/test_init.py +++ b/tests/components/unifiprotect/test_init.py @@ -5,6 +5,7 @@ from __future__ import annotations from unittest.mock import AsyncMock, Mock, patch import pytest +from syrupy.assertion import SnapshotAssertion from uiprotect import NvrError, ProtectApiClient from uiprotect.api import DEVICE_UPDATE_INTERVAL from uiprotect.data import NVR, Bootstrap, CloudAccount, Light @@ -44,6 +45,30 @@ def mock_user_can_write_nvr(request: pytest.FixtureRequest, ufp: MockUFPFixture) object.__setattr__(ufp.api.bootstrap.nvr, "can_write", original_can_write) +async def test_setup_creates_nvr_device( + hass: HomeAssistant, + device_registry: dr.DeviceRegistry, + ufp: MockUFPFixture, + snapshot: SnapshotAssertion, +) -> None: + """Test that setup creates the NVR device before loading platforms. + + This ensures that via_device references from camera/sensor entities + to the NVR device work correctly. + """ + await hass.config_entries.async_setup(ufp.entry.entry_id) + await hass.async_block_till_done() + + assert ufp.entry.state is ConfigEntryState.LOADED + + # Verify NVR device was created + nvr = ufp.api.bootstrap.nvr + nvr_device = device_registry.async_get_device( + identifiers={(DOMAIN, nvr.mac)}, + ) + assert nvr_device == snapshot + + async def test_setup(hass: HomeAssistant, ufp: MockUFPFixture) -> None: """Test working setup of unifiprotect entry."""