core/tests/components/withings/test_sensor.py

401 lines
16 KiB
Python

"""Tests for the Withings component."""
from typing import Any
from unittest.mock import patch
import arrow
from withings_api.common import (
GetSleepSummaryData,
GetSleepSummarySerie,
MeasureGetMeasGroup,
MeasureGetMeasGroupAttrib,
MeasureGetMeasGroupCategory,
MeasureGetMeasMeasure,
MeasureGetMeasResponse,
MeasureType,
NotifyAppli,
SleepGetSummaryResponse,
SleepModel,
)
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.withings.common import WithingsEntityDescription
from homeassistant.components.withings.const import Measurement
from homeassistant.components.withings.sensor import SENSORS
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.util import dt as dt_util
from .common import ComponentFactory, async_get_entity_id, new_profile_config
WITHINGS_MEASUREMENTS_MAP: dict[Measurement, WithingsEntityDescription] = {
attr.measurement: attr for attr in SENSORS
}
PERSON0 = new_profile_config(
"person0",
0,
api_response_measure_get_meas=MeasureGetMeasResponse(
measuregrps=(
MeasureGetMeasGroup(
attrib=MeasureGetMeasGroupAttrib.DEVICE_ENTRY_FOR_USER,
category=MeasureGetMeasGroupCategory.REAL,
created=arrow.utcnow().shift(hours=-1),
date=arrow.utcnow().shift(hours=-1),
deviceid="DEV_ID",
grpid=1,
measures=(
MeasureGetMeasMeasure(type=MeasureType.WEIGHT, unit=0, value=70),
MeasureGetMeasMeasure(
type=MeasureType.FAT_MASS_WEIGHT, unit=0, value=5
),
MeasureGetMeasMeasure(
type=MeasureType.FAT_FREE_MASS, unit=0, value=60
),
MeasureGetMeasMeasure(
type=MeasureType.MUSCLE_MASS, unit=0, value=50
),
MeasureGetMeasMeasure(type=MeasureType.BONE_MASS, unit=0, value=10),
MeasureGetMeasMeasure(type=MeasureType.HEIGHT, unit=0, value=2),
MeasureGetMeasMeasure(
type=MeasureType.TEMPERATURE, unit=0, value=40
),
MeasureGetMeasMeasure(
type=MeasureType.BODY_TEMPERATURE, unit=0, value=40
),
MeasureGetMeasMeasure(
type=MeasureType.SKIN_TEMPERATURE, unit=0, value=20
),
MeasureGetMeasMeasure(
type=MeasureType.FAT_RATIO, unit=-3, value=70
),
MeasureGetMeasMeasure(
type=MeasureType.DIASTOLIC_BLOOD_PRESSURE, unit=0, value=70
),
MeasureGetMeasMeasure(
type=MeasureType.SYSTOLIC_BLOOD_PRESSURE, unit=0, value=100
),
MeasureGetMeasMeasure(
type=MeasureType.HEART_RATE, unit=0, value=60
),
MeasureGetMeasMeasure(type=MeasureType.SP02, unit=-2, value=95),
MeasureGetMeasMeasure(
type=MeasureType.HYDRATION, unit=-2, value=95
),
MeasureGetMeasMeasure(
type=MeasureType.PULSE_WAVE_VELOCITY, unit=0, value=100
),
),
),
MeasureGetMeasGroup(
attrib=MeasureGetMeasGroupAttrib.DEVICE_ENTRY_FOR_USER,
category=MeasureGetMeasGroupCategory.REAL,
created=arrow.utcnow().shift(hours=-2),
date=arrow.utcnow().shift(hours=-2),
deviceid="DEV_ID",
grpid=1,
measures=(
MeasureGetMeasMeasure(type=MeasureType.WEIGHT, unit=0, value=71),
MeasureGetMeasMeasure(
type=MeasureType.FAT_MASS_WEIGHT, unit=0, value=51
),
MeasureGetMeasMeasure(
type=MeasureType.FAT_FREE_MASS, unit=0, value=61
),
MeasureGetMeasMeasure(
type=MeasureType.MUSCLE_MASS, unit=0, value=51
),
MeasureGetMeasMeasure(type=MeasureType.BONE_MASS, unit=0, value=11),
MeasureGetMeasMeasure(type=MeasureType.HEIGHT, unit=0, value=21),
MeasureGetMeasMeasure(
type=MeasureType.TEMPERATURE, unit=0, value=41
),
MeasureGetMeasMeasure(
type=MeasureType.BODY_TEMPERATURE, unit=0, value=41
),
MeasureGetMeasMeasure(
type=MeasureType.SKIN_TEMPERATURE, unit=0, value=21
),
MeasureGetMeasMeasure(
type=MeasureType.FAT_RATIO, unit=-3, value=71
),
MeasureGetMeasMeasure(
type=MeasureType.DIASTOLIC_BLOOD_PRESSURE, unit=0, value=71
),
MeasureGetMeasMeasure(
type=MeasureType.SYSTOLIC_BLOOD_PRESSURE, unit=0, value=101
),
MeasureGetMeasMeasure(
type=MeasureType.HEART_RATE, unit=0, value=61
),
MeasureGetMeasMeasure(type=MeasureType.SP02, unit=-2, value=96),
MeasureGetMeasMeasure(
type=MeasureType.HYDRATION, unit=-2, value=96
),
MeasureGetMeasMeasure(
type=MeasureType.PULSE_WAVE_VELOCITY, unit=0, value=101
),
),
),
MeasureGetMeasGroup(
attrib=MeasureGetMeasGroupAttrib.DEVICE_ENTRY_FOR_USER_AMBIGUOUS,
category=MeasureGetMeasGroupCategory.REAL,
created=arrow.utcnow(),
date=arrow.utcnow(),
deviceid="DEV_ID",
grpid=1,
measures=(
MeasureGetMeasMeasure(type=MeasureType.WEIGHT, unit=0, value=71),
MeasureGetMeasMeasure(
type=MeasureType.FAT_MASS_WEIGHT, unit=0, value=4
),
MeasureGetMeasMeasure(
type=MeasureType.FAT_FREE_MASS, unit=0, value=40
),
MeasureGetMeasMeasure(
type=MeasureType.MUSCLE_MASS, unit=0, value=51
),
MeasureGetMeasMeasure(type=MeasureType.BONE_MASS, unit=0, value=11),
MeasureGetMeasMeasure(type=MeasureType.HEIGHT, unit=0, value=201),
MeasureGetMeasMeasure(
type=MeasureType.TEMPERATURE, unit=0, value=41
),
MeasureGetMeasMeasure(
type=MeasureType.BODY_TEMPERATURE, unit=0, value=34
),
MeasureGetMeasMeasure(
type=MeasureType.SKIN_TEMPERATURE, unit=0, value=21
),
MeasureGetMeasMeasure(
type=MeasureType.FAT_RATIO, unit=-3, value=71
),
MeasureGetMeasMeasure(
type=MeasureType.DIASTOLIC_BLOOD_PRESSURE, unit=0, value=71
),
MeasureGetMeasMeasure(
type=MeasureType.SYSTOLIC_BLOOD_PRESSURE, unit=0, value=101
),
MeasureGetMeasMeasure(
type=MeasureType.HEART_RATE, unit=0, value=61
),
MeasureGetMeasMeasure(type=MeasureType.SP02, unit=-2, value=98),
MeasureGetMeasMeasure(
type=MeasureType.HYDRATION, unit=-2, value=96
),
MeasureGetMeasMeasure(
type=MeasureType.PULSE_WAVE_VELOCITY, unit=0, value=102
),
),
),
),
more=False,
timezone=dt_util.UTC,
updatetime=arrow.get("2019-08-01"),
offset=0,
),
api_response_sleep_get_summary=SleepGetSummaryResponse(
more=False,
offset=0,
series=(
GetSleepSummarySerie(
timezone=dt_util.UTC,
model=SleepModel.SLEEP_MONITOR,
startdate=arrow.get("2019-02-01"),
enddate=arrow.get("2019-02-01"),
date=arrow.get("2019-02-01"),
modified=arrow.get(12345),
data=GetSleepSummaryData(
breathing_disturbances_intensity=110,
deepsleepduration=111,
durationtosleep=112,
durationtowakeup=113,
hr_average=114,
hr_max=115,
hr_min=116,
lightsleepduration=117,
remsleepduration=118,
rr_average=119,
rr_max=120,
rr_min=121,
sleep_score=122,
snoring=123,
snoringepisodecount=124,
wakeupcount=125,
wakeupduration=126,
),
),
GetSleepSummarySerie(
timezone=dt_util.UTC,
model=SleepModel.SLEEP_MONITOR,
startdate=arrow.get("2019-02-01"),
enddate=arrow.get("2019-02-01"),
date=arrow.get("2019-02-01"),
modified=arrow.get(12345),
data=GetSleepSummaryData(
breathing_disturbances_intensity=210,
deepsleepduration=211,
durationtosleep=212,
durationtowakeup=213,
hr_average=214,
hr_max=215,
hr_min=216,
lightsleepduration=217,
remsleepduration=218,
rr_average=219,
rr_max=220,
rr_min=221,
sleep_score=222,
snoring=223,
snoringepisodecount=224,
wakeupcount=225,
wakeupduration=226,
),
),
),
),
)
EXPECTED_DATA = (
(PERSON0, Measurement.WEIGHT_KG, 70.0),
(PERSON0, Measurement.FAT_MASS_KG, 5.0),
(PERSON0, Measurement.FAT_FREE_MASS_KG, 60.0),
(PERSON0, Measurement.MUSCLE_MASS_KG, 50.0),
(PERSON0, Measurement.BONE_MASS_KG, 10.0),
(PERSON0, Measurement.HEIGHT_M, 2.0),
(PERSON0, Measurement.FAT_RATIO_PCT, 0.07),
(PERSON0, Measurement.DIASTOLIC_MMHG, 70.0),
(PERSON0, Measurement.SYSTOLIC_MMGH, 100.0),
(PERSON0, Measurement.HEART_PULSE_BPM, 60.0),
(PERSON0, Measurement.SPO2_PCT, 0.95),
(PERSON0, Measurement.HYDRATION, 0.95),
(PERSON0, Measurement.PWV, 100.0),
(PERSON0, Measurement.SLEEP_BREATHING_DISTURBANCES_INTENSITY, 160.0),
(PERSON0, Measurement.SLEEP_DEEP_DURATION_SECONDS, 322),
(PERSON0, Measurement.SLEEP_HEART_RATE_AVERAGE, 164.0),
(PERSON0, Measurement.SLEEP_HEART_RATE_MAX, 165.0),
(PERSON0, Measurement.SLEEP_HEART_RATE_MIN, 166.0),
(PERSON0, Measurement.SLEEP_LIGHT_DURATION_SECONDS, 334),
(PERSON0, Measurement.SLEEP_REM_DURATION_SECONDS, 336),
(PERSON0, Measurement.SLEEP_RESPIRATORY_RATE_AVERAGE, 169.0),
(PERSON0, Measurement.SLEEP_RESPIRATORY_RATE_MAX, 170.0),
(PERSON0, Measurement.SLEEP_RESPIRATORY_RATE_MIN, 171.0),
(PERSON0, Measurement.SLEEP_SCORE, 222),
(PERSON0, Measurement.SLEEP_SNORING, 173.0),
(PERSON0, Measurement.SLEEP_SNORING_EPISODE_COUNT, 348),
(PERSON0, Measurement.SLEEP_TOSLEEP_DURATION_SECONDS, 162.0),
(PERSON0, Measurement.SLEEP_TOWAKEUP_DURATION_SECONDS, 163.0),
(PERSON0, Measurement.SLEEP_WAKEUP_COUNT, 350),
(PERSON0, Measurement.SLEEP_WAKEUP_DURATION_SECONDS, 176.0),
)
def async_assert_state_equals(
entity_id: str,
state_obj: State,
expected: Any,
description: WithingsEntityDescription,
) -> None:
"""Assert at given state matches what is expected."""
assert state_obj, f"Expected entity {entity_id} to exist but it did not"
assert state_obj.state == str(expected), (
f"Expected {expected} but was {state_obj.state} "
f"for measure {description.measurement}, {entity_id}"
)
async def test_sensor_default_enabled_entities(
hass: HomeAssistant, component_factory: ComponentFactory, current_request_with_host
) -> None:
"""Test entities enabled by default."""
entity_registry: EntityRegistry = er.async_get(hass)
await component_factory.configure_component(profile_configs=(PERSON0,))
# Assert entities should not exist yet.
for attribute in SENSORS:
assert not await async_get_entity_id(
hass, attribute, PERSON0.user_id, SENSOR_DOMAIN
)
# person 0
await component_factory.setup_profile(PERSON0.user_id)
# Assert entities should exist.
for attribute in SENSORS:
entity_id = await async_get_entity_id(
hass, attribute, PERSON0.user_id, SENSOR_DOMAIN
)
assert entity_id
assert entity_registry.async_is_registered(entity_id)
resp = await component_factory.call_webhook(PERSON0.user_id, NotifyAppli.SLEEP)
assert resp.message_code == 0
resp = await component_factory.call_webhook(PERSON0.user_id, NotifyAppli.WEIGHT)
assert resp.message_code == 0
for person, measurement, expected in EXPECTED_DATA:
attribute = WITHINGS_MEASUREMENTS_MAP[measurement]
entity_id = await async_get_entity_id(
hass, attribute, person.user_id, SENSOR_DOMAIN
)
state_obj = hass.states.get(entity_id)
if attribute.entity_registry_enabled_default:
async_assert_state_equals(entity_id, state_obj, expected, attribute)
else:
assert state_obj is None
# Unload
await component_factory.unload(PERSON0)
async def test_all_entities(
hass: HomeAssistant, component_factory: ComponentFactory, current_request_with_host
) -> None:
"""Test all entities."""
entity_registry: EntityRegistry = er.async_get(hass)
with patch(
"homeassistant.components.withings.sensor.BaseWithingsSensor.entity_registry_enabled_default"
) as enabled_by_default_mock:
enabled_by_default_mock.return_value = True
await component_factory.configure_component(profile_configs=(PERSON0,))
# Assert entities should not exist yet.
for attribute in SENSORS:
assert not await async_get_entity_id(
hass, attribute, PERSON0.user_id, SENSOR_DOMAIN
)
# person 0
await component_factory.setup_profile(PERSON0.user_id)
# Assert entities should exist.
for attribute in SENSORS:
entity_id = await async_get_entity_id(
hass, attribute, PERSON0.user_id, SENSOR_DOMAIN
)
assert entity_id
assert entity_registry.async_is_registered(entity_id)
resp = await component_factory.call_webhook(PERSON0.user_id, NotifyAppli.SLEEP)
assert resp.message_code == 0
resp = await component_factory.call_webhook(PERSON0.user_id, NotifyAppli.WEIGHT)
assert resp.message_code == 0
for person, measurement, expected in EXPECTED_DATA:
attribute = WITHINGS_MEASUREMENTS_MAP[measurement]
entity_id = await async_get_entity_id(
hass, attribute, person.user_id, SENSOR_DOMAIN
)
state_obj = hass.states.get(entity_id)
async_assert_state_equals(entity_id, state_obj, expected, attribute)
# Unload
await component_factory.unload(PERSON0)