Add more test coverage for fitbit sensors (#100776)

This commit is contained in:
Allen Porter 2023-09-23 15:38:53 -07:00 committed by GitHub
parent 451c085587
commit ae29ddee74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 530 additions and 20 deletions

View File

@ -104,13 +104,19 @@ async def mock_sensor_platform_setup(
@pytest.fixture(name="profile_id")
async def mock_profile_id() -> str:
def mock_profile_id() -> str:
"""Fixture for the profile id returned from the API response."""
return PROFILE_USER_ID
@pytest.fixture(name="profile_locale")
def mock_profile_locale() -> str:
"""Fixture to set the API response for the user profile."""
return "en_US"
@pytest.fixture(name="profile", autouse=True)
async def mock_profile(requests_mock: Mocker, profile_id: str) -> None:
def mock_profile(requests_mock: Mocker, profile_id: str, profile_locale: str) -> None:
"""Fixture to setup fake requests made to Fitbit API during config flow."""
requests_mock.register_uri(
"GET",
@ -120,20 +126,20 @@ async def mock_profile(requests_mock: Mocker, profile_id: str) -> None:
"user": {
"encodedId": profile_id,
"fullName": "My name",
"locale": "en_US",
"locale": profile_locale,
},
},
)
@pytest.fixture(name="devices_response")
async def mock_device_response() -> list[dict[str, Any]]:
def mock_device_response() -> list[dict[str, Any]]:
"""Return the list of devices."""
return []
@pytest.fixture(autouse=True)
async def mock_devices(requests_mock: Mocker, devices_response: dict[str, Any]) -> None:
def mock_devices(requests_mock: Mocker, devices_response: dict[str, Any]) -> None:
"""Fixture to setup fake device responses."""
requests_mock.register_uri(
"GET",
@ -151,7 +157,7 @@ def timeseries_response(resource: str, value: str) -> dict[str, Any]:
@pytest.fixture(name="register_timeseries")
async def mock_register_timeseries(
def mock_register_timeseries(
requests_mock: Mocker,
) -> Callable[[str, dict[str, Any]], None]:
"""Fixture to setup fake timeseries API responses."""

View File

@ -0,0 +1,280 @@
# serializer version: 1
# name: test_sensors[monitored_resources0-sensor.activity_calories-activities/activityCalories-135]
tuple(
'135',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Activity Calories',
'icon': 'mdi:fire',
'unit_of_measurement': 'cal',
}),
'fitbit-api-user-id-1_activities/activityCalories',
)
# ---
# name: test_sensors[monitored_resources1-sensor.calories-activities/calories-139]
tuple(
'139',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Calories',
'icon': 'mdi:fire',
'unit_of_measurement': 'cal',
}),
'fitbit-api-user-id-1_activities/calories',
)
# ---
# name: test_sensors[monitored_resources10-sensor.steps-activities/steps-5600]
tuple(
'5600',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Steps',
'icon': 'mdi:walk',
'unit_of_measurement': 'steps',
}),
'fitbit-api-user-id-1_activities/steps',
)
# ---
# name: test_sensors[monitored_resources11-sensor.weight-body/weight-175]
tuple(
'175.0',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'weight',
'friendly_name': 'Weight',
'icon': 'mdi:human',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': <UnitOfMass.KILOGRAMS: 'kg'>,
}),
'fitbit-api-user-id-1_body/weight',
)
# ---
# name: test_sensors[monitored_resources12-sensor.body_fat-body/fat-18]
tuple(
'18.0',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Body Fat',
'icon': 'mdi:human',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'fitbit-api-user-id-1_body/fat',
)
# ---
# name: test_sensors[monitored_resources13-sensor.bmi-body/bmi-23.7]
tuple(
'23.7',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'BMI',
'icon': 'mdi:human',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': 'BMI',
}),
'fitbit-api-user-id-1_body/bmi',
)
# ---
# name: test_sensors[monitored_resources14-sensor.awakenings_count-sleep/awakeningsCount-7]
tuple(
'7',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Awakenings Count',
'icon': 'mdi:sleep',
'unit_of_measurement': 'times awaken',
}),
'fitbit-api-user-id-1_sleep/awakeningsCount',
)
# ---
# name: test_sensors[monitored_resources15-sensor.sleep_efficiency-sleep/efficiency-80]
tuple(
'80',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Sleep Efficiency',
'icon': 'mdi:sleep',
'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>,
'unit_of_measurement': '%',
}),
'fitbit-api-user-id-1_sleep/efficiency',
)
# ---
# name: test_sensors[monitored_resources16-sensor.minutes_after_wakeup-sleep/minutesAfterWakeup-17]
tuple(
'17',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Minutes After Wakeup',
'icon': 'mdi:sleep',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_sleep/minutesAfterWakeup',
)
# ---
# name: test_sensors[monitored_resources17-sensor.sleep_minutes_asleep-sleep/minutesAsleep-360]
tuple(
'360',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Sleep Minutes Asleep',
'icon': 'mdi:sleep',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_sleep/minutesAsleep',
)
# ---
# name: test_sensors[monitored_resources18-sensor.sleep_minutes_awake-sleep/minutesAwake-35]
tuple(
'35',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Sleep Minutes Awake',
'icon': 'mdi:sleep',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_sleep/minutesAwake',
)
# ---
# name: test_sensors[monitored_resources19-sensor.sleep_minutes_to_fall_asleep-sleep/minutesToFallAsleep-35]
tuple(
'35',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Sleep Minutes to Fall Asleep',
'icon': 'mdi:sleep',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_sleep/minutesToFallAsleep',
)
# ---
# name: test_sensors[monitored_resources2-sensor.distance-activities/distance-12.7]
tuple(
'12.70',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'distance',
'friendly_name': 'Distance',
'icon': 'mdi:map-marker',
'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
}),
'fitbit-api-user-id-1_activities/distance',
)
# ---
# name: test_sensors[monitored_resources20-sensor.sleep_start_time-sleep/startTime-2020-01-27T00:17:30.000]
tuple(
'2020-01-27T00:17:30.000',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Sleep Start Time',
'icon': 'mdi:clock',
}),
'fitbit-api-user-id-1_sleep/startTime',
)
# ---
# name: test_sensors[monitored_resources21-sensor.sleep_time_in_bed-sleep/timeInBed-462]
tuple(
'462',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Sleep Time in Bed',
'icon': 'mdi:hotel',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_sleep/timeInBed',
)
# ---
# name: test_sensors[monitored_resources3-sensor.elevation-activities/elevation-7600.24]
tuple(
'7600.24',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'distance',
'friendly_name': 'Elevation',
'icon': 'mdi:walk',
'unit_of_measurement': <UnitOfLength.METERS: 'm'>,
}),
'fitbit-api-user-id-1_activities/elevation',
)
# ---
# name: test_sensors[monitored_resources4-sensor.floors-activities/floors-8]
tuple(
'8',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Floors',
'icon': 'mdi:walk',
'unit_of_measurement': 'floors',
}),
'fitbit-api-user-id-1_activities/floors',
)
# ---
# name: test_sensors[monitored_resources5-sensor.resting_heart_rate-activities/heart-api_value5]
tuple(
'76',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'friendly_name': 'Resting Heart Rate',
'icon': 'mdi:heart-pulse',
'unit_of_measurement': 'bpm',
}),
'fitbit-api-user-id-1_activities/heart',
)
# ---
# name: test_sensors[monitored_resources6-sensor.minutes_fairly_active-activities/minutesFairlyActive-35]
tuple(
'35',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Minutes Fairly Active',
'icon': 'mdi:walk',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_activities/minutesFairlyActive',
)
# ---
# name: test_sensors[monitored_resources7-sensor.minutes_lightly_active-activities/minutesLightlyActive-95]
tuple(
'95',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Minutes Lightly Active',
'icon': 'mdi:walk',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_activities/minutesLightlyActive',
)
# ---
# name: test_sensors[monitored_resources8-sensor.minutes_sedentary-activities/minutesSedentary-18]
tuple(
'18',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Minutes Sedentary',
'icon': 'mdi:seat-recline-normal',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_activities/minutesSedentary',
)
# ---
# name: test_sensors[monitored_resources9-sensor.minutes_very_active-activities/minutesVeryActive-20]
tuple(
'20',
ReadOnlyDict({
'attribution': 'Data provided by Fitbit.com',
'device_class': 'duration',
'friendly_name': 'Minutes Very Active',
'icon': 'mdi:run',
'unit_of_measurement': <UnitOfTime.MINUTES: 'min'>,
}),
'fitbit-api-user-id-1_activities/minutesVeryActive',
)
# ---

View File

@ -5,10 +5,12 @@ from collections.abc import Awaitable, Callable
from typing import Any
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from .conftest import timeseries_response
from .conftest import PROFILE_USER_ID, timeseries_response
DEVICE_RESPONSE_CHARGE_2 = {
"battery": "Medium",
@ -31,30 +33,169 @@ DEVICE_RESPONSE_ARIA_AIR = {
@pytest.mark.parametrize(
"monitored_resources",
[["activities/steps"]],
(
"monitored_resources",
"entity_id",
"api_resource",
"api_value",
),
[
(
["activities/activityCalories"],
"sensor.activity_calories",
"activities/activityCalories",
"135",
),
(
["activities/calories"],
"sensor.calories",
"activities/calories",
"139",
),
(
["activities/distance"],
"sensor.distance",
"activities/distance",
"12.7",
),
(
["activities/elevation"],
"sensor.elevation",
"activities/elevation",
"7600.24",
),
(
["activities/floors"],
"sensor.floors",
"activities/floors",
"8",
),
(
["activities/heart"],
"sensor.resting_heart_rate",
"activities/heart",
{"restingHeartRate": 76},
),
(
["activities/minutesFairlyActive"],
"sensor.minutes_fairly_active",
"activities/minutesFairlyActive",
35,
),
(
["activities/minutesLightlyActive"],
"sensor.minutes_lightly_active",
"activities/minutesLightlyActive",
95,
),
(
["activities/minutesSedentary"],
"sensor.minutes_sedentary",
"activities/minutesSedentary",
18,
),
(
["activities/minutesVeryActive"],
"sensor.minutes_very_active",
"activities/minutesVeryActive",
20,
),
(
["activities/steps"],
"sensor.steps",
"activities/steps",
"5600",
),
(
["body/weight"],
"sensor.weight",
"body/weight",
"175",
),
(
["body/fat"],
"sensor.body_fat",
"body/fat",
"18",
),
(
["body/bmi"],
"sensor.bmi",
"body/bmi",
"23.7",
),
(
["sleep/awakeningsCount"],
"sensor.awakenings_count",
"sleep/awakeningsCount",
"7",
),
(
["sleep/efficiency"],
"sensor.sleep_efficiency",
"sleep/efficiency",
"80",
),
(
["sleep/minutesAfterWakeup"],
"sensor.minutes_after_wakeup",
"sleep/minutesAfterWakeup",
"17",
),
(
["sleep/minutesAsleep"],
"sensor.sleep_minutes_asleep",
"sleep/minutesAsleep",
"360",
),
(
["sleep/minutesAwake"],
"sensor.sleep_minutes_awake",
"sleep/minutesAwake",
"35",
),
(
["sleep/minutesToFallAsleep"],
"sensor.sleep_minutes_to_fall_asleep",
"sleep/minutesToFallAsleep",
"35",
),
(
["sleep/startTime"],
"sensor.sleep_start_time",
"sleep/startTime",
"2020-01-27T00:17:30.000",
),
(
["sleep/timeInBed"],
"sensor.sleep_time_in_bed",
"sleep/timeInBed",
"462",
),
],
)
async def test_step_sensor(
async def test_sensors(
hass: HomeAssistant,
sensor_platform_setup: Callable[[], Awaitable[bool]],
register_timeseries: Callable[[str, dict[str, Any]], None],
entity_registry: er.EntityRegistry,
entity_id: str,
api_resource: str,
api_value: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test battery level sensor."""
"""Test sensors."""
register_timeseries(
"activities/steps", timeseries_response("activities-steps", "5600")
api_resource, timeseries_response(api_resource.replace("/", "-"), api_value)
)
await sensor_platform_setup()
state = hass.states.get("sensor.steps")
state = hass.states.get(entity_id)
assert state
assert state.state == "5600"
assert state.attributes == {
"attribution": "Data provided by Fitbit.com",
"friendly_name": "Steps",
"icon": "mdi:walk",
"unit_of_measurement": "steps",
}
entry = entity_registry.async_get(entity_id)
assert entry
assert (state.state, state.attributes, entry.unique_id) == snapshot
@pytest.mark.parametrize(
@ -64,6 +205,7 @@ async def test_step_sensor(
async def test_device_battery_level(
hass: HomeAssistant,
sensor_platform_setup: Callable[[], Awaitable[bool]],
entity_registry: er.EntityRegistry,
) -> None:
"""Test battery level sensor for devices."""
@ -80,6 +222,10 @@ async def test_device_battery_level(
"type": "tracker",
}
entry = entity_registry.async_get("sensor.charge_2_battery")
assert entry
assert entry.unique_id == f"{PROFILE_USER_ID}_devices/battery_816713257"
state = hass.states.get("sensor.aria_air_battery")
assert state
assert state.state == "High"
@ -90,3 +236,81 @@ async def test_device_battery_level(
"model": "Aria Air",
"type": "scale",
}
entity_registry = er.async_get(hass)
entry = entity_registry.async_get("sensor.aria_air_battery")
assert entry
assert entry.unique_id == f"{PROFILE_USER_ID}_devices/battery_016713257"
@pytest.mark.parametrize(
("monitored_resources", "profile_locale", "expected_unit"),
[
(["body/weight"], "en_US", "kg"),
(["body/weight"], "en_GB", "st"),
(["body/weight"], "es_ES", "kg"),
],
)
async def test_profile_local(
hass: HomeAssistant,
sensor_platform_setup: Callable[[], Awaitable[bool]],
register_timeseries: Callable[[str, dict[str, Any]], None],
expected_unit: str,
) -> None:
"""Test the fitbit profile locale impact on unit of measure."""
register_timeseries("body/weight", timeseries_response("body-weight", "175"))
await sensor_platform_setup()
state = hass.states.get("sensor.weight")
assert state
assert state.attributes.get("unit_of_measurement") == expected_unit
@pytest.mark.parametrize(
("sensor_platform_config", "api_response", "expected_state"),
[
(
{"clock_format": "12H", "monitored_resources": ["sleep/startTime"]},
"17:05",
"5:05 PM",
),
(
{"clock_format": "12H", "monitored_resources": ["sleep/startTime"]},
"5:05",
"5:05 AM",
),
(
{"clock_format": "12H", "monitored_resources": ["sleep/startTime"]},
"00:05",
"12:05 AM",
),
(
{"clock_format": "24H", "monitored_resources": ["sleep/startTime"]},
"17:05",
"17:05",
),
(
{"clock_format": "12H", "monitored_resources": ["sleep/startTime"]},
"",
"-",
),
],
)
async def test_sleep_time_clock_format(
hass: HomeAssistant,
sensor_platform_setup: Callable[[], Awaitable[bool]],
register_timeseries: Callable[[str, dict[str, Any]], None],
api_response: str,
expected_state: str,
) -> None:
"""Test the clock format configuration."""
register_timeseries(
"sleep/startTime", timeseries_response("sleep-startTime", api_response)
)
await sensor_platform_setup()
state = hass.states.get("sensor.sleep_start_time")
assert state
assert state.state == expected_state