mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Add devices to Withings (#126853)
This commit is contained in:
parent
05288dad51
commit
10805805fe
@ -48,6 +48,7 @@ from .coordinator import (
|
||||
WithingsActivityDataUpdateCoordinator,
|
||||
WithingsBedPresenceDataUpdateCoordinator,
|
||||
WithingsDataUpdateCoordinator,
|
||||
WithingsDeviceDataUpdateCoordinator,
|
||||
WithingsGoalsDataUpdateCoordinator,
|
||||
WithingsMeasurementDataUpdateCoordinator,
|
||||
WithingsSleepDataUpdateCoordinator,
|
||||
@ -73,6 +74,7 @@ class WithingsData:
|
||||
goals_coordinator: WithingsGoalsDataUpdateCoordinator
|
||||
activity_coordinator: WithingsActivityDataUpdateCoordinator
|
||||
workout_coordinator: WithingsWorkoutDataUpdateCoordinator
|
||||
device_coordinator: WithingsDeviceDataUpdateCoordinator
|
||||
coordinators: set[WithingsDataUpdateCoordinator] = field(default_factory=set)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
@ -84,6 +86,7 @@ class WithingsData:
|
||||
self.goals_coordinator,
|
||||
self.activity_coordinator,
|
||||
self.workout_coordinator,
|
||||
self.device_coordinator,
|
||||
}
|
||||
|
||||
|
||||
@ -122,6 +125,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: WithingsConfigEntry) ->
|
||||
goals_coordinator=WithingsGoalsDataUpdateCoordinator(hass, client),
|
||||
activity_coordinator=WithingsActivityDataUpdateCoordinator(hass, client),
|
||||
workout_coordinator=WithingsWorkoutDataUpdateCoordinator(hass, client),
|
||||
device_coordinator=WithingsDeviceDataUpdateCoordinator(hass, client),
|
||||
)
|
||||
|
||||
for coordinator in withings_data.coordinators:
|
||||
|
@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from aiowithings import (
|
||||
Activity,
|
||||
Device,
|
||||
Goals,
|
||||
MeasurementPosition,
|
||||
MeasurementType,
|
||||
@ -291,3 +292,17 @@ class WithingsWorkoutDataUpdateCoordinator(
|
||||
self._previous_data = latest_workout
|
||||
self._last_valid_update = latest_workout.end_date
|
||||
return self._previous_data
|
||||
|
||||
|
||||
class WithingsDeviceDataUpdateCoordinator(
|
||||
WithingsDataUpdateCoordinator[dict[str, Device]]
|
||||
):
|
||||
"""Withings device coordinator."""
|
||||
|
||||
coordinator_name: str = "device"
|
||||
_default_update_interval = timedelta(hours=1)
|
||||
|
||||
async def _internal_update_data(self) -> dict[str, Device]:
|
||||
"""Update coordinator data."""
|
||||
devices = await self._client.get_devices()
|
||||
return {device.device_id: device for device in devices}
|
||||
|
@ -4,11 +4,16 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from aiowithings import Device
|
||||
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import WithingsDataUpdateCoordinator
|
||||
from .coordinator import (
|
||||
WithingsDataUpdateCoordinator,
|
||||
WithingsDeviceDataUpdateCoordinator,
|
||||
)
|
||||
|
||||
|
||||
class WithingsEntity[_T: WithingsDataUpdateCoordinator[Any]](CoordinatorEntity[_T]):
|
||||
@ -28,3 +33,35 @@ class WithingsEntity[_T: WithingsDataUpdateCoordinator[Any]](CoordinatorEntity[_
|
||||
identifiers={(DOMAIN, str(coordinator.config_entry.unique_id))},
|
||||
manufacturer="Withings",
|
||||
)
|
||||
|
||||
|
||||
class WithingsDeviceEntity(WithingsEntity[WithingsDeviceDataUpdateCoordinator]):
|
||||
"""Base class for withings device entities."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: WithingsDeviceDataUpdateCoordinator,
|
||||
device_id: str,
|
||||
key: str,
|
||||
) -> None:
|
||||
"""Initialize the Withings entity."""
|
||||
super().__init__(coordinator, key)
|
||||
self._attr_unique_id = f"{device_id}_{key}"
|
||||
self.device_id = device_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, device_id)},
|
||||
manufacturer="Withings",
|
||||
name=self.device.raw_model,
|
||||
model=self.device.raw_model,
|
||||
via_device=(DOMAIN, str(coordinator.config_entry.unique_id)),
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if entity is available."""
|
||||
return super().available and self.device_id in self.coordinator.data
|
||||
|
||||
@property
|
||||
def device(self) -> Device:
|
||||
"""Return the Withings device."""
|
||||
return self.coordinator.data[self.device_id]
|
||||
|
@ -136,6 +136,14 @@
|
||||
},
|
||||
"workout_duration": {
|
||||
"default": "mdi:timer"
|
||||
},
|
||||
"battery": {
|
||||
"default": "mdi:battery-off",
|
||||
"state": {
|
||||
"low": "mdi:battery-20",
|
||||
"medium": "mdi:battery-50",
|
||||
"high": "mdi:battery"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ from typing import Any
|
||||
|
||||
from aiowithings import (
|
||||
Activity,
|
||||
Device,
|
||||
Goals,
|
||||
MeasurementPosition,
|
||||
MeasurementType,
|
||||
@ -23,6 +24,7 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import (
|
||||
PERCENTAGE,
|
||||
Platform,
|
||||
@ -33,8 +35,8 @@ from homeassistant.const import (
|
||||
UnitOfTime,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
import homeassistant.helpers.entity_registry as er
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
@ -51,12 +53,13 @@ from .const import (
|
||||
from .coordinator import (
|
||||
WithingsActivityDataUpdateCoordinator,
|
||||
WithingsDataUpdateCoordinator,
|
||||
WithingsDeviceDataUpdateCoordinator,
|
||||
WithingsGoalsDataUpdateCoordinator,
|
||||
WithingsMeasurementDataUpdateCoordinator,
|
||||
WithingsSleepDataUpdateCoordinator,
|
||||
WithingsWorkoutDataUpdateCoordinator,
|
||||
)
|
||||
from .entity import WithingsEntity
|
||||
from .entity import WithingsDeviceEntity, WithingsEntity
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
@ -650,6 +653,24 @@ WORKOUT_SENSORS = [
|
||||
]
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class WithingsDeviceSensorEntityDescription(SensorEntityDescription):
|
||||
"""Immutable class for describing withings data."""
|
||||
|
||||
value_fn: Callable[[Device], StateType]
|
||||
|
||||
|
||||
DEVICE_SENSORS = [
|
||||
WithingsDeviceSensorEntityDescription(
|
||||
key="battery",
|
||||
translation_key="battery",
|
||||
options=["low", "medium", "high"],
|
||||
device_class=SensorDeviceClass.ENUM,
|
||||
value_fn=lambda device: device.battery,
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def get_current_goals(goals: Goals) -> set[str]:
|
||||
"""Return a list of present goals."""
|
||||
result = set()
|
||||
@ -800,6 +821,48 @@ async def async_setup_entry(
|
||||
_async_add_workout_entities
|
||||
)
|
||||
|
||||
device_coordinator = withings_data.device_coordinator
|
||||
|
||||
current_devices: set[str] = set()
|
||||
|
||||
def _async_device_listener() -> None:
|
||||
"""Add device entities."""
|
||||
received_devices = set(device_coordinator.data)
|
||||
new_devices = received_devices - current_devices
|
||||
old_devices = current_devices - received_devices
|
||||
if new_devices:
|
||||
device_registry = dr.async_get(hass)
|
||||
for device_id in new_devices:
|
||||
if device := device_registry.async_get_device({(DOMAIN, device_id)}):
|
||||
if any(
|
||||
(
|
||||
config_entry := hass.config_entries.async_get_entry(
|
||||
config_entry_id
|
||||
)
|
||||
)
|
||||
and config_entry.state == ConfigEntryState.LOADED
|
||||
for config_entry_id in device.config_entries
|
||||
):
|
||||
continue
|
||||
async_add_entities(
|
||||
WithingsDeviceSensor(device_coordinator, description, device_id)
|
||||
for description in DEVICE_SENSORS
|
||||
)
|
||||
current_devices.add(device_id)
|
||||
|
||||
if old_devices:
|
||||
device_registry = dr.async_get(hass)
|
||||
for device_id in old_devices:
|
||||
if device := device_registry.async_get_device({(DOMAIN, device_id)}):
|
||||
device_registry.async_update_device(
|
||||
device.id, remove_config_entry_id=entry.entry_id
|
||||
)
|
||||
current_devices.remove(device_id)
|
||||
|
||||
device_coordinator.async_add_listener(_async_device_listener)
|
||||
|
||||
_async_device_listener()
|
||||
|
||||
if not entities:
|
||||
LOGGER.warning(
|
||||
"No data found for Withings entry %s, sensors will be added when new data is available"
|
||||
@ -923,3 +986,24 @@ class WithingsWorkoutSensor(
|
||||
if not self.coordinator.data:
|
||||
return None
|
||||
return self.entity_description.value_fn(self.coordinator.data)
|
||||
|
||||
|
||||
class WithingsDeviceSensor(WithingsDeviceEntity, SensorEntity):
|
||||
"""Implementation of a Withings workout sensor."""
|
||||
|
||||
entity_description: WithingsDeviceSensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: WithingsDeviceDataUpdateCoordinator,
|
||||
entity_description: WithingsDeviceSensorEntityDescription,
|
||||
device_id: str,
|
||||
) -> None:
|
||||
"""Initialize sensor."""
|
||||
super().__init__(coordinator, device_id, entity_description.key)
|
||||
self.entity_description = entity_description
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
"""Return the state of the entity."""
|
||||
return self.entity_description.value_fn(self.device)
|
||||
|
@ -307,6 +307,14 @@
|
||||
},
|
||||
"workout_duration": {
|
||||
"name": "Last workout duration"
|
||||
},
|
||||
"battery": {
|
||||
"name": "[%key:component::sensor::entity_component::battery::name%]",
|
||||
"state": {
|
||||
"low": "Low",
|
||||
"medium": "Medium",
|
||||
"high": "High"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from aiohttp.test_utils import TestClient
|
||||
from aiowithings import Activity, Goals, MeasurementGroup, SleepSummary, Workout
|
||||
from aiowithings import Activity, Device, Goals, MeasurementGroup, SleepSummary, Workout
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.webhook import async_generate_url
|
||||
@ -109,3 +109,11 @@ def load_sleep_fixture(
|
||||
"""Return sleep summaries from fixture."""
|
||||
sleep_json = load_json_array_fixture("withings/sleep_summaries.json")
|
||||
return [SleepSummary.from_api(sleep_summary) for sleep_summary in sleep_json]
|
||||
|
||||
|
||||
def load_device_fixture(
|
||||
fixture: str = "withings/devices.json",
|
||||
) -> list[Device]:
|
||||
"""Return sleep summaries from fixture."""
|
||||
devices_json = load_json_array_fixture(fixture)
|
||||
return [Device.from_api(device) for device in devices_json]
|
||||
|
@ -133,6 +133,29 @@ def polling_config_entry(expires_at: int, scopes: list[str]) -> MockConfigEntry:
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def second_polling_config_entry(expires_at: int, scopes: list[str]) -> MockConfigEntry:
|
||||
"""Create Withings entry in Home Assistant."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title="Not Henk",
|
||||
unique_id="54321",
|
||||
data={
|
||||
"auth_implementation": DOMAIN,
|
||||
"token": {
|
||||
"status": 0,
|
||||
"userid": "54321",
|
||||
"access_token": "mock-access-token",
|
||||
"refresh_token": "mock-refresh-token",
|
||||
"expires_at": expires_at,
|
||||
"scope": ",".join(scopes),
|
||||
},
|
||||
"profile": TITLE,
|
||||
"webhook_id": WEBHOOK_ID,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="withings")
|
||||
def mock_withings():
|
||||
"""Mock withings."""
|
||||
|
65
tests/components/withings/snapshots/test_init.ambr
Normal file
65
tests/components/withings/snapshots/test_init.ambr
Normal file
@ -0,0 +1,65 @@
|
||||
# serializer version: 1
|
||||
# name: test_devices[12345]
|
||||
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(
|
||||
'withings',
|
||||
'12345',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Withings',
|
||||
'model': None,
|
||||
'model_id': None,
|
||||
'name': 'henk',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': None,
|
||||
'via_device_id': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_devices[f998be4b9ccc9e136fd8cd8e8e344c31ec3b271d]
|
||||
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(
|
||||
'withings',
|
||||
'f998be4b9ccc9e136fd8cd8e8e344c31ec3b271d',
|
||||
),
|
||||
}),
|
||||
'is_new': False,
|
||||
'labels': set({
|
||||
}),
|
||||
'manufacturer': 'Withings',
|
||||
'model': 'Body+',
|
||||
'model_id': None,
|
||||
'name': 'Body+',
|
||||
'name_by_user': None,
|
||||
'primary_config_entry': <ANY>,
|
||||
'serial_number': None,
|
||||
'suggested_area': None,
|
||||
'sw_version': None,
|
||||
'via_device_id': <ANY>,
|
||||
})
|
||||
# ---
|
@ -1,4 +1,62 @@
|
||||
# serializer version: 1
|
||||
# name: test_all_entities[sensor.body_battery-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': dict({
|
||||
'options': list([
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
]),
|
||||
}),
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.body_battery',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.ENUM: 'enum'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Battery',
|
||||
'platform': 'withings',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'battery',
|
||||
'unique_id': 'f998be4b9ccc9e136fd8cd8e8e344c31ec3b271d_battery',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.body_battery-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'device_class': 'enum',
|
||||
'friendly_name': 'Body+ Battery',
|
||||
'options': list([
|
||||
'low',
|
||||
'medium',
|
||||
'high',
|
||||
]),
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.body_battery',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': 'high',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.henk_active_calories_burnt_today-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
|
@ -14,6 +14,7 @@ from aiowithings import (
|
||||
)
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import cloud
|
||||
@ -22,6 +23,7 @@ from homeassistant.components.webhook import async_generate_url
|
||||
from homeassistant.components.withings.const import DOMAIN
|
||||
from homeassistant.const import CONF_WEBHOOK_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import call_webhook, prepare_webhook_setup, setup_integration
|
||||
@ -569,3 +571,21 @@ async def test_webhook_post(
|
||||
resp.close()
|
||||
|
||||
assert data["code"] == expected_code
|
||||
|
||||
|
||||
async def test_devices(
|
||||
hass: HomeAssistant,
|
||||
withings: AsyncMock,
|
||||
webhook_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test devices."""
|
||||
await setup_integration(hass, webhook_config_entry)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for device_id in ("12345", "f998be4b9ccc9e136fd8cd8e8e344c31ec3b271d"):
|
||||
device = device_registry.async_get_device({(DOMAIN, device_id)})
|
||||
assert device is not None
|
||||
assert device == snapshot(name=device_id)
|
||||
|
@ -8,12 +8,14 @@ from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.withings import DOMAIN
|
||||
from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, 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 . import (
|
||||
load_activity_fixture,
|
||||
load_device_fixture,
|
||||
load_goals_fixture,
|
||||
load_measurements_fixture,
|
||||
load_sleep_fixture,
|
||||
@ -351,3 +353,83 @@ async def test_warning_if_no_entities_created(
|
||||
await setup_integration(hass, polling_config_entry, False)
|
||||
|
||||
assert "No data found for Withings entry" in caplog.text
|
||||
|
||||
|
||||
async def test_device_sensors_created_when_device_data_received(
|
||||
hass: HomeAssistant,
|
||||
withings: AsyncMock,
|
||||
polling_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
) -> None:
|
||||
"""Test device sensors will be added if we receive device data."""
|
||||
withings.get_devices.return_value = []
|
||||
await setup_integration(hass, polling_config_entry, False)
|
||||
|
||||
assert hass.states.get("sensor.body_battery") is None
|
||||
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.body_battery") is None
|
||||
|
||||
withings.get_devices.return_value = load_device_fixture()
|
||||
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.body_battery")
|
||||
assert device_registry.async_get_device(
|
||||
{(DOMAIN, "f998be4b9ccc9e136fd8cd8e8e344c31ec3b271d")}
|
||||
)
|
||||
|
||||
withings.get_devices.return_value = []
|
||||
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.body_battery") is None
|
||||
assert not device_registry.async_get_device(
|
||||
{(DOMAIN, "f998be4b9ccc9e136fd8cd8e8e344c31ec3b271d")}
|
||||
)
|
||||
|
||||
|
||||
async def test_device_two_config_entries(
|
||||
hass: HomeAssistant,
|
||||
withings: AsyncMock,
|
||||
polling_config_entry: MockConfigEntry,
|
||||
second_polling_config_entry: MockConfigEntry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test device sensors will be added for one config entry only at a time."""
|
||||
await setup_integration(hass, polling_config_entry, False)
|
||||
|
||||
assert hass.states.get("sensor.body_battery") is not None
|
||||
|
||||
second_polling_config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(second_polling_config_entry.entry_id)
|
||||
|
||||
assert hass.states.get("sensor.not_henk_temperature") is not None
|
||||
|
||||
assert "Platform withings does not generate unique IDs" not in caplog.text
|
||||
|
||||
await hass.config_entries.async_unload(polling_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.body_battery").state == STATE_UNAVAILABLE
|
||||
|
||||
freezer.tick(timedelta(hours=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get("sensor.body_battery").state != STATE_UNAVAILABLE
|
||||
|
||||
await hass.config_entries.async_setup(polling_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert "Platform withings does not generate unique IDs" not in caplog.text
|
||||
|
Loading…
x
Reference in New Issue
Block a user