mirror of
https://github.com/home-assistant/core.git
synced 2026-08-10 07:12:54 +01:00
Update Google Health sensor units to be more reasonable (#177657)
This commit is contained in:
@@ -27,6 +27,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM, UnitSystem
|
||||
|
||||
from . import GoogleHealthConfigEntry
|
||||
from .const import DOMAIN
|
||||
@@ -50,6 +51,7 @@ class GoogleHealthSensorEntityDescription[
|
||||
"""Class describing Google Health sensor entities."""
|
||||
|
||||
value_fn: Callable[[Any], _ValueT]
|
||||
suggested_unit_fn: Callable[[UnitSystem], str | None] | None = None
|
||||
|
||||
|
||||
ACTIVITY_SENSORS: list[
|
||||
@@ -69,6 +71,11 @@ ACTIVITY_SENSORS: list[
|
||||
value_fn=lambda data: (
|
||||
data.distance.millimeters_sum / 1000.0 if data and data.distance else 0.0
|
||||
),
|
||||
suggested_unit_fn=lambda units: (
|
||||
UnitOfLength.MILES
|
||||
if units is US_CUSTOMARY_SYSTEM
|
||||
else UnitOfLength.KILOMETERS
|
||||
),
|
||||
),
|
||||
GoogleHealthSensorEntityDescription[GoogleHealthActivityCoordinator, float](
|
||||
key="active_calories",
|
||||
@@ -109,6 +116,9 @@ BODY_SENSORS: list[
|
||||
value_fn=lambda data: (
|
||||
data.weight.weight_grams / 1000.0 if data and data.weight else None
|
||||
),
|
||||
suggested_unit_fn=lambda units: (
|
||||
UnitOfMass.POUNDS if units is US_CUSTOMARY_SYSTEM else None
|
||||
),
|
||||
),
|
||||
GoogleHealthSensorEntityDescription[GoogleHealthBodyCoordinator, int | None](
|
||||
key="resting_heart_rate",
|
||||
@@ -212,6 +222,9 @@ NUTRITION_SENSORS: list[
|
||||
if data and data.hydration and data.hydration.amount_consumed
|
||||
else 0.0
|
||||
),
|
||||
suggested_unit_fn=lambda units: (
|
||||
UnitOfVolume.FLUID_OUNCES if units is US_CUSTOMARY_SYSTEM else None
|
||||
),
|
||||
),
|
||||
GoogleHealthSensorEntityDescription[GoogleHealthNutritionCoordinator, float](
|
||||
key="calories_consumed",
|
||||
@@ -345,6 +358,15 @@ class GoogleHealthSensor[_CoordinatorT: GoogleHealthDataUpdateCoordinator[Any]](
|
||||
"""Return the state of the sensor."""
|
||||
return cast(StateType, self.entity_description.value_fn(self.coordinator.data))
|
||||
|
||||
@property
|
||||
@override
|
||||
def suggested_unit_of_measurement(self) -> str | None:
|
||||
"""Return the suggested unit of measurement."""
|
||||
if (suggested_unit_fn := self.entity_description.suggested_unit_fn) is not None:
|
||||
return suggested_unit_fn(self.hass.config.units)
|
||||
|
||||
return super().suggested_unit_of_measurement
|
||||
|
||||
|
||||
class GoogleHealthDeviceSensor(
|
||||
CoordinatorEntity[GoogleHealthDeviceCoordinator], SensorEntity
|
||||
|
||||
@@ -402,6 +402,9 @@
|
||||
'sensor': dict({
|
||||
'suggested_display_precision': 2,
|
||||
}),
|
||||
'sensor.private': dict({
|
||||
'suggested_unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
|
||||
}),
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DISTANCE: 'distance'>,
|
||||
'original_icon': None,
|
||||
@@ -412,7 +415,7 @@
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': '01J0BC4QM2YBRP6H5G933CETT7_distance',
|
||||
'unit_of_measurement': <UnitOfLength.METERS: 'm'>,
|
||||
'unit_of_measurement': <UnitOfLength.KILOMETERS: 'km'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.google_health_distance-state]
|
||||
@@ -421,14 +424,14 @@
|
||||
<EntityStateAttribute.DEVICE_CLASS: 'device_class'>: 'distance',
|
||||
<EntityStateAttribute.FRIENDLY_NAME: 'friendly_name'>: 'Google Health Distance',
|
||||
<SensorEntityCapabilityAttribute.STATE_CLASS: 'state_class'>: <SensorStateClass.TOTAL_INCREASING: 'total_increasing'>,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfLength.METERS: 'm'>,
|
||||
<EntityStateAttribute.UNIT_OF_MEASUREMENT: 'unit_of_measurement'>: <UnitOfLength.KILOMETERS: 'km'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.google_health_distance',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '5000.0',
|
||||
'state': '5.0',
|
||||
})
|
||||
# ---
|
||||
# name: test_all_entities[sensor.google_health_floors-entry]
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
"""Tests for Google Health sensor platform."""
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from google_health_api.model import ListDataPointResult, _ListDataPointsModel
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.unit_system import (
|
||||
METRIC_SYSTEM,
|
||||
US_CUSTOMARY_SYSTEM,
|
||||
UnitSystem,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
@@ -80,12 +85,58 @@ async def test_sensor_empty_sleep(
|
||||
integration_setup: Callable[[], Awaitable[bool]],
|
||||
) -> None:
|
||||
"""Test sleep sensors when the sleep endpoint returns no data."""
|
||||
mock_google_health_client.sleep.list.return_value = ListDataPointResult(
|
||||
_ListDataPointsModel(data_points=[])
|
||||
)
|
||||
mock_google_health_client.sleep.list.return_value = MagicMock(data_points=[])
|
||||
|
||||
assert await integration_setup()
|
||||
|
||||
time_asleep_state = hass.states.get("sensor.google_health_time_asleep")
|
||||
assert time_asleep_state is not None
|
||||
assert time_asleep_state.state == "unknown"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("unit_system", "expected_sensors"),
|
||||
[
|
||||
pytest.param(
|
||||
METRIC_SYSTEM,
|
||||
{
|
||||
"sensor.google_health_weight": (pytest.approx(80.0), "kg"),
|
||||
"sensor.google_health_distance": (pytest.approx(5.0), "km"),
|
||||
"sensor.google_health_water_intake": (pytest.approx(2.5), "L"),
|
||||
},
|
||||
id="metric",
|
||||
),
|
||||
pytest.param(
|
||||
US_CUSTOMARY_SYSTEM,
|
||||
{
|
||||
"sensor.google_health_weight": (pytest.approx(176.37, abs=1e-2), "lb"),
|
||||
"sensor.google_health_distance": (
|
||||
pytest.approx(3.11, abs=1e-2),
|
||||
"mi",
|
||||
),
|
||||
"sensor.google_health_water_intake": (
|
||||
pytest.approx(84.54, abs=1e-1),
|
||||
"fl. oz.",
|
||||
),
|
||||
},
|
||||
id="us_customary",
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.usefixtures("mock_google_health_client")
|
||||
async def test_sensor_unit_conversions(
|
||||
hass: HomeAssistant,
|
||||
integration_setup: Callable[[], Awaitable[bool]],
|
||||
unit_system: UnitSystem,
|
||||
expected_sensors: dict[str, tuple[Any, str]],
|
||||
) -> None:
|
||||
"""Test sensors dynamically convert states and units under different unit systems."""
|
||||
hass.config.units = unit_system
|
||||
|
||||
assert await integration_setup()
|
||||
|
||||
for entity_id, (expected_state, expected_unit) in expected_sensors.items():
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert float(state.state) == expected_state
|
||||
assert state.attributes.get("unit_of_measurement") == expected_unit
|
||||
|
||||
Reference in New Issue
Block a user