📝 Add docstrings to mfa

Docstrings generation was requested by @cyberjunky.

* https://github.com/cyberjunky/home-assistant-garmin_connect/pull/306#issuecomment-2943501760

The following files were modified:

* `custom_components/garmin_connect/__init__.py`
* `custom_components/garmin_connect/config_flow.py`
* `custom_components/garmin_connect/sensor.py`
This commit is contained in:
coderabbitai[bot]
2025-06-05 10:54:20 +00:00
committed by GitHub
parent d0e7a15d35
commit 9a83f6aca8
3 changed files with 136 additions and 22 deletions

View File

@@ -61,7 +61,11 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
"""Garmin Connect Data Update Coordinator."""
def __init__(self, hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Initialize the Garmin Connect hub."""
"""
Initializes the Garmin Connect data update coordinator.
Sets up the integration's API client, determines if the user is in China, configures the time zone, and establishes the update interval for data synchronization.
"""
self.entry = entry
self.hass = hass
self._in_china = False
@@ -81,7 +85,18 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
update_interval=DEFAULT_UPDATE_INTERVAL)
async def async_login(self) -> bool:
"""Login to Garmin Connect."""
"""
Asynchronously logs into Garmin Connect using a stored authentication token.
Attempts to authenticate with Garmin Connect via the token stored in the configuration entry. Handles authentication failures, rate limiting, connection errors, and missing tokens by raising appropriate Home Assistant exceptions or returning False on recoverable errors.
Returns:
True if login is successful, False if rate limited or an unknown error occurs.
Raises:
ConfigEntryAuthFailed: If authentication fails or the token is missing.
ConfigEntryNotReady: If a connection error occurs.
"""
try:
# Check if the token exists in the entry data
if CONF_TOKEN not in self.entry.data:
@@ -124,7 +139,14 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
return True
async def _async_update_data(self) -> dict:
"""Fetch data from Garmin Connect."""
"""
Asynchronously fetches and aggregates user data from Garmin Connect.
Retrieves user summary, body composition, recent activities, badges, alarms, activity types, sleep data, HRV data, fitness age, hydration, and gear information. Calculates user points, user level, and determines the next active alarms. Handles authentication, connection, and rate limiting errors by raising Home Assistant exceptions or returning False as appropriate.
Returns:
A dictionary containing consolidated Garmin Connect data, including user summary, body composition, activities, badges, alarms, activity types, sleep metrics, HRV status, fitness age, hydration, gear details, and calculated fields such as user points, user level, next alarms, sleep score, and sleep time.
"""
summary = {}
body = {}
alarms = {}
@@ -390,11 +412,16 @@ class GarminConnectDataUpdateCoordinator(DataUpdateCoordinator):
def calculate_next_active_alarms(alarms, time_zone):
"""
Calculate garmin next active alarms from settings.
Alarms are sorted by time.
Example of alarms data:
Alarms data fetched: [{'alarmMode': 'OFF', 'alarmTime': 1233, 'alarmDays': ['ONCE'], 'alarmSound': 'TONE_AND_VIBRATION', 'alarmId': 1737308355, 'changeState': 'UNCHANGED', 'backlight': 'ON', 'enabled': None, 'alarmMessage': None, 'alarmImageId': None, 'alarmIcon': None, 'alarmType': None}]
Calculates the next active Garmin alarms based on alarm settings and the current time.
Filters alarms set to "ON" and computes the next scheduled datetime for each alarm day, considering both one-time and recurring alarms. Returns a sorted list of ISO-formatted datetimes for upcoming alarms, or None if no active alarms are found.
Args:
alarms: List of alarm setting dictionaries from Garmin devices.
time_zone: Time zone string used to localize alarm times.
Returns:
A sorted list of ISO-formatted datetimes for the next active alarms, or None if none are scheduled.
"""
active_alarms = []

View File

@@ -26,7 +26,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
VERSION = 1
def __init__(self) -> None:
"""Initialize flow."""
"""
Initializes the Garmin Connect configuration flow handler.
Sets up schemas for user credentials and MFA input, and initializes internal state variables for API client, login results, MFA code, credentials, and region flag.
"""
self.data_schema = {
vol.Required(CONF_USERNAME): str,
vol.Required(CONF_PASSWORD): str,
@@ -44,7 +48,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
self._in_china = False
async def _async_garmin_connect_login(self, step_id: str) -> ConfigFlowResult:
"""Handle login with Garmin Connect."""
"""
Attempts to authenticate the user with Garmin Connect and handles login errors or MFA requirements.
If the user is located in China, configures the API client accordingly. Initiates the login process and, if multi-factor authentication is required, transitions to the MFA step. Handles specific authentication and connection errors, returning appropriate error messages to the user. On successful authentication, proceeds to create the configuration entry.
"""
errors = {}
# Check if the user resides in China
@@ -86,7 +94,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._async_create_entry()
async def _async_garmin_connect_mfa_login(self) -> ConfigFlowResult:
"""Handle multi-factor authentication (MFA) login with Garmin Connect."""
"""
Completes the Garmin Connect login process using the provided MFA code.
Attempts to resume the login session with the stored MFA code. If the MFA code is invalid or an error occurs, prompts the user to re-enter the code. On success, creates the configuration entry.
"""
try:
await self.hass.async_add_executor_job(self._api.resume_login, self._login_result2, self._mfa_code)
@@ -101,7 +113,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
return await self._async_create_entry()
async def _async_create_entry(self) -> ConfigFlowResult:
"""Create the config entry."""
"""
Creates or updates the configuration entry for the Garmin Connect integration.
If an entry with the same username exists, updates its data and reloads it; otherwise, creates a new entry with the username as the unique ID and stores the serialized API token.
"""
config_data = {
CONF_ID: self._username,
CONF_USERNAME: self._username,
@@ -119,7 +135,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow initialized by the user."""
"""
Handles the initial step of the configuration flow triggered by the user.
If no input is provided, displays a form requesting username and password. Otherwise, stores the provided credentials and attempts to authenticate with Garmin Connect.
"""
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=vol.Schema(self.data_schema)
@@ -133,7 +153,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
async def async_step_mfa(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a multi-factor authentication (MFA) flow."""
"""
Handles the multi-factor authentication (MFA) step during the configuration flow.
If no user input is provided, displays a form requesting the MFA code. If input is received, stores the MFA code for further authentication processing.
"""
if user_input is None:
return self.async_show_form(
step_id="mfa", data_schema=vol.Schema(self.mfa_data_schema)
@@ -148,7 +172,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
async def async_step_reauth(
self, entry_data: Mapping[str, Any]
) -> ConfigFlowResult:
"""Handle reauthorization request from Garmin Connect."""
"""
Initiates the reauthorization flow using existing entry data.
Extracts the username from the provided entry data and proceeds to the reauthorization confirmation step.
"""
self._username = entry_data[CONF_USERNAME]
return await self.async_step_reauth_confirm()
@@ -156,7 +184,11 @@ class GarminConnectConfigFlowHandler(ConfigFlow, domain=DOMAIN):
async def async_step_reauth_confirm(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle reauthorization flow."""
"""
Handles the confirmation step for reauthorizing the Garmin Connect integration.
Prompts the user to re-enter their username and password. Upon receiving input, attempts to log in with the provided credentials to complete the reauthorization process.
"""
if user_input is None:
return self.async_show_form(
step_id="reauth_confirm",

View File

@@ -187,7 +187,11 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
@property
def native_value(self):
"""Return the state of the sensor."""
"""
Returns the current state value for the sensor, formatted and converted as appropriate for its type.
Handles special cases for activity, badge, HRV status, duration, mass, alarm, and stress qualifier sensor types, including value conversions and formatting. Returns None if data is unavailable.
"""
if not self.coordinator.data:
return None
@@ -231,7 +235,11 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
@property
def extra_state_attributes(self):
"""Return attributes for sensor."""
"""
Returns additional state attributes for the sensor entity.
Includes attributes such as the last sync timestamp and, depending on the sensor type, recent activities, badges, alarms, or HRV status details. Limits the number of returned activities and badges for performance.
"""
if not self.coordinator.data:
return {}
@@ -284,7 +292,29 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
return super().available and self.coordinator.data and self._type in self.coordinator.data
async def add_body_composition(self, **kwargs):
"""Handle the service call to add body composition."""
"""
Adds a new body composition record to Garmin Connect.
Extracts body composition metrics from the provided keyword arguments and submits them to the Garmin Connect API. Ensures the user is logged in before attempting to add the record.
Args:
weight: The user's weight to record.
timestamp: Optional timestamp for the measurement.
percent_fat: Optional body fat percentage.
percent_hydration: Optional body hydration percentage.
visceral_fat_mass: Optional visceral fat mass.
bone_mass: Optional bone mass.
muscle_mass: Optional muscle mass.
basal_met: Optional basal metabolic rate.
active_met: Optional active metabolic rate.
physique_rating: Optional physique rating.
metabolic_age: Optional metabolic age.
visceral_fat_rating: Optional visceral fat rating.
bmi: Optional body mass index.
Raises:
IntegrationError: If login to Garmin Connect fails.
"""
weight = kwargs.get("weight")
timestamp = kwargs.get("timestamp")
percent_fat = kwargs.get("percent_fat")
@@ -323,7 +353,19 @@ class GarminConnectSensor(CoordinatorEntity, SensorEntity):
)
async def add_blood_pressure(self, **kwargs):
"""Handle the service call to add blood pressure."""
"""
Adds a blood pressure measurement to Garmin Connect via a service call.
Args:
systolic: Systolic blood pressure value.
diastolic: Diastolic blood pressure value.
pulse: Pulse rate.
timestamp: Optional timestamp for the measurement.
notes: Optional notes for the measurement.
Raises:
IntegrationError: If login to Garmin Connect fails.
"""
timestamp = kwargs.get("timestamp")
systolic = kwargs.get("systolic")
diastolic = kwargs.get("diastolic")
@@ -390,7 +432,11 @@ class GarminConnectGearSensor(CoordinatorEntity, SensorEntity):
@property
def extra_state_attributes(self):
"""Return attributes for sensor."""
"""
Returns additional state attributes for the gear sensor entity.
Includes metadata such as last sync time, total activities, creation and update dates, gear details, maximum distance, and the activity types for which this gear is set as default.
"""
gear = self._gear()
stats = self._stats()
gear_defaults = self._gear_defaults()
@@ -467,7 +513,16 @@ class GarminConnectGearSensor(CoordinatorEntity, SensorEntity):
)
async def set_active_gear(self, **kwargs):
"""Handle the service call to set active gear."""
"""
Sets the active gear for a specified activity type in Garmin Connect.
Args:
activity_type: The activity type for which to set the active gear.
setting: The desired gear setting, determining whether to set as default or only default.
Raises:
IntegrationError: If login to Garmin Connect fails.
"""
activity_type = kwargs.get("activity_type")
setting = kwargs.get("setting")