mirror of
https://github.com/home-assistant/core.git
synced 2026-08-03 20:24:55 +02:00
Fix via_device race in google_health (#177933)
This commit is contained in:
@@ -11,7 +11,7 @@ from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.helpers import aiohttp_client, device_registry as dr
|
||||
from homeassistant.helpers.config_entry_oauth2_flow import (
|
||||
ImplementationUnavailableError,
|
||||
OAuth2Session,
|
||||
@@ -115,6 +115,16 @@ async def async_setup_entry(
|
||||
sleep_coordinator=sleep_coordinator,
|
||||
)
|
||||
|
||||
# Register the account device up front so the per-device sensors can resolve
|
||||
# it as their via_device parent even when only the device scope is granted
|
||||
# (the account-level sensors that would otherwise create it are gated on
|
||||
# different scopes).
|
||||
dr.async_get(hass).async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, entry.entry_id)},
|
||||
manufacturer="Google",
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, _PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
@@ -23,7 +23,11 @@ from homeassistant.const import (
|
||||
UnitOfVolume,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
|
||||
from homeassistant.helpers.device_registry import (
|
||||
CONNECTION_NETWORK_MAC,
|
||||
DeviceInfo,
|
||||
async_get_device_id_by_identifier,
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
@@ -401,7 +405,11 @@ class GoogleHealthDeviceSensor(
|
||||
or (device.device_type.title() if device.device_type else "Device"),
|
||||
model=device.device_type.title() if device.device_type else None,
|
||||
sw_version=device.device_version,
|
||||
via_device=(DOMAIN, entry_id),
|
||||
via_device_id=async_get_device_id_by_identifier(
|
||||
coordinator.hass,
|
||||
(DOMAIN, entry_id),
|
||||
config_entry_id=entry_id,
|
||||
),
|
||||
)
|
||||
|
||||
if device.mac_address:
|
||||
|
||||
@@ -78,9 +78,9 @@ def mock_expires_at() -> int:
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def scopes() -> list[str]:
|
||||
def scopes(request: pytest.FixtureRequest) -> list[str]:
|
||||
"""Fixture with scopes to set up."""
|
||||
return OAUTH_SCOPES
|
||||
return getattr(request, "param", OAUTH_SCOPES)
|
||||
|
||||
|
||||
@pytest.fixture(name="token_entry")
|
||||
|
||||
@@ -4,12 +4,14 @@ from collections.abc import Awaitable, Callable
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from google_health_api.const import HealthApiScope
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.google_health.const import DOMAIN
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.util.unit_system import (
|
||||
METRIC_SYSTEM,
|
||||
US_CUSTOMARY_SYSTEM,
|
||||
@@ -140,3 +142,36 @@ async def test_sensor_unit_conversions(
|
||||
assert state is not None
|
||||
assert float(state.state) == expected_state
|
||||
assert state.attributes.get("unit_of_measurement") == expected_unit
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"scopes",
|
||||
[[HealthApiScope.PROFILE_READ, HealthApiScope.SETTINGS_READ]],
|
||||
indirect=True,
|
||||
)
|
||||
@pytest.mark.usefixtures("mock_google_health_client")
|
||||
async def test_device_sensor_via_device_id(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
config_entry: MockConfigEntry,
|
||||
integration_setup: Callable[[], Awaitable[bool]],
|
||||
) -> None:
|
||||
"""Test a paired device is linked to the account device via via_device_id.
|
||||
|
||||
Only the profile and settings scopes are granted so the account device
|
||||
can only come from the up-front registration, not from account-level
|
||||
sensors that scopes outside of this test would also create.
|
||||
"""
|
||||
with patch("homeassistant.components.google_health._PLATFORMS", [Platform.SENSOR]):
|
||||
assert await integration_setup()
|
||||
|
||||
account_device = device_registry.async_get_device_by_identifier(
|
||||
(DOMAIN, config_entry.entry_id), config_entry.entry_id
|
||||
)
|
||||
assert account_device is not None
|
||||
|
||||
paired_device = device_registry.async_get_device_by_identifier(
|
||||
(DOMAIN, "watch_123"), config_entry.entry_id
|
||||
)
|
||||
assert paired_device is not None
|
||||
assert paired_device.via_device_id == account_device.id
|
||||
|
||||
Reference in New Issue
Block a user