1 """API for fitbit bound to Home Assistant OAuth."""
3 from abc
import ABC, abstractmethod
4 from collections.abc
import Callable
6 from typing
import Any, cast
8 from fitbit
import Fitbit
9 from fitbit.exceptions
import HTTPException, HTTPUnauthorized
10 from requests.exceptions
import ConnectionError
as RequestsConnectionError
17 from .const
import FitbitUnitSystem
18 from .exceptions
import FitbitApiException, FitbitAuthException
19 from .model
import FitbitDevice, FitbitProfile
21 _LOGGER = logging.getLogger(__name__)
23 CONF_REFRESH_TOKEN =
"refresh_token"
24 CONF_EXPIRES_AT =
"expires_at"
28 """Fitbit client library wrapper base class.
30 This can be subclassed with different implementations for providing an access
31 token depending on the use case.
37 unit_system: FitbitUnitSystem |
None =
None,
39 """Initialize Fitbit auth."""
41 self.
_profile_profile: FitbitProfile |
None =
None
46 """Return a valid token dictionary for the Fitbit API."""
49 """Get synchronous client library, called before each client request."""
52 token = await self.async_get_access_token()
56 access_token=token[CONF_ACCESS_TOKEN],
57 refresh_token=token[CONF_REFRESH_TOKEN],
58 expires_at=
float(token[CONF_EXPIRES_AT]),
62 """Return the user profile from the API."""
65 response: dict[str, Any] = await self._run(client.user_profile_get)
66 _LOGGER.debug(
"user_profile_get=%s", response)
67 profile = response[
"user"]
69 encoded_id=profile[
"encodedId"],
70 display_name=profile[
"displayName"],
71 locale=profile.get(
"locale"),
76 """Get the unit system to use when fetching timeseries.
78 This is used in a couple ways. The first is to determine the request
79 header to use when talking to the fitbit API which changes the
80 units returned by the API. The second is to tell Home Assistant the
81 units set in sensor values for the values returned by the API.
85 and self.
_unit_system_unit_system != FitbitUnitSystem.LEGACY_DEFAULT
91 if profile.locale == FitbitUnitSystem.EN_GB:
92 return FitbitUnitSystem.EN_GB
93 if self.
_hass_hass.config.units
is METRIC_SYSTEM:
94 return FitbitUnitSystem.METRIC
95 return FitbitUnitSystem.EN_US
98 """Return available devices."""
100 devices: list[dict[str, str]] = await self._run(client.get_devices)
101 _LOGGER.debug(
"get_devices=%s", devices)
105 device_version=device[
"deviceVersion"],
106 battery_level=
int(device[
"batteryLevel"]),
107 battery=device[
"battery"],
110 for device
in devices
114 """Return the most recent value from the time series for the specified resource type."""
120 def _time_series() -> dict[str, Any]:
121 return cast(dict[str, Any], client.time_series(resource_type, period=
"7d"))
123 response: dict[str, Any] = await self._run(_time_series)
124 _LOGGER.debug(
"time_series(%s)=%s", resource_type, response)
125 key = resource_type.replace(
"/",
"-")
126 dated_results: list[dict[str, Any]] = response[key]
127 return dated_results[-1]
129 async
def _run[_T](self, func: Callable[[], _T]) -> _T:
130 """Run client command."""
132 return await self.
_hass_hass.async_add_executor_job(func)
133 except RequestsConnectionError
as err:
134 _LOGGER.debug(
"Connection error to fitbit API: %s", err)
136 except HTTPUnauthorized
as err:
137 _LOGGER.debug(
"Unauthorized error from fitbit API: %s", err)
139 except HTTPException
as err:
140 _LOGGER.debug(
"Error from fitbit API: %s", err)
145 """Provide fitbit authentication tied to an OAuth2 based config entry."""
150 oauth_session: config_entry_oauth2_flow.OAuth2Session,
151 unit_system: FitbitUnitSystem |
None =
None,
153 """Initialize OAuthFitbitApi."""
158 """Return a valid access token for the Fitbit API."""
159 await self.
_oauth_session_oauth_session.async_ensure_token_valid()
164 """Profile fitbit authentication before a ConfigEntry exists.
166 This implementation directly provides the token without supporting refresh.
172 token: dict[str, Any],
174 """Initialize ConfigFlowFitbitApi."""
179 """Return the token for the Fitbit API."""
dict[str, Any] async_get_access_token(self)
None __init__(self, HomeAssistant hass, dict[str, Any] token)
FitbitProfile async_get_user_profile(self)
FitbitUnitSystem async_get_unit_system(self)
None __init__(self, HomeAssistant hass, FitbitUnitSystem|None unit_system=None)
Fitbit _async_get_client(self)
dict[str, Any] async_get_latest_time_series(self, str resource_type)
dict[str, Any] async_get_access_token(self)
list[FitbitDevice] async_get_devices(self)
dict[str, Any] async_get_access_token(self)
None __init__(self, HomeAssistant hass, config_entry_oauth2_flow.OAuth2Session oauth_session, FitbitUnitSystem|None unit_system=None)