mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
Simplify fitbit unit system and conversions (#100825)
* Simplify fitbit unit conversions * Use enum values in unit system schema * Use fitbit unit system enums
This commit is contained in:
parent
785618909a
commit
18f29993c5
@ -6,7 +6,9 @@ from typing import Any
|
|||||||
from fitbit import Fitbit
|
from fitbit import Fitbit
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||||
|
|
||||||
|
from .const import FitbitUnitSystem
|
||||||
from .model import FitbitDevice, FitbitProfile
|
from .model import FitbitDevice, FitbitProfile
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@ -19,11 +21,13 @@ class FitbitApi:
|
|||||||
self,
|
self,
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
client: Fitbit,
|
client: Fitbit,
|
||||||
|
unit_system: FitbitUnitSystem | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize Fitbit auth."""
|
"""Initialize Fitbit auth."""
|
||||||
self._hass = hass
|
self._hass = hass
|
||||||
self._profile: FitbitProfile | None = None
|
self._profile: FitbitProfile | None = None
|
||||||
self._client = client
|
self._client = client
|
||||||
|
self._unit_system = unit_system
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def client(self) -> Fitbit:
|
def client(self) -> Fitbit:
|
||||||
@ -32,14 +36,38 @@ class FitbitApi:
|
|||||||
|
|
||||||
def get_user_profile(self) -> FitbitProfile:
|
def get_user_profile(self) -> FitbitProfile:
|
||||||
"""Return the user profile from the API."""
|
"""Return the user profile from the API."""
|
||||||
|
if self._profile is None:
|
||||||
response: dict[str, Any] = self._client.user_profile_get()
|
response: dict[str, Any] = self._client.user_profile_get()
|
||||||
_LOGGER.debug("user_profile_get=%s", response)
|
_LOGGER.debug("user_profile_get=%s", response)
|
||||||
profile = response["user"]
|
profile = response["user"]
|
||||||
return FitbitProfile(
|
self._profile = FitbitProfile(
|
||||||
encoded_id=profile["encodedId"],
|
encoded_id=profile["encodedId"],
|
||||||
full_name=profile["fullName"],
|
full_name=profile["fullName"],
|
||||||
locale=profile.get("locale"),
|
locale=profile.get("locale"),
|
||||||
)
|
)
|
||||||
|
return self._profile
|
||||||
|
|
||||||
|
def get_unit_system(self) -> FitbitUnitSystem:
|
||||||
|
"""Get the unit system to use when fetching timeseries.
|
||||||
|
|
||||||
|
This is used in a couple ways. The first is to determine the request
|
||||||
|
header to use when talking to the fitbit API which changes the
|
||||||
|
units returned by the API. The second is to tell Home Assistant the
|
||||||
|
units set in sensor values for the values returned by the API.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
self._unit_system is not None
|
||||||
|
and self._unit_system != FitbitUnitSystem.LEGACY_DEFAULT
|
||||||
|
):
|
||||||
|
return self._unit_system
|
||||||
|
# Use units consistent with the account user profile or fallback to the
|
||||||
|
# home assistant unit settings.
|
||||||
|
profile = self.get_user_profile()
|
||||||
|
if profile.locale == FitbitUnitSystem.EN_GB:
|
||||||
|
return FitbitUnitSystem.EN_GB
|
||||||
|
if self._hass.config.units is METRIC_SYSTEM:
|
||||||
|
return FitbitUnitSystem.METRIC
|
||||||
|
return FitbitUnitSystem.EN_US
|
||||||
|
|
||||||
def get_devices(self) -> list[FitbitDevice]:
|
def get_devices(self) -> list[FitbitDevice]:
|
||||||
"""Return available devices."""
|
"""Return available devices."""
|
||||||
@ -58,6 +86,10 @@ class FitbitApi:
|
|||||||
|
|
||||||
def get_latest_time_series(self, resource_type: str) -> dict[str, Any]:
|
def get_latest_time_series(self, resource_type: str) -> dict[str, Any]:
|
||||||
"""Return the most recent value from the time series for the specified resource type."""
|
"""Return the most recent value from the time series for the specified resource type."""
|
||||||
|
|
||||||
|
# Set request header based on the configured unit system
|
||||||
|
self._client.system = self.get_unit_system()
|
||||||
|
|
||||||
response: dict[str, Any] = self._client.time_series(resource_type, period="7d")
|
response: dict[str, Any] = self._client.time_series(resource_type, period="7d")
|
||||||
_LOGGER.debug("time_series(%s)=%s", resource_type, response)
|
_LOGGER.debug("time_series(%s)=%s", resource_type, response)
|
||||||
key = resource_type.replace("/", "-")
|
key = resource_type.replace("/", "-")
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
"""Constants for the Fitbit platform."""
|
"""Constants for the Fitbit platform."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from enum import StrEnum
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||||
CONF_CLIENT_ID,
|
|
||||||
CONF_CLIENT_SECRET,
|
|
||||||
UnitOfLength,
|
|
||||||
UnitOfMass,
|
|
||||||
UnitOfTime,
|
|
||||||
UnitOfVolume,
|
|
||||||
)
|
|
||||||
|
|
||||||
DOMAIN: Final = "fitbit"
|
DOMAIN: Final = "fitbit"
|
||||||
|
|
||||||
@ -43,46 +37,31 @@ DEFAULT_CONFIG: Final[dict[str, str]] = {
|
|||||||
}
|
}
|
||||||
DEFAULT_CLOCK_FORMAT: Final = "24H"
|
DEFAULT_CLOCK_FORMAT: Final = "24H"
|
||||||
|
|
||||||
|
|
||||||
FITBIT_MEASUREMENTS: Final[dict[str, dict[str, str]]] = {
|
|
||||||
"en_US": {
|
|
||||||
ATTR_DURATION: UnitOfTime.MILLISECONDS,
|
|
||||||
ATTR_DISTANCE: UnitOfLength.MILES,
|
|
||||||
ATTR_ELEVATION: UnitOfLength.FEET,
|
|
||||||
ATTR_HEIGHT: UnitOfLength.INCHES,
|
|
||||||
ATTR_WEIGHT: UnitOfMass.POUNDS,
|
|
||||||
ATTR_BODY: UnitOfLength.INCHES,
|
|
||||||
ATTR_LIQUIDS: UnitOfVolume.FLUID_OUNCES,
|
|
||||||
ATTR_BLOOD_GLUCOSE: f"{UnitOfMass.MILLIGRAMS}/dL",
|
|
||||||
ATTR_BATTERY: "",
|
|
||||||
},
|
|
||||||
"en_GB": {
|
|
||||||
ATTR_DURATION: UnitOfTime.MILLISECONDS,
|
|
||||||
ATTR_DISTANCE: UnitOfLength.KILOMETERS,
|
|
||||||
ATTR_ELEVATION: UnitOfLength.METERS,
|
|
||||||
ATTR_HEIGHT: UnitOfLength.CENTIMETERS,
|
|
||||||
ATTR_WEIGHT: UnitOfMass.STONES,
|
|
||||||
ATTR_BODY: UnitOfLength.CENTIMETERS,
|
|
||||||
ATTR_LIQUIDS: UnitOfVolume.MILLILITERS,
|
|
||||||
ATTR_BLOOD_GLUCOSE: "mmol/L",
|
|
||||||
ATTR_BATTERY: "",
|
|
||||||
},
|
|
||||||
"metric": {
|
|
||||||
ATTR_DURATION: UnitOfTime.MILLISECONDS,
|
|
||||||
ATTR_DISTANCE: UnitOfLength.KILOMETERS,
|
|
||||||
ATTR_ELEVATION: UnitOfLength.METERS,
|
|
||||||
ATTR_HEIGHT: UnitOfLength.CENTIMETERS,
|
|
||||||
ATTR_WEIGHT: UnitOfMass.KILOGRAMS,
|
|
||||||
ATTR_BODY: UnitOfLength.CENTIMETERS,
|
|
||||||
ATTR_LIQUIDS: UnitOfVolume.MILLILITERS,
|
|
||||||
ATTR_BLOOD_GLUCOSE: "mmol/L",
|
|
||||||
ATTR_BATTERY: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
BATTERY_LEVELS: Final[dict[str, int]] = {
|
BATTERY_LEVELS: Final[dict[str, int]] = {
|
||||||
"High": 100,
|
"High": 100,
|
||||||
"Medium": 50,
|
"Medium": 50,
|
||||||
"Low": 20,
|
"Low": 20,
|
||||||
"Empty": 0,
|
"Empty": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FitbitUnitSystem(StrEnum):
|
||||||
|
"""Fitbit unit system set when sending requests to the Fitbit API.
|
||||||
|
|
||||||
|
This is used as a header to tell the Fitbit API which type of units to return.
|
||||||
|
https://dev.fitbit.com/build/reference/web-api/developer-guide/application-design/#Units
|
||||||
|
|
||||||
|
Prefer to leave unset for newer configurations to use the Home Assistant default units.
|
||||||
|
"""
|
||||||
|
|
||||||
|
LEGACY_DEFAULT = "default"
|
||||||
|
"""When set, will use an appropriate default using a legacy algorithm."""
|
||||||
|
|
||||||
|
METRIC = "metric"
|
||||||
|
"""Use metric units."""
|
||||||
|
|
||||||
|
EN_US = "en_US"
|
||||||
|
"""Use United States units."""
|
||||||
|
|
||||||
|
EN_GB = "en_GB"
|
||||||
|
"""Use United Kingdom units."""
|
||||||
|
@ -29,6 +29,8 @@ from homeassistant.const import (
|
|||||||
CONF_CLIENT_SECRET,
|
CONF_CLIENT_SECRET,
|
||||||
CONF_UNIT_SYSTEM,
|
CONF_UNIT_SYSTEM,
|
||||||
PERCENTAGE,
|
PERCENTAGE,
|
||||||
|
UnitOfLength,
|
||||||
|
UnitOfMass,
|
||||||
UnitOfTime,
|
UnitOfTime,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -39,7 +41,6 @@ from homeassistant.helpers.json import save_json
|
|||||||
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
from homeassistant.helpers.network import NoURLAvailableError, get_url
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
from homeassistant.util.json import load_json_object
|
from homeassistant.util.json import load_json_object
|
||||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
|
||||||
|
|
||||||
from .api import FitbitApi
|
from .api import FitbitApi
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -56,9 +57,9 @@ from .const import (
|
|||||||
FITBIT_AUTH_START,
|
FITBIT_AUTH_START,
|
||||||
FITBIT_CONFIG_FILE,
|
FITBIT_CONFIG_FILE,
|
||||||
FITBIT_DEFAULT_RESOURCES,
|
FITBIT_DEFAULT_RESOURCES,
|
||||||
FITBIT_MEASUREMENTS,
|
FitbitUnitSystem,
|
||||||
)
|
)
|
||||||
from .model import FitbitDevice, FitbitProfile
|
from .model import FitbitDevice
|
||||||
|
|
||||||
_LOGGER: Final = logging.getLogger(__name__)
|
_LOGGER: Final = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -97,12 +98,36 @@ def _clock_format_12h(result: dict[str, Any]) -> str:
|
|||||||
return f"{hours}:{minutes:02d} {setting}"
|
return f"{hours}:{minutes:02d} {setting}"
|
||||||
|
|
||||||
|
|
||||||
|
def _weight_unit(unit_system: FitbitUnitSystem) -> UnitOfMass:
|
||||||
|
"""Determine the weight unit."""
|
||||||
|
if unit_system == FitbitUnitSystem.EN_US:
|
||||||
|
return UnitOfMass.POUNDS
|
||||||
|
if unit_system == FitbitUnitSystem.EN_GB:
|
||||||
|
return UnitOfMass.STONES
|
||||||
|
return UnitOfMass.KILOGRAMS
|
||||||
|
|
||||||
|
|
||||||
|
def _distance_unit(unit_system: FitbitUnitSystem) -> UnitOfLength:
|
||||||
|
"""Determine the distance unit."""
|
||||||
|
if unit_system == FitbitUnitSystem.EN_US:
|
||||||
|
return UnitOfLength.MILES
|
||||||
|
return UnitOfLength.KILOMETERS
|
||||||
|
|
||||||
|
|
||||||
|
def _elevation_unit(unit_system: FitbitUnitSystem) -> UnitOfLength:
|
||||||
|
"""Determine the elevation unit."""
|
||||||
|
if unit_system == FitbitUnitSystem.EN_US:
|
||||||
|
return UnitOfLength.FEET
|
||||||
|
return UnitOfLength.METERS
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FitbitSensorEntityDescription(SensorEntityDescription):
|
class FitbitSensorEntityDescription(SensorEntityDescription):
|
||||||
"""Describes Fitbit sensor entity."""
|
"""Describes Fitbit sensor entity."""
|
||||||
|
|
||||||
unit_type: str | None = None
|
unit_type: str | None = None
|
||||||
value_fn: Callable[[dict[str, Any]], Any] = _default_value_fn
|
value_fn: Callable[[dict[str, Any]], Any] = _default_value_fn
|
||||||
|
unit_fn: Callable[[FitbitUnitSystem], str | None] = lambda x: None
|
||||||
|
|
||||||
|
|
||||||
FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
||||||
@ -127,17 +152,17 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/distance",
|
key="activities/distance",
|
||||||
name="Distance",
|
name="Distance",
|
||||||
unit_type="distance",
|
|
||||||
icon="mdi:map-marker",
|
icon="mdi:map-marker",
|
||||||
device_class=SensorDeviceClass.DISTANCE,
|
device_class=SensorDeviceClass.DISTANCE,
|
||||||
value_fn=_distance_value_fn,
|
value_fn=_distance_value_fn,
|
||||||
|
unit_fn=_distance_unit,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/elevation",
|
key="activities/elevation",
|
||||||
name="Elevation",
|
name="Elevation",
|
||||||
unit_type="elevation",
|
|
||||||
icon="mdi:walk",
|
icon="mdi:walk",
|
||||||
device_class=SensorDeviceClass.DISTANCE,
|
device_class=SensorDeviceClass.DISTANCE,
|
||||||
|
unit_fn=_elevation_unit,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/floors",
|
key="activities/floors",
|
||||||
@ -201,17 +226,17 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/tracker/distance",
|
key="activities/tracker/distance",
|
||||||
name="Tracker Distance",
|
name="Tracker Distance",
|
||||||
unit_type="distance",
|
|
||||||
icon="mdi:map-marker",
|
icon="mdi:map-marker",
|
||||||
device_class=SensorDeviceClass.DISTANCE,
|
device_class=SensorDeviceClass.DISTANCE,
|
||||||
value_fn=_distance_value_fn,
|
value_fn=_distance_value_fn,
|
||||||
|
unit_fn=_distance_unit,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/tracker/elevation",
|
key="activities/tracker/elevation",
|
||||||
name="Tracker Elevation",
|
name="Tracker Elevation",
|
||||||
unit_type="elevation",
|
|
||||||
icon="mdi:walk",
|
icon="mdi:walk",
|
||||||
device_class=SensorDeviceClass.DISTANCE,
|
device_class=SensorDeviceClass.DISTANCE,
|
||||||
|
unit_fn=_elevation_unit,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/tracker/floors",
|
key="activities/tracker/floors",
|
||||||
@ -272,11 +297,11 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="body/weight",
|
key="body/weight",
|
||||||
name="Weight",
|
name="Weight",
|
||||||
unit_type="weight",
|
|
||||||
icon="mdi:human",
|
icon="mdi:human",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
device_class=SensorDeviceClass.WEIGHT,
|
device_class=SensorDeviceClass.WEIGHT,
|
||||||
value_fn=_body_value_fn,
|
value_fn=_body_value_fn,
|
||||||
|
unit_fn=_weight_unit,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="sleep/awakeningsCount",
|
key="sleep/awakeningsCount",
|
||||||
@ -360,8 +385,13 @@ PLATFORM_SCHEMA: Final = PARENT_PLATFORM_SCHEMA.extend(
|
|||||||
vol.Optional(CONF_CLOCK_FORMAT, default=DEFAULT_CLOCK_FORMAT): vol.In(
|
vol.Optional(CONF_CLOCK_FORMAT, default=DEFAULT_CLOCK_FORMAT): vol.In(
|
||||||
["12H", "24H"]
|
["12H", "24H"]
|
||||||
),
|
),
|
||||||
vol.Optional(CONF_UNIT_SYSTEM, default="default"): vol.In(
|
vol.Optional(CONF_UNIT_SYSTEM, default=FitbitUnitSystem.LEGACY_DEFAULT): vol.In(
|
||||||
["en_GB", "en_US", "metric", "default"]
|
[
|
||||||
|
FitbitUnitSystem.EN_GB,
|
||||||
|
FitbitUnitSystem.EN_US,
|
||||||
|
FitbitUnitSystem.METRIC,
|
||||||
|
FitbitUnitSystem.LEGACY_DEFAULT,
|
||||||
|
]
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -487,17 +517,9 @@ def setup_platform(
|
|||||||
if int(time.time()) - cast(int, expires_at) > 3600:
|
if int(time.time()) - cast(int, expires_at) > 3600:
|
||||||
authd_client.client.refresh_token()
|
authd_client.client.refresh_token()
|
||||||
|
|
||||||
api = FitbitApi(hass, authd_client)
|
api = FitbitApi(hass, authd_client, config[CONF_UNIT_SYSTEM])
|
||||||
user_profile = api.get_user_profile()
|
user_profile = api.get_user_profile()
|
||||||
if (unit_system := config[CONF_UNIT_SYSTEM]) == "default":
|
unit_system = api.get_unit_system()
|
||||||
authd_client.system = user_profile.locale
|
|
||||||
if authd_client.system != "en_GB":
|
|
||||||
if hass.config.units is METRIC_SYSTEM:
|
|
||||||
authd_client.system = "metric"
|
|
||||||
else:
|
|
||||||
authd_client.system = "en_US"
|
|
||||||
else:
|
|
||||||
authd_client.system = unit_system
|
|
||||||
|
|
||||||
clock_format = config[CONF_CLOCK_FORMAT]
|
clock_format = config[CONF_CLOCK_FORMAT]
|
||||||
monitored_resources = config[CONF_MONITORED_RESOURCES]
|
monitored_resources = config[CONF_MONITORED_RESOURCES]
|
||||||
@ -508,11 +530,10 @@ def setup_platform(
|
|||||||
entities = [
|
entities = [
|
||||||
FitbitSensor(
|
FitbitSensor(
|
||||||
api,
|
api,
|
||||||
user_profile,
|
user_profile.encoded_id,
|
||||||
config_path,
|
config_path,
|
||||||
description,
|
description,
|
||||||
hass.config.units is METRIC_SYSTEM,
|
units=description.unit_fn(unit_system),
|
||||||
clock_format,
|
|
||||||
)
|
)
|
||||||
for description in resource_list
|
for description in resource_list
|
||||||
if description.key in monitored_resources
|
if description.key in monitored_resources
|
||||||
@ -523,11 +544,9 @@ def setup_platform(
|
|||||||
[
|
[
|
||||||
FitbitSensor(
|
FitbitSensor(
|
||||||
api,
|
api,
|
||||||
user_profile,
|
user_profile.encoded_id,
|
||||||
config_path,
|
config_path,
|
||||||
FITBIT_RESOURCE_BATTERY,
|
FITBIT_RESOURCE_BATTERY,
|
||||||
hass.config.units is METRIC_SYSTEM,
|
|
||||||
clock_format,
|
|
||||||
device,
|
device,
|
||||||
)
|
)
|
||||||
for device in devices
|
for device in devices
|
||||||
@ -646,37 +665,25 @@ class FitbitSensor(SensorEntity):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
api: FitbitApi,
|
api: FitbitApi,
|
||||||
user_profile: FitbitProfile,
|
user_profile_id: str,
|
||||||
config_path: str,
|
config_path: str,
|
||||||
description: FitbitSensorEntityDescription,
|
description: FitbitSensorEntityDescription,
|
||||||
is_metric: bool,
|
|
||||||
clock_format: str,
|
|
||||||
device: FitbitDevice | None = None,
|
device: FitbitDevice | None = None,
|
||||||
|
units: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Fitbit sensor."""
|
"""Initialize the Fitbit sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self.api = api
|
self.api = api
|
||||||
self.config_path = config_path
|
self.config_path = config_path
|
||||||
self.is_metric = is_metric
|
|
||||||
self.clock_format = clock_format
|
|
||||||
self.device = device
|
self.device = device
|
||||||
|
|
||||||
self._attr_unique_id = f"{user_profile.encoded_id}_{description.key}"
|
self._attr_unique_id = f"{user_profile_id}_{description.key}"
|
||||||
if device is not None:
|
if device is not None:
|
||||||
self._attr_name = f"{device.device_version} Battery"
|
self._attr_name = f"{device.device_version} Battery"
|
||||||
self._attr_unique_id = f"{self._attr_unique_id}_{device.id}"
|
self._attr_unique_id = f"{self._attr_unique_id}_{device.id}"
|
||||||
|
|
||||||
if description.unit_type:
|
if units is not None:
|
||||||
try:
|
self._attr_native_unit_of_measurement = units
|
||||||
measurement_system = FITBIT_MEASUREMENTS[self.api.client.system]
|
|
||||||
except KeyError:
|
|
||||||
if self.is_metric:
|
|
||||||
measurement_system = FITBIT_MEASUREMENTS["metric"]
|
|
||||||
else:
|
|
||||||
measurement_system = FITBIT_MEASUREMENTS["en_US"]
|
|
||||||
split_resource = description.key.rsplit("/", maxsplit=1)[-1]
|
|
||||||
unit_type = measurement_system[split_resource]
|
|
||||||
self._attr_native_unit_of_measurement = unit_type
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icon(self) -> str | None:
|
def icon(self) -> str | None:
|
||||||
|
@ -66,14 +66,23 @@ def mock_monitored_resources() -> list[str] | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="configured_unit_system")
|
||||||
|
def mock_configured_unit_syststem() -> str | None:
|
||||||
|
"""Fixture for the fitbit yaml config monitored_resources field."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="sensor_platform_config")
|
@pytest.fixture(name="sensor_platform_config")
|
||||||
def mock_sensor_platform_config(
|
def mock_sensor_platform_config(
|
||||||
monitored_resources: list[str] | None,
|
monitored_resources: list[str] | None,
|
||||||
|
configured_unit_system: str | None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Fixture for the fitbit sensor platform configuration data in configuration.yaml."""
|
"""Fixture for the fitbit sensor platform configuration data in configuration.yaml."""
|
||||||
config = {}
|
config = {}
|
||||||
if monitored_resources is not None:
|
if monitored_resources is not None:
|
||||||
config["monitored_resources"] = monitored_resources
|
config["monitored_resources"] = monitored_resources
|
||||||
|
if configured_unit_system is not None:
|
||||||
|
config["unit_system"] = configured_unit_system
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@ -244,11 +244,27 @@ async def test_device_battery_level(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("monitored_resources", "profile_locale", "expected_unit"),
|
(
|
||||||
|
"monitored_resources",
|
||||||
|
"profile_locale",
|
||||||
|
"configured_unit_system",
|
||||||
|
"expected_unit",
|
||||||
|
),
|
||||||
[
|
[
|
||||||
(["body/weight"], "en_US", "kg"),
|
# Defaults to home assistant unit system unless UK
|
||||||
(["body/weight"], "en_GB", "st"),
|
(["body/weight"], "en_US", "default", "kg"),
|
||||||
(["body/weight"], "es_ES", "kg"),
|
(["body/weight"], "en_GB", "default", "st"),
|
||||||
|
(["body/weight"], "es_ES", "default", "kg"),
|
||||||
|
# Use the configured unit system from yaml
|
||||||
|
(["body/weight"], "en_US", "en_US", "lb"),
|
||||||
|
(["body/weight"], "en_GB", "en_US", "lb"),
|
||||||
|
(["body/weight"], "es_ES", "en_US", "lb"),
|
||||||
|
(["body/weight"], "en_US", "en_GB", "st"),
|
||||||
|
(["body/weight"], "en_GB", "en_GB", "st"),
|
||||||
|
(["body/weight"], "es_ES", "en_GB", "st"),
|
||||||
|
(["body/weight"], "en_US", "metric", "kg"),
|
||||||
|
(["body/weight"], "en_GB", "metric", "kg"),
|
||||||
|
(["body/weight"], "es_ES", "metric", "kg"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
async def test_profile_local(
|
async def test_profile_local(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user