mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 02:37:08 +00:00
Rewrite fitbit sensor API response value parsing (#100782)
* Cleanup fitbit sensor API parsing * Remove API code that is not used yet * Remove dead code for battery levels Small API parsing cleanup * Address PR feedback * Update homeassistant/components/fitbit/sensor.py Co-authored-by: Martin Hjelmare <marhje52@gmail.com> --------- Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
parent
5549f697cf
commit
66ebb479ea
65
homeassistant/components/fitbit/api.py
Normal file
65
homeassistant/components/fitbit/api.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"""API for fitbit bound to Home Assistant OAuth."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from fitbit import Fitbit
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .model import FitbitDevice, FitbitProfile
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class FitbitApi:
|
||||||
|
"""Fitbit client library wrapper base class."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
client: Fitbit,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize Fitbit auth."""
|
||||||
|
self._hass = hass
|
||||||
|
self._profile: FitbitProfile | None = None
|
||||||
|
self._client = client
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client(self) -> Fitbit:
|
||||||
|
"""Property to expose the underlying client library."""
|
||||||
|
return self._client
|
||||||
|
|
||||||
|
def get_user_profile(self) -> FitbitProfile:
|
||||||
|
"""Return the user profile from the API."""
|
||||||
|
response: dict[str, Any] = self._client.user_profile_get()
|
||||||
|
_LOGGER.debug("user_profile_get=%s", response)
|
||||||
|
profile = response["user"]
|
||||||
|
return FitbitProfile(
|
||||||
|
encoded_id=profile["encodedId"],
|
||||||
|
full_name=profile["fullName"],
|
||||||
|
locale=profile.get("locale"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_devices(self) -> list[FitbitDevice]:
|
||||||
|
"""Return available devices."""
|
||||||
|
devices: list[dict[str, str]] = self._client.get_devices()
|
||||||
|
_LOGGER.debug("get_devices=%s", devices)
|
||||||
|
return [
|
||||||
|
FitbitDevice(
|
||||||
|
id=device["id"],
|
||||||
|
device_version=device["deviceVersion"],
|
||||||
|
battery_level=int(device["batteryLevel"]),
|
||||||
|
battery=device["battery"],
|
||||||
|
type=device["type"],
|
||||||
|
)
|
||||||
|
for device in devices
|
||||||
|
]
|
||||||
|
|
||||||
|
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."""
|
||||||
|
response: dict[str, Any] = self._client.time_series(resource_type, period="7d")
|
||||||
|
_LOGGER.debug("time_series(%s)=%s", resource_type, response)
|
||||||
|
key = resource_type.replace("/", "-")
|
||||||
|
dated_results: list[dict[str, Any]] = response[key]
|
||||||
|
return dated_results[-1]
|
37
homeassistant/components/fitbit/model.py
Normal file
37
homeassistant/components/fitbit/model.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
"""Data representation for fitbit API responses."""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FitbitProfile:
|
||||||
|
"""User profile from the Fitbit API response."""
|
||||||
|
|
||||||
|
encoded_id: str
|
||||||
|
"""The ID representing the Fitbit user."""
|
||||||
|
|
||||||
|
full_name: str
|
||||||
|
"""The first name value specified in the user's account settings."""
|
||||||
|
|
||||||
|
locale: str | None
|
||||||
|
"""The locale defined in the user's Fitbit account settings."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FitbitDevice:
|
||||||
|
"""Device from the Fitbit API response."""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
"""The device ID."""
|
||||||
|
|
||||||
|
device_version: str
|
||||||
|
"""The product name of the device."""
|
||||||
|
|
||||||
|
battery_level: int
|
||||||
|
"""The battery level as a percentage."""
|
||||||
|
|
||||||
|
battery: str
|
||||||
|
"""Returns the battery level of the device."""
|
||||||
|
|
||||||
|
type: str
|
||||||
|
"""The type of the device such as TRACKER or SCALE."""
|
@ -1,6 +1,7 @@
|
|||||||
"""Support for the Fitbit API."""
|
"""Support for the Fitbit API."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
@ -40,6 +41,7 @@ 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 homeassistant.util.unit_system import METRIC_SYSTEM
|
||||||
|
|
||||||
|
from .api import FitbitApi
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_ACCESS_TOKEN,
|
ATTR_ACCESS_TOKEN,
|
||||||
ATTR_LAST_SAVED_AT,
|
ATTR_LAST_SAVED_AT,
|
||||||
@ -56,6 +58,7 @@ from .const import (
|
|||||||
FITBIT_DEFAULT_RESOURCES,
|
FITBIT_DEFAULT_RESOURCES,
|
||||||
FITBIT_MEASUREMENTS,
|
FITBIT_MEASUREMENTS,
|
||||||
)
|
)
|
||||||
|
from .model import FitbitDevice, FitbitProfile
|
||||||
|
|
||||||
_LOGGER: Final = logging.getLogger(__name__)
|
_LOGGER: Final = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -64,11 +67,42 @@ _CONFIGURING: dict[str, str] = {}
|
|||||||
SCAN_INTERVAL: Final = datetime.timedelta(minutes=30)
|
SCAN_INTERVAL: Final = datetime.timedelta(minutes=30)
|
||||||
|
|
||||||
|
|
||||||
|
def _default_value_fn(result: dict[str, Any]) -> str:
|
||||||
|
"""Parse a Fitbit timeseries API responses."""
|
||||||
|
return cast(str, result["value"])
|
||||||
|
|
||||||
|
|
||||||
|
def _distance_value_fn(result: dict[str, Any]) -> int | str:
|
||||||
|
"""Format function for distance values."""
|
||||||
|
return format(float(_default_value_fn(result)), ".2f")
|
||||||
|
|
||||||
|
|
||||||
|
def _body_value_fn(result: dict[str, Any]) -> int | str:
|
||||||
|
"""Format function for body values."""
|
||||||
|
return format(float(_default_value_fn(result)), ".1f")
|
||||||
|
|
||||||
|
|
||||||
|
def _clock_format_12h(result: dict[str, Any]) -> str:
|
||||||
|
raw_state = result["value"]
|
||||||
|
if raw_state == "":
|
||||||
|
return "-"
|
||||||
|
hours_str, minutes_str = raw_state.split(":")
|
||||||
|
hours, minutes = int(hours_str), int(minutes_str)
|
||||||
|
setting = "AM"
|
||||||
|
if hours > 12:
|
||||||
|
setting = "PM"
|
||||||
|
hours -= 12
|
||||||
|
elif hours == 0:
|
||||||
|
hours = 12
|
||||||
|
return f"{hours}:{minutes:02d} {setting}"
|
||||||
|
|
||||||
|
|
||||||
@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
|
||||||
|
|
||||||
|
|
||||||
FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
||||||
@ -96,6 +130,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
unit_type="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,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/elevation",
|
key="activities/elevation",
|
||||||
@ -115,6 +150,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
name="Resting Heart Rate",
|
name="Resting Heart Rate",
|
||||||
native_unit_of_measurement="bpm",
|
native_unit_of_measurement="bpm",
|
||||||
icon="mdi:heart-pulse",
|
icon="mdi:heart-pulse",
|
||||||
|
value_fn=lambda result: int(result["value"]["restingHeartRate"]),
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/minutesFairlyActive",
|
key="activities/minutesFairlyActive",
|
||||||
@ -168,6 +204,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
unit_type="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,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="activities/tracker/elevation",
|
key="activities/tracker/elevation",
|
||||||
@ -222,6 +259,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
native_unit_of_measurement="BMI",
|
native_unit_of_measurement="BMI",
|
||||||
icon="mdi:human",
|
icon="mdi:human",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=_body_value_fn,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="body/fat",
|
key="body/fat",
|
||||||
@ -229,6 +267,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
icon="mdi:human",
|
icon="mdi:human",
|
||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
|
value_fn=_body_value_fn,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="body/weight",
|
key="body/weight",
|
||||||
@ -237,6 +276,7 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
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,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="sleep/awakeningsCount",
|
key="sleep/awakeningsCount",
|
||||||
@ -279,11 +319,6 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
icon="mdi:sleep",
|
icon="mdi:sleep",
|
||||||
device_class=SensorDeviceClass.DURATION,
|
device_class=SensorDeviceClass.DURATION,
|
||||||
),
|
),
|
||||||
FitbitSensorEntityDescription(
|
|
||||||
key="sleep/startTime",
|
|
||||||
name="Sleep Start Time",
|
|
||||||
icon="mdi:clock",
|
|
||||||
),
|
|
||||||
FitbitSensorEntityDescription(
|
FitbitSensorEntityDescription(
|
||||||
key="sleep/timeInBed",
|
key="sleep/timeInBed",
|
||||||
name="Sleep Time in Bed",
|
name="Sleep Time in Bed",
|
||||||
@ -293,6 +328,19 @@ FITBIT_RESOURCES_LIST: Final[tuple[FitbitSensorEntityDescription, ...]] = (
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Different description depending on clock format
|
||||||
|
SLEEP_START_TIME = FitbitSensorEntityDescription(
|
||||||
|
key="sleep/startTime",
|
||||||
|
name="Sleep Start Time",
|
||||||
|
icon="mdi:clock",
|
||||||
|
)
|
||||||
|
SLEEP_START_TIME_12HR = FitbitSensorEntityDescription(
|
||||||
|
key="sleep/startTime",
|
||||||
|
name="Sleep Start Time",
|
||||||
|
icon="mdi:clock",
|
||||||
|
value_fn=_clock_format_12h,
|
||||||
|
)
|
||||||
|
|
||||||
FITBIT_RESOURCE_BATTERY = FitbitSensorEntityDescription(
|
FITBIT_RESOURCE_BATTERY = FitbitSensorEntityDescription(
|
||||||
key="devices/battery",
|
key="devices/battery",
|
||||||
name="Battery",
|
name="Battery",
|
||||||
@ -300,7 +348,8 @@ FITBIT_RESOURCE_BATTERY = FitbitSensorEntityDescription(
|
|||||||
)
|
)
|
||||||
|
|
||||||
FITBIT_RESOURCES_KEYS: Final[list[str]] = [
|
FITBIT_RESOURCES_KEYS: Final[list[str]] = [
|
||||||
desc.key for desc in (*FITBIT_RESOURCES_LIST, FITBIT_RESOURCE_BATTERY)
|
desc.key
|
||||||
|
for desc in (*FITBIT_RESOURCES_LIST, FITBIT_RESOURCE_BATTERY, SLEEP_START_TIME)
|
||||||
]
|
]
|
||||||
|
|
||||||
PLATFORM_SCHEMA: Final = PARENT_PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA: Final = PARENT_PLATFORM_SCHEMA.extend(
|
||||||
@ -438,9 +487,10 @@ 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()
|
||||||
|
|
||||||
user_profile = authd_client.user_profile_get()["user"]
|
api = FitbitApi(hass, authd_client)
|
||||||
|
user_profile = api.get_user_profile()
|
||||||
if (unit_system := config[CONF_UNIT_SYSTEM]) == "default":
|
if (unit_system := config[CONF_UNIT_SYSTEM]) == "default":
|
||||||
authd_client.system = user_profile["locale"]
|
authd_client.system = user_profile.locale
|
||||||
if authd_client.system != "en_GB":
|
if authd_client.system != "en_GB":
|
||||||
if hass.config.units is METRIC_SYSTEM:
|
if hass.config.units is METRIC_SYSTEM:
|
||||||
authd_client.system = "metric"
|
authd_client.system = "metric"
|
||||||
@ -449,34 +499,38 @@ def setup_platform(
|
|||||||
else:
|
else:
|
||||||
authd_client.system = unit_system
|
authd_client.system = unit_system
|
||||||
|
|
||||||
registered_devs = authd_client.get_devices()
|
|
||||||
clock_format = config[CONF_CLOCK_FORMAT]
|
clock_format = config[CONF_CLOCK_FORMAT]
|
||||||
monitored_resources = config[CONF_MONITORED_RESOURCES]
|
monitored_resources = config[CONF_MONITORED_RESOURCES]
|
||||||
|
resource_list = [
|
||||||
|
*FITBIT_RESOURCES_LIST,
|
||||||
|
SLEEP_START_TIME_12HR if clock_format == "12H" else SLEEP_START_TIME,
|
||||||
|
]
|
||||||
entities = [
|
entities = [
|
||||||
FitbitSensor(
|
FitbitSensor(
|
||||||
authd_client,
|
api,
|
||||||
user_profile,
|
user_profile,
|
||||||
config_path,
|
config_path,
|
||||||
description,
|
description,
|
||||||
hass.config.units is METRIC_SYSTEM,
|
hass.config.units is METRIC_SYSTEM,
|
||||||
clock_format,
|
clock_format,
|
||||||
)
|
)
|
||||||
for description in FITBIT_RESOURCES_LIST
|
for description in resource_list
|
||||||
if description.key in monitored_resources
|
if description.key in monitored_resources
|
||||||
]
|
]
|
||||||
if "devices/battery" in monitored_resources:
|
if "devices/battery" in monitored_resources:
|
||||||
|
devices = api.get_devices()
|
||||||
entities.extend(
|
entities.extend(
|
||||||
[
|
[
|
||||||
FitbitSensor(
|
FitbitSensor(
|
||||||
authd_client,
|
api,
|
||||||
user_profile,
|
user_profile,
|
||||||
config_path,
|
config_path,
|
||||||
FITBIT_RESOURCE_BATTERY,
|
FITBIT_RESOURCE_BATTERY,
|
||||||
hass.config.units is METRIC_SYSTEM,
|
hass.config.units is METRIC_SYSTEM,
|
||||||
clock_format,
|
clock_format,
|
||||||
dev_extra,
|
device,
|
||||||
)
|
)
|
||||||
for dev_extra in registered_devs
|
for device in devices
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
add_entities(entities, True)
|
add_entities(entities, True)
|
||||||
@ -591,30 +645,30 @@ class FitbitSensor(SensorEntity):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
client: Fitbit,
|
api: FitbitApi,
|
||||||
user_profile: dict[str, Any],
|
user_profile: FitbitProfile,
|
||||||
config_path: str,
|
config_path: str,
|
||||||
description: FitbitSensorEntityDescription,
|
description: FitbitSensorEntityDescription,
|
||||||
is_metric: bool,
|
is_metric: bool,
|
||||||
clock_format: str,
|
clock_format: str,
|
||||||
extra: dict[str, str] | None = None,
|
device: FitbitDevice | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Fitbit sensor."""
|
"""Initialize the Fitbit sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self.client = client
|
self.api = api
|
||||||
self.config_path = config_path
|
self.config_path = config_path
|
||||||
self.is_metric = is_metric
|
self.is_metric = is_metric
|
||||||
self.clock_format = clock_format
|
self.clock_format = clock_format
|
||||||
self.extra = extra
|
self.device = device
|
||||||
|
|
||||||
self._attr_unique_id = f"{user_profile['encodedId']}_{description.key}"
|
self._attr_unique_id = f"{user_profile.encoded_id}_{description.key}"
|
||||||
if self.extra is not None:
|
if device is not None:
|
||||||
self._attr_name = f"{self.extra.get('deviceVersion')} Battery"
|
self._attr_name = f"{device.device_version} Battery"
|
||||||
self._attr_unique_id = f"{self._attr_unique_id}_{self.extra.get('id')}"
|
self._attr_unique_id = f"{self._attr_unique_id}_{device.id}"
|
||||||
|
|
||||||
if description.unit_type:
|
if description.unit_type:
|
||||||
try:
|
try:
|
||||||
measurement_system = FITBIT_MEASUREMENTS[self.client.system]
|
measurement_system = FITBIT_MEASUREMENTS[self.api.client.system]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if self.is_metric:
|
if self.is_metric:
|
||||||
measurement_system = FITBIT_MEASUREMENTS["metric"]
|
measurement_system = FITBIT_MEASUREMENTS["metric"]
|
||||||
@ -629,9 +683,8 @@ class FitbitSensor(SensorEntity):
|
|||||||
"""Icon to use in the frontend, if any."""
|
"""Icon to use in the frontend, if any."""
|
||||||
if (
|
if (
|
||||||
self.entity_description.key == "devices/battery"
|
self.entity_description.key == "devices/battery"
|
||||||
and self.extra is not None
|
and self.device is not None
|
||||||
and (extra_battery := self.extra.get("battery")) is not None
|
and (battery_level := BATTERY_LEVELS.get(self.device.battery)) is not None
|
||||||
and (battery_level := BATTERY_LEVELS.get(extra_battery)) is not None
|
|
||||||
):
|
):
|
||||||
return icon_for_battery_level(battery_level=battery_level)
|
return icon_for_battery_level(battery_level=battery_level)
|
||||||
return self.entity_description.icon
|
return self.entity_description.icon
|
||||||
@ -641,72 +694,34 @@ class FitbitSensor(SensorEntity):
|
|||||||
"""Return the state attributes."""
|
"""Return the state attributes."""
|
||||||
attrs: dict[str, str | None] = {}
|
attrs: dict[str, str | None] = {}
|
||||||
|
|
||||||
if self.extra is not None:
|
if self.device is not None:
|
||||||
attrs["model"] = self.extra.get("deviceVersion")
|
attrs["model"] = self.device.device_version
|
||||||
extra_type = self.extra.get("type")
|
device_type = self.device.type
|
||||||
attrs["type"] = extra_type.lower() if extra_type is not None else None
|
attrs["type"] = device_type.lower() if device_type is not None else None
|
||||||
|
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Get the latest data from the Fitbit API and update the states."""
|
"""Get the latest data from the Fitbit API and update the states."""
|
||||||
resource_type = self.entity_description.key
|
resource_type = self.entity_description.key
|
||||||
if resource_type == "devices/battery" and self.extra is not None:
|
if resource_type == "devices/battery" and self.device is not None:
|
||||||
registered_devs: list[dict[str, Any]] = self.client.get_devices()
|
device_id = self.device.id
|
||||||
device_id = self.extra.get("id")
|
registered_devs: list[FitbitDevice] = self.api.get_devices()
|
||||||
self.extra = list(
|
self.device = next(
|
||||||
filter(lambda device: device.get("id") == device_id, registered_devs)
|
device for device in registered_devs if device.id == device_id
|
||||||
)[0]
|
)
|
||||||
self._attr_native_value = self.extra.get("battery")
|
self._attr_native_value = self.device.battery
|
||||||
|
|
||||||
else:
|
else:
|
||||||
container = resource_type.replace("/", "-")
|
result = self.api.get_latest_time_series(resource_type)
|
||||||
response = self.client.time_series(resource_type, period="7d")
|
self._attr_native_value = self.entity_description.value_fn(result)
|
||||||
raw_state = response[container][-1].get("value")
|
|
||||||
if resource_type == "activities/distance":
|
|
||||||
self._attr_native_value = format(float(raw_state), ".2f")
|
|
||||||
elif resource_type == "activities/tracker/distance":
|
|
||||||
self._attr_native_value = format(float(raw_state), ".2f")
|
|
||||||
elif resource_type == "body/bmi":
|
|
||||||
self._attr_native_value = format(float(raw_state), ".1f")
|
|
||||||
elif resource_type == "body/fat":
|
|
||||||
self._attr_native_value = format(float(raw_state), ".1f")
|
|
||||||
elif resource_type == "body/weight":
|
|
||||||
self._attr_native_value = format(float(raw_state), ".1f")
|
|
||||||
elif resource_type == "sleep/startTime":
|
|
||||||
if raw_state == "":
|
|
||||||
self._attr_native_value = "-"
|
|
||||||
elif self.clock_format == "12H":
|
|
||||||
hours, minutes = raw_state.split(":")
|
|
||||||
hours, minutes = int(hours), int(minutes)
|
|
||||||
setting = "AM"
|
|
||||||
if hours > 12:
|
|
||||||
setting = "PM"
|
|
||||||
hours -= 12
|
|
||||||
elif hours == 0:
|
|
||||||
hours = 12
|
|
||||||
self._attr_native_value = f"{hours}:{minutes:02d} {setting}"
|
|
||||||
else:
|
|
||||||
self._attr_native_value = raw_state
|
|
||||||
elif self.is_metric:
|
|
||||||
self._attr_native_value = raw_state
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
self._attr_native_value = int(raw_state)
|
|
||||||
except TypeError:
|
|
||||||
self._attr_native_value = raw_state
|
|
||||||
|
|
||||||
if resource_type == "activities/heart":
|
token = self.api.client.client.session.token
|
||||||
self._attr_native_value = (
|
|
||||||
response[container][-1].get("value").get("restingHeartRate")
|
|
||||||
)
|
|
||||||
|
|
||||||
token = self.client.client.session.token
|
|
||||||
config_contents = {
|
config_contents = {
|
||||||
ATTR_ACCESS_TOKEN: token.get("access_token"),
|
ATTR_ACCESS_TOKEN: token.get("access_token"),
|
||||||
ATTR_REFRESH_TOKEN: token.get("refresh_token"),
|
ATTR_REFRESH_TOKEN: token.get("refresh_token"),
|
||||||
CONF_CLIENT_ID: self.client.client.client_id,
|
CONF_CLIENT_ID: self.api.client.client.client_id,
|
||||||
CONF_CLIENT_SECRET: self.client.client.client_secret,
|
CONF_CLIENT_SECRET: self.api.client.client.client_secret,
|
||||||
ATTR_LAST_SAVED_AT: int(time.time()),
|
ATTR_LAST_SAVED_AT: int(time.time()),
|
||||||
}
|
}
|
||||||
save_json(self.config_path, config_contents)
|
save_json(self.config_path, config_contents)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user