diff --git a/homeassistant/components/tedee/__init__.py b/homeassistant/components/tedee/__init__.py index 1eb6b7a0333..eeb0f8e0d5a 100644 --- a/homeassistant/components/tedee/__init__.py +++ b/homeassistant/components/tedee/__init__.py @@ -4,6 +4,7 @@ import logging from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from .const import DOMAIN from .coordinator import TedeeApiCoordinator @@ -24,6 +25,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await coordinator.async_config_entry_first_refresh() + device_registry = dr.async_get(hass) + device_registry.async_get_or_create( + config_entry_id=entry.entry_id, + identifiers={(DOMAIN, coordinator.bridge.serial)}, + manufacturer="Tedee", + name=coordinator.bridge.name, + model="Bridge", + serial_number=coordinator.bridge.serial, + ) + hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) diff --git a/homeassistant/components/tedee/coordinator.py b/homeassistant/components/tedee/coordinator.py index 18fca035532..90539f881c3 100644 --- a/homeassistant/components/tedee/coordinator.py +++ b/homeassistant/components/tedee/coordinator.py @@ -11,6 +11,7 @@ from pytedee_async import ( TedeeLocalAuthException, TedeeLock, ) +from pytedee_async.bridge import TedeeBridge from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST @@ -40,6 +41,7 @@ class TedeeApiCoordinator(DataUpdateCoordinator[dict[int, TedeeLock]]): update_interval=SCAN_INTERVAL, ) + self._bridge: TedeeBridge | None = None self.tedee_client = TedeeClient( local_token=self.config_entry.data[CONF_LOCAL_ACCESS_TOKEN], local_ip=self.config_entry.data[CONF_HOST], @@ -47,18 +49,31 @@ class TedeeApiCoordinator(DataUpdateCoordinator[dict[int, TedeeLock]]): self._next_get_locks = time.time() + @property + def bridge(self) -> TedeeBridge: + """Return bridge.""" + assert self._bridge + return self._bridge + async def _async_update_data(self) -> dict[int, TedeeLock]: """Fetch data from API endpoint.""" + if self._bridge is None: + + async def _async_get_bridge() -> None: + self._bridge = await self.tedee_client.get_local_bridge() + + _LOGGER.debug("Update coordinator: Getting bridge from API") + await self._async_update(_async_get_bridge) _LOGGER.debug("Update coordinator: Getting locks from API") # once every hours get all lock details, otherwise use the sync endpoint if self._next_get_locks <= time.time(): _LOGGER.debug("Updating through /my/lock endpoint") - await self._async_update_locks(self.tedee_client.get_locks) + await self._async_update(self.tedee_client.get_locks) self._next_get_locks = time.time() + GET_LOCKS_INTERVAL_SECONDS else: _LOGGER.debug("Updating through /sync endpoint") - await self._async_update_locks(self.tedee_client.sync) + await self._async_update(self.tedee_client.sync) _LOGGER.debug( "available_locks: %s", @@ -67,9 +82,7 @@ class TedeeApiCoordinator(DataUpdateCoordinator[dict[int, TedeeLock]]): return self.tedee_client.locks_dict - async def _async_update_locks( - self, update_fn: Callable[[], Awaitable[None]] - ) -> None: + async def _async_update(self, update_fn: Callable[[], Awaitable[None]]) -> None: """Update locks based on update function.""" try: await update_fn() diff --git a/homeassistant/components/tedee/entity.py b/homeassistant/components/tedee/entity.py index 86baa81b452..6dfcbebe3de 100644 --- a/homeassistant/components/tedee/entity.py +++ b/homeassistant/components/tedee/entity.py @@ -32,6 +32,7 @@ class TedeeEntity(CoordinatorEntity[TedeeApiCoordinator]): name=lock.lock_name, manufacturer="Tedee", model=lock.lock_type, + via_device=(DOMAIN, coordinator.bridge.serial), ) @callback diff --git a/tests/components/tedee/snapshots/test_init.ambr b/tests/components/tedee/snapshots/test_init.ambr new file mode 100644 index 00000000000..e10a9f298bb --- /dev/null +++ b/tests/components/tedee/snapshots/test_init.ambr @@ -0,0 +1,29 @@ +# serializer version: 1 +# name: test_bridge_device + DeviceRegistryEntrySnapshot({ + 'area_id': None, + 'config_entries': , + 'configuration_url': None, + 'connections': set({ + }), + 'disabled_by': None, + 'entry_type': None, + 'hw_version': None, + 'id': , + 'identifiers': set({ + tuple( + 'tedee', + '0000-0000', + ), + }), + 'is_new': False, + 'manufacturer': 'Tedee', + 'model': 'Bridge', + 'name': 'Bridge-AB1C', + 'name_by_user': None, + 'serial_number': '0000-0000', + 'suggested_area': None, + 'sw_version': None, + 'via_device_id': None, + }) +# --- diff --git a/tests/components/tedee/snapshots/test_lock.ambr b/tests/components/tedee/snapshots/test_lock.ambr index b7c20f39750..dd0eab46c90 100644 --- a/tests/components/tedee/snapshots/test_lock.ambr +++ b/tests/components/tedee/snapshots/test_lock.ambr @@ -68,7 +68,7 @@ 'serial_number': None, 'suggested_area': None, 'sw_version': None, - 'via_device_id': None, + 'via_device_id': , }) # --- # name: test_lock_without_pullspring @@ -140,6 +140,6 @@ 'serial_number': None, 'suggested_area': None, 'sw_version': None, - 'via_device_id': None, + 'via_device_id': , }) # --- diff --git a/tests/components/tedee/test_init.py b/tests/components/tedee/test_init.py index 874a827e458..ca64c01a983 100644 --- a/tests/components/tedee/test_init.py +++ b/tests/components/tedee/test_init.py @@ -3,9 +3,11 @@ from unittest.mock import MagicMock from pytedee_async.exception import TedeeAuthException, TedeeClientException import pytest +from syrupy import SnapshotAssertion from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr from tests.common import MockConfigEntry @@ -37,7 +39,7 @@ async def test_config_entry_not_ready( mock_tedee: MagicMock, side_effect: Exception, ) -> None: - """Test the LaMetric configuration entry not ready.""" + """Test the Tedee configuration entry not ready.""" mock_tedee.get_locks.side_effect = side_effect mock_config_entry.add_to_hass(hass) @@ -46,3 +48,22 @@ async def test_config_entry_not_ready( assert len(mock_tedee.get_locks.mock_calls) == 1 assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY + + +async def test_bridge_device( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_tedee: MagicMock, + device_registry: dr.DeviceRegistry, + snapshot: SnapshotAssertion, +) -> None: + """Ensure the bridge device is registered.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + device = device_registry.async_get_device( + {(mock_config_entry.domain, mock_tedee.get_local_bridge.return_value.serial)} + ) + assert device + assert device == snapshot