Add sensors for Withings Goals (#102468)
parent
f0f3a43b09
commit
235a3486ee
|
@ -56,6 +56,7 @@ from .const import (
|
||||||
CONF_USE_WEBHOOK,
|
CONF_USE_WEBHOOK,
|
||||||
DEFAULT_TITLE,
|
DEFAULT_TITLE,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
GOALS_COORDINATOR,
|
||||||
LOGGER,
|
LOGGER,
|
||||||
MEASUREMENT_COORDINATOR,
|
MEASUREMENT_COORDINATOR,
|
||||||
SLEEP_COORDINATOR,
|
SLEEP_COORDINATOR,
|
||||||
|
@ -63,6 +64,7 @@ from .const import (
|
||||||
from .coordinator import (
|
from .coordinator import (
|
||||||
WithingsBedPresenceDataUpdateCoordinator,
|
WithingsBedPresenceDataUpdateCoordinator,
|
||||||
WithingsDataUpdateCoordinator,
|
WithingsDataUpdateCoordinator,
|
||||||
|
WithingsGoalsDataUpdateCoordinator,
|
||||||
WithingsMeasurementDataUpdateCoordinator,
|
WithingsMeasurementDataUpdateCoordinator,
|
||||||
WithingsSleepDataUpdateCoordinator,
|
WithingsSleepDataUpdateCoordinator,
|
||||||
)
|
)
|
||||||
|
@ -160,6 +162,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
BED_PRESENCE_COORDINATOR: WithingsBedPresenceDataUpdateCoordinator(
|
BED_PRESENCE_COORDINATOR: WithingsBedPresenceDataUpdateCoordinator(
|
||||||
hass, client
|
hass, client
|
||||||
),
|
),
|
||||||
|
GOALS_COORDINATOR: WithingsGoalsDataUpdateCoordinator(hass, client),
|
||||||
}
|
}
|
||||||
|
|
||||||
for coordinator in coordinators.values():
|
for coordinator in coordinators.values():
|
||||||
|
|
|
@ -16,6 +16,7 @@ PUSH_HANDLER = "push_handler"
|
||||||
MEASUREMENT_COORDINATOR = "measurement_coordinator"
|
MEASUREMENT_COORDINATOR = "measurement_coordinator"
|
||||||
SLEEP_COORDINATOR = "sleep_coordinator"
|
SLEEP_COORDINATOR = "sleep_coordinator"
|
||||||
BED_PRESENCE_COORDINATOR = "bed_presence_coordinator"
|
BED_PRESENCE_COORDINATOR = "bed_presence_coordinator"
|
||||||
|
GOALS_COORDINATOR = "goals_coordinator"
|
||||||
|
|
||||||
LOGGER = logging.getLogger(__package__)
|
LOGGER = logging.getLogger(__package__)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ from datetime import datetime, timedelta
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
from aiowithings import (
|
from aiowithings import (
|
||||||
|
Goals,
|
||||||
MeasurementType,
|
MeasurementType,
|
||||||
NotificationCategory,
|
NotificationCategory,
|
||||||
SleepSummary,
|
SleepSummary,
|
||||||
|
@ -170,3 +171,17 @@ class WithingsBedPresenceDataUpdateCoordinator(WithingsDataUpdateCoordinator[Non
|
||||||
|
|
||||||
async def _internal_update_data(self) -> None:
|
async def _internal_update_data(self) -> None:
|
||||||
"""Update coordinator data."""
|
"""Update coordinator data."""
|
||||||
|
|
||||||
|
|
||||||
|
class WithingsGoalsDataUpdateCoordinator(WithingsDataUpdateCoordinator[Goals]):
|
||||||
|
"""Withings goals coordinator."""
|
||||||
|
|
||||||
|
_default_update_interval = timedelta(hours=1)
|
||||||
|
|
||||||
|
def webhook_subscription_listener(self, connected: bool) -> None:
|
||||||
|
"""Call when webhook status changed."""
|
||||||
|
# Webhooks aren't available for this datapoint, so we keep polling
|
||||||
|
|
||||||
|
async def _internal_update_data(self) -> Goals:
|
||||||
|
"""Retrieve goals data."""
|
||||||
|
return await self._client.get_goals()
|
||||||
|
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from aiowithings import MeasurementType, SleepSummary
|
from aiowithings import Goals, MeasurementType, SleepSummary
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
|
@ -27,6 +27,7 @@ from homeassistant.helpers.typing import StateType
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
GOALS_COORDINATOR,
|
||||||
MEASUREMENT_COORDINATOR,
|
MEASUREMENT_COORDINATOR,
|
||||||
SCORE_POINTS,
|
SCORE_POINTS,
|
||||||
SLEEP_COORDINATOR,
|
SLEEP_COORDINATOR,
|
||||||
|
@ -37,6 +38,7 @@ from .const import (
|
||||||
)
|
)
|
||||||
from .coordinator import (
|
from .coordinator import (
|
||||||
WithingsDataUpdateCoordinator,
|
WithingsDataUpdateCoordinator,
|
||||||
|
WithingsGoalsDataUpdateCoordinator,
|
||||||
WithingsMeasurementDataUpdateCoordinator,
|
WithingsMeasurementDataUpdateCoordinator,
|
||||||
WithingsSleepDataUpdateCoordinator,
|
WithingsSleepDataUpdateCoordinator,
|
||||||
)
|
)
|
||||||
|
@ -396,6 +398,64 @@ SLEEP_SENSORS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
STEP_GOAL = "steps"
|
||||||
|
SLEEP_GOAL = "sleep"
|
||||||
|
WEIGHT_GOAL = "weight"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WithingsGoalsSensorEntityDescriptionMixin:
|
||||||
|
"""Mixin for describing withings data."""
|
||||||
|
|
||||||
|
value_fn: Callable[[Goals], StateType]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class WithingsGoalsSensorEntityDescription(
|
||||||
|
SensorEntityDescription, WithingsGoalsSensorEntityDescriptionMixin
|
||||||
|
):
|
||||||
|
"""Immutable class for describing withings data."""
|
||||||
|
|
||||||
|
|
||||||
|
GOALS_SENSORS: dict[str, WithingsGoalsSensorEntityDescription] = {
|
||||||
|
STEP_GOAL: WithingsGoalsSensorEntityDescription(
|
||||||
|
key="step_goal",
|
||||||
|
value_fn=lambda goals: goals.steps,
|
||||||
|
icon="mdi:shoe-print",
|
||||||
|
translation_key="step_goal",
|
||||||
|
native_unit_of_measurement="Steps",
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
|
SLEEP_GOAL: WithingsGoalsSensorEntityDescription(
|
||||||
|
key="sleep_goal",
|
||||||
|
value_fn=lambda goals: goals.sleep,
|
||||||
|
icon="mdi:bed-clock",
|
||||||
|
translation_key="sleep_goal",
|
||||||
|
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||||
|
suggested_unit_of_measurement=UnitOfTime.HOURS,
|
||||||
|
device_class=SensorDeviceClass.DURATION,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
|
WEIGHT_GOAL: WithingsGoalsSensorEntityDescription(
|
||||||
|
key="weight_goal",
|
||||||
|
value_fn=lambda goals: goals.weight,
|
||||||
|
translation_key="weight_goal",
|
||||||
|
native_unit_of_measurement=UnitOfMass.KILOGRAMS,
|
||||||
|
device_class=SensorDeviceClass.WEIGHT,
|
||||||
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_goals(goals: Goals) -> set[str]:
|
||||||
|
"""Return a list of present goals."""
|
||||||
|
result = set()
|
||||||
|
for goal in (STEP_GOAL, SLEEP_GOAL, WEIGHT_GOAL):
|
||||||
|
if getattr(goals, goal):
|
||||||
|
result.add(goal)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entry: ConfigEntry,
|
entry: ConfigEntry,
|
||||||
|
@ -406,8 +466,6 @@ async def async_setup_entry(
|
||||||
DOMAIN
|
DOMAIN
|
||||||
][entry.entry_id][MEASUREMENT_COORDINATOR]
|
][entry.entry_id][MEASUREMENT_COORDINATOR]
|
||||||
|
|
||||||
current_measurement_types = set(measurement_coordinator.data)
|
|
||||||
|
|
||||||
entities: list[SensorEntity] = []
|
entities: list[SensorEntity] = []
|
||||||
entities.extend(
|
entities.extend(
|
||||||
WithingsMeasurementSensor(
|
WithingsMeasurementSensor(
|
||||||
|
@ -417,6 +475,8 @@ async def async_setup_entry(
|
||||||
if measurement_type in MEASUREMENT_SENSORS
|
if measurement_type in MEASUREMENT_SENSORS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
current_measurement_types = set(measurement_coordinator.data)
|
||||||
|
|
||||||
def _async_measurement_listener() -> None:
|
def _async_measurement_listener() -> None:
|
||||||
"""Listen for new measurements and add sensors if they did not exist."""
|
"""Listen for new measurements and add sensors if they did not exist."""
|
||||||
received_measurement_types = set(measurement_coordinator.data)
|
received_measurement_types = set(measurement_coordinator.data)
|
||||||
|
@ -431,6 +491,31 @@ async def async_setup_entry(
|
||||||
)
|
)
|
||||||
|
|
||||||
measurement_coordinator.async_add_listener(_async_measurement_listener)
|
measurement_coordinator.async_add_listener(_async_measurement_listener)
|
||||||
|
|
||||||
|
goals_coordinator: WithingsGoalsDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
|
entry.entry_id
|
||||||
|
][GOALS_COORDINATOR]
|
||||||
|
|
||||||
|
current_goals = get_current_goals(goals_coordinator.data)
|
||||||
|
|
||||||
|
entities.extend(
|
||||||
|
WithingsGoalsSensor(goals_coordinator, GOALS_SENSORS[goal])
|
||||||
|
for goal in current_goals
|
||||||
|
)
|
||||||
|
|
||||||
|
def _async_goals_listener() -> None:
|
||||||
|
"""Listen for new goals and add sensors if they did not exist."""
|
||||||
|
received_goals = get_current_goals(goals_coordinator.data)
|
||||||
|
new_goals = received_goals - current_goals
|
||||||
|
if new_goals:
|
||||||
|
current_goals.update(new_goals)
|
||||||
|
async_add_entities(
|
||||||
|
WithingsGoalsSensor(goals_coordinator, GOALS_SENSORS[goal])
|
||||||
|
for goal in new_goals
|
||||||
|
)
|
||||||
|
|
||||||
|
goals_coordinator.async_add_listener(_async_goals_listener)
|
||||||
|
|
||||||
sleep_coordinator: WithingsSleepDataUpdateCoordinator = hass.data[DOMAIN][
|
sleep_coordinator: WithingsSleepDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
entry.entry_id
|
entry.entry_id
|
||||||
][SLEEP_COORDINATOR]
|
][SLEEP_COORDINATOR]
|
||||||
|
@ -492,3 +577,17 @@ class WithingsSleepSensor(WithingsSensor):
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return if the sensor is available."""
|
"""Return if the sensor is available."""
|
||||||
return super().available and self.coordinator.data is not None
|
return super().available and self.coordinator.data is not None
|
||||||
|
|
||||||
|
|
||||||
|
class WithingsGoalsSensor(WithingsSensor):
|
||||||
|
"""Implementation of a Withings goals sensor."""
|
||||||
|
|
||||||
|
coordinator: WithingsGoalsDataUpdateCoordinator
|
||||||
|
|
||||||
|
entity_description: WithingsGoalsSensorEntityDescription
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_value(self) -> StateType:
|
||||||
|
"""Return the state of the entity."""
|
||||||
|
assert self.coordinator.data
|
||||||
|
return self.entity_description.value_fn(self.coordinator.data)
|
||||||
|
|
|
@ -134,6 +134,15 @@
|
||||||
},
|
},
|
||||||
"wakeup_time": {
|
"wakeup_time": {
|
||||||
"name": "Wakeup time"
|
"name": "Wakeup time"
|
||||||
|
},
|
||||||
|
"step_goal": {
|
||||||
|
"name": "Step goal"
|
||||||
|
},
|
||||||
|
"sleep_goal": {
|
||||||
|
"name": "Sleep goal"
|
||||||
|
},
|
||||||
|
"weight_goal": {
|
||||||
|
"name": "Weight goal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ from datetime import timedelta
|
||||||
import time
|
import time
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from aiowithings import Device, MeasurementGroup, SleepSummary, WithingsClient
|
from aiowithings import Device, Goals, MeasurementGroup, SleepSummary, WithingsClient
|
||||||
from aiowithings.models import NotificationConfiguration
|
from aiowithings.models import NotificationConfiguration
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -148,8 +148,12 @@ def mock_withings():
|
||||||
for not_conf in notification_json["profiles"]
|
for not_conf in notification_json["profiles"]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
goals_json = load_json_object_fixture("withings/goals.json")
|
||||||
|
goals = Goals.from_api(goals_json)
|
||||||
|
|
||||||
mock = AsyncMock(spec=WithingsClient)
|
mock = AsyncMock(spec=WithingsClient)
|
||||||
mock.get_devices.return_value = devices
|
mock.get_devices.return_value = devices
|
||||||
|
mock.get_goals.return_value = goals
|
||||||
mock.get_measurement_in_period.return_value = measurement_groups
|
mock.get_measurement_in_period.return_value = measurement_groups
|
||||||
mock.get_measurement_since.return_value = measurement_groups
|
mock.get_measurement_since.return_value = measurement_groups
|
||||||
mock.get_sleep_summary_since.return_value = sleep_summaries
|
mock.get_sleep_summary_since.return_value = sleep_summaries
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"steps": 10000,
|
||||||
|
"sleep": 28800,
|
||||||
|
"weight": {
|
||||||
|
"value": 70500,
|
||||||
|
"unit": -3
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"weight": {
|
||||||
|
"value": 70500,
|
||||||
|
"unit": -3
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,255 +1,5 @@
|
||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
# name: test_all_entities
|
# name: test_all_entities[sensor.henk_average_heart_rate]
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'weight',
|
|
||||||
'friendly_name': 'henk Weight',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_weight',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '70',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.1
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'weight',
|
|
||||||
'friendly_name': 'henk Fat mass',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_fat_mass',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '5',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.10
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Diastolic blood pressure',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'mmhg',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_diastolic_blood_pressure',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '70',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.11
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Systolic blood pressure',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'mmhg',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_systolic_blood_pressure',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '100',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.12
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Heart pulse',
|
|
||||||
'icon': 'mdi:heart-pulse',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'bpm',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_heart_pulse',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '60',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.13
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk SpO2',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': '%',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_spo2',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '0.95',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.14
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'weight',
|
|
||||||
'friendly_name': 'henk Hydration',
|
|
||||||
'icon': 'mdi:water',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_hydration',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '0.95',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.15
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'speed',
|
|
||||||
'friendly_name': 'henk Pulse wave velocity',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfSpeed.METERS_PER_SECOND: 'm/s'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_pulse_wave_velocity',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '100',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.16
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk VO2 max',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'ml/min/kg',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_vo2_max',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '100',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.17
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Vascular age',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_vascular_age',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '100',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.18
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'weight',
|
|
||||||
'friendly_name': 'henk Extracellular water',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_extracellular_water',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '100',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.19
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'weight',
|
|
||||||
'friendly_name': 'henk Intracellular water',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_intracellular_water',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '100',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.2
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'weight',
|
|
||||||
'friendly_name': 'henk Fat free mass',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_fat_free_mass',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '60',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.20
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Breathing disturbances intensity',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_breathing_disturbances_intensity',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '10',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.21
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'duration',
|
|
||||||
'friendly_name': 'henk Deep sleep',
|
|
||||||
'icon': 'mdi:sleep',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_deep_sleep',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '26220',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.22
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'duration',
|
|
||||||
'friendly_name': 'henk Time to sleep',
|
|
||||||
'icon': 'mdi:sleep',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_time_to_sleep',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '780',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.23
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'duration',
|
|
||||||
'friendly_name': 'henk Time to wakeup',
|
|
||||||
'icon': 'mdi:sleep-off',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_time_to_wakeup',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '996',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.24
|
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'friendly_name': 'henk Average heart rate',
|
'friendly_name': 'henk Average heart rate',
|
||||||
|
@ -264,69 +14,7 @@
|
||||||
'state': '83',
|
'state': '83',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.25
|
# name: test_all_entities[sensor.henk_average_respiratory_rate]
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Maximum heart rate',
|
|
||||||
'icon': 'mdi:heart-pulse',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'bpm',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_maximum_heart_rate',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '108',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.26
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Minimum heart rate',
|
|
||||||
'icon': 'mdi:heart-pulse',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'bpm',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_minimum_heart_rate',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '58',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.27
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'duration',
|
|
||||||
'friendly_name': 'henk Light sleep',
|
|
||||||
'icon': 'mdi:sleep',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_light_sleep',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '58440',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.28
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'duration',
|
|
||||||
'friendly_name': 'henk REM sleep',
|
|
||||||
'icon': 'mdi:sleep',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_rem_sleep',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '17280',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.29
|
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'friendly_name': 'henk Average respiratory rate',
|
'friendly_name': 'henk Average respiratory rate',
|
||||||
|
@ -340,122 +28,22 @@
|
||||||
'state': '14',
|
'state': '14',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.3
|
# name: test_all_entities[sensor.henk_body_temperature]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'device_class': 'weight',
|
'device_class': 'temperature',
|
||||||
'friendly_name': 'henk Muscle mass',
|
'friendly_name': 'henk Body temperature',
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'sensor.henk_muscle_mass',
|
'entity_id': 'sensor.henk_body_temperature',
|
||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': '50',
|
'state': '40',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.30
|
# name: test_all_entities[sensor.henk_bone_mass]
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Maximum respiratory rate',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'br/min',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_maximum_respiratory_rate',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '20',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.31
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Minimum respiratory rate',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'br/min',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_minimum_respiratory_rate',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '10',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.32
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Sleep score',
|
|
||||||
'icon': 'mdi:medal',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'points',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_sleep_score',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '90',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.33
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Snoring',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_snoring',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '1044',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.34
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Snoring episode count',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_snoring_episode_count',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '87',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.35
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'friendly_name': 'henk Wakeup count',
|
|
||||||
'icon': 'mdi:sleep-off',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': 'times',
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_wakeup_count',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '8',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.36
|
|
||||||
StateSnapshot({
|
|
||||||
'attributes': ReadOnlyDict({
|
|
||||||
'device_class': 'duration',
|
|
||||||
'friendly_name': 'henk Wakeup time',
|
|
||||||
'icon': 'mdi:sleep-off',
|
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
|
||||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
|
||||||
}),
|
|
||||||
'context': <ANY>,
|
|
||||||
'entity_id': 'sensor.henk_wakeup_time',
|
|
||||||
'last_changed': <ANY>,
|
|
||||||
'last_updated': <ANY>,
|
|
||||||
'state': '3468',
|
|
||||||
})
|
|
||||||
# ---
|
|
||||||
# name: test_all_entities.4
|
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'device_class': 'weight',
|
'device_class': 'weight',
|
||||||
|
@ -471,7 +59,124 @@
|
||||||
'state': '10',
|
'state': '10',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.5
|
# name: test_all_entities[sensor.henk_breathing_disturbances_intensity]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Breathing disturbances intensity',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_breathing_disturbances_intensity',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '10',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_deep_sleep]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk Deep sleep',
|
||||||
|
'icon': 'mdi:sleep',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_deep_sleep',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '26220',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_diastolic_blood_pressure]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Diastolic blood pressure',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'mmhg',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_diastolic_blood_pressure',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '70',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_extracellular_water]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'weight',
|
||||||
|
'friendly_name': 'henk Extracellular water',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_extracellular_water',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '100',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_fat_free_mass]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'weight',
|
||||||
|
'friendly_name': 'henk Fat free mass',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_fat_free_mass',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '60',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_fat_mass]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'weight',
|
||||||
|
'friendly_name': 'henk Fat mass',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_fat_mass',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '5',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_fat_ratio]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Fat ratio',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': '%',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_fat_ratio',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '0.07',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_heart_pulse]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Heart pulse',
|
||||||
|
'icon': 'mdi:heart-pulse',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'bpm',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_heart_pulse',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '60',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_height]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'device_class': 'distance',
|
'device_class': 'distance',
|
||||||
|
@ -486,37 +191,158 @@
|
||||||
'state': '2',
|
'state': '2',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.6
|
# name: test_all_entities[sensor.henk_hydration]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'device_class': 'temperature',
|
'device_class': 'weight',
|
||||||
'friendly_name': 'henk Temperature',
|
'friendly_name': 'henk Hydration',
|
||||||
|
'icon': 'mdi:water',
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'sensor.henk_temperature',
|
'entity_id': 'sensor.henk_hydration',
|
||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': '40',
|
'state': '0.95',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.7
|
# name: test_all_entities[sensor.henk_intracellular_water]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'device_class': 'temperature',
|
'device_class': 'weight',
|
||||||
'friendly_name': 'henk Body temperature',
|
'friendly_name': 'henk Intracellular water',
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'sensor.henk_body_temperature',
|
'entity_id': 'sensor.henk_intracellular_water',
|
||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': '40',
|
'state': '100',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.8
|
# name: test_all_entities[sensor.henk_light_sleep]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk Light sleep',
|
||||||
|
'icon': 'mdi:sleep',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_light_sleep',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '58440',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_maximum_heart_rate]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Maximum heart rate',
|
||||||
|
'icon': 'mdi:heart-pulse',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'bpm',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_maximum_heart_rate',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '108',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_maximum_respiratory_rate]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Maximum respiratory rate',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'br/min',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_maximum_respiratory_rate',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '20',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_minimum_heart_rate]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Minimum heart rate',
|
||||||
|
'icon': 'mdi:heart-pulse',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'bpm',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_minimum_heart_rate',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '58',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_minimum_respiratory_rate]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Minimum respiratory rate',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'br/min',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_minimum_respiratory_rate',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '10',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_muscle_mass]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'weight',
|
||||||
|
'friendly_name': 'henk Muscle mass',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_muscle_mass',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '50',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_pulse_wave_velocity]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'speed',
|
||||||
|
'friendly_name': 'henk Pulse wave velocity',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfSpeed.METERS_PER_SECOND: 'm/s'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_pulse_wave_velocity',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '100',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_rem_sleep]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk REM sleep',
|
||||||
|
'icon': 'mdi:sleep',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_rem_sleep',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '17280',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_skin_temperature]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'device_class': 'temperature',
|
'device_class': 'temperature',
|
||||||
|
@ -531,17 +357,237 @@
|
||||||
'state': '20',
|
'state': '20',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
# name: test_all_entities.9
|
# name: test_all_entities[sensor.henk_sleep_goal]
|
||||||
StateSnapshot({
|
StateSnapshot({
|
||||||
'attributes': ReadOnlyDict({
|
'attributes': ReadOnlyDict({
|
||||||
'friendly_name': 'henk Fat ratio',
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk Sleep goal',
|
||||||
|
'icon': 'mdi:bed-clock',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_sleep_goal',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '28800',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_sleep_score]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Sleep score',
|
||||||
|
'icon': 'mdi:medal',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'points',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_sleep_score',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '90',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_snoring]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Snoring',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_snoring',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '1044',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_snoring_episode_count]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Snoring episode count',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_snoring_episode_count',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '87',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_spo2]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk SpO2',
|
||||||
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
'unit_of_measurement': '%',
|
'unit_of_measurement': '%',
|
||||||
}),
|
}),
|
||||||
'context': <ANY>,
|
'context': <ANY>,
|
||||||
'entity_id': 'sensor.henk_fat_ratio',
|
'entity_id': 'sensor.henk_spo2',
|
||||||
'last_changed': <ANY>,
|
'last_changed': <ANY>,
|
||||||
'last_updated': <ANY>,
|
'last_updated': <ANY>,
|
||||||
'state': '0.07',
|
'state': '0.95',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_step_goal]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Step goal',
|
||||||
|
'icon': 'mdi:shoe-print',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'Steps',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_step_goal',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '10000',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_systolic_blood_pressure]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Systolic blood pressure',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'mmhg',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_systolic_blood_pressure',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '100',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_temperature]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'temperature',
|
||||||
|
'friendly_name': 'henk Temperature',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_temperature',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '40',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_time_to_sleep]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk Time to sleep',
|
||||||
|
'icon': 'mdi:sleep',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_time_to_sleep',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '780',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_time_to_wakeup]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk Time to wakeup',
|
||||||
|
'icon': 'mdi:sleep-off',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_time_to_wakeup',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '996',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_vascular_age]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Vascular age',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_vascular_age',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '100',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_vo2_max]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk VO2 max',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'ml/min/kg',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_vo2_max',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '100',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_wakeup_count]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'friendly_name': 'henk Wakeup count',
|
||||||
|
'icon': 'mdi:sleep-off',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': 'times',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_wakeup_count',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '8',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_wakeup_time]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'duration',
|
||||||
|
'friendly_name': 'henk Wakeup time',
|
||||||
|
'icon': 'mdi:sleep-off',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_wakeup_time',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '3468',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_weight]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'weight',
|
||||||
|
'friendly_name': 'henk Weight',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_weight',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '70',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_all_entities[sensor.henk_weight_goal]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'weight',
|
||||||
|
'friendly_name': 'henk Weight goal',
|
||||||
|
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
|
||||||
|
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'sensor.henk_weight_goal',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': '70.5',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
from aiowithings import MeasurementGroup
|
from aiowithings import Goals, MeasurementGroup
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
import pytest
|
import pytest
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
|
@ -38,7 +38,9 @@ async def test_all_entities(
|
||||||
|
|
||||||
assert entity_entries
|
assert entity_entries
|
||||||
for entity_entry in entity_entries:
|
for entity_entry in entity_entries:
|
||||||
assert hass.states.get(entity_entry.entity_id) == snapshot
|
assert hass.states.get(entity_entry.entity_id) == snapshot(
|
||||||
|
name=entity_entry.entity_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_update_failed(
|
async def test_update_failed(
|
||||||
|
@ -135,3 +137,29 @@ async def test_update_new_measurement_creates_new_sensor(
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("sensor.henk_fat_mass") is not None
|
assert hass.states.get("sensor.henk_fat_mass") is not None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_update_new_goals_creates_new_sensor(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
withings: AsyncMock,
|
||||||
|
polling_config_entry: MockConfigEntry,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
) -> None:
|
||||||
|
"""Test fetching new goals will add a new sensor."""
|
||||||
|
goals_json = load_json_object_fixture("withings/goals_1.json")
|
||||||
|
goals = Goals.from_api(goals_json)
|
||||||
|
withings.get_goals.return_value = goals
|
||||||
|
await setup_integration(hass, polling_config_entry, False)
|
||||||
|
|
||||||
|
assert hass.states.get("sensor.henk_step_goal") is None
|
||||||
|
assert hass.states.get("sensor.henk_weight_goal") is not None
|
||||||
|
|
||||||
|
goals_json = load_json_object_fixture("withings/goals.json")
|
||||||
|
goals = Goals.from_api(goals_json)
|
||||||
|
withings.get_goals.return_value = goals
|
||||||
|
|
||||||
|
freezer.tick(timedelta(hours=1))
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert hass.states.get("sensor.henk_step_goal") is not None
|
||||||
|
|
Loading…
Reference in New Issue