Add tedee bridge as via_device for tedee integration (#106914)

* add bridge as via_device

* add bridge as via_device

* move getting bridge to update_data

* add bridge property
This commit is contained in:
Josef Zweck 2024-01-02 23:23:50 +01:00 committed by GitHub
parent 5003993658
commit 87c79ef57f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 8 deletions

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -0,0 +1,29 @@
# serializer version: 1
# name: test_bridge_device
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': None,
'connections': set({
}),
'disabled_by': None,
'entry_type': None,
'hw_version': None,
'id': <ANY>,
'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,
})
# ---

View File

@ -68,7 +68,7 @@
'serial_number': None,
'suggested_area': None,
'sw_version': None,
'via_device_id': None,
'via_device_id': <ANY>,
})
# ---
# 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': <ANY>,
})
# ---

View File

@ -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