From dcb9a33665ef77e750c5f33295e365329a906d81 Mon Sep 17 00:00:00 2001 From: Jon Stroop Date: Mon, 3 Mar 2025 22:47:36 -0500 Subject: [PATCH] updated docs and enchanced docstring test --- README.md | 2 + TODO.md | 39 +- .../resources/active_zone_minutes.py | 84 +- fitbit_client/resources/activity.py | 440 +++++++--- .../resources/activity_timeseries.py | 88 +- fitbit_client/resources/base.py | 108 ++- fitbit_client/resources/body.py | 298 ++++--- fitbit_client/resources/body_timeseries.py | 220 +++-- fitbit_client/resources/breathing_rate.py | 86 +- .../resources/cardio_fitness_score.py | 74 +- fitbit_client/resources/constants.py | 25 +- fitbit_client/resources/device.py | 159 +++- fitbit_client/resources/electrocardiogram.py | 77 +- fitbit_client/resources/friends.py | 90 ++- .../resources/heartrate_timeseries.py | 107 ++- .../resources/heartrate_variability.py | 89 +- fitbit_client/resources/intraday.py | 610 ++++++++++---- .../irregular_rhythm_notifications.py | 106 ++- fitbit_client/resources/nutrition.py | 757 ++++++++++++++---- .../resources/nutrition_timeseries.py | 95 ++- fitbit_client/resources/sleep.py | 232 ++++-- fitbit_client/resources/spo2.py | 93 ++- fitbit_client/resources/subscription.py | 127 ++- fitbit_client/resources/temperature.py | 128 +-- fitbit_client/resources/user.py | 110 ++- tests/resources/test_docstrings.py | 128 ++- 26 files changed, 3104 insertions(+), 1268 deletions(-) diff --git a/README.md b/README.md index 2b53b66..e7e4ad2 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,8 @@ Additional documentation: - To understand the logging implemementation, see [LOGGING](docs/LOGGING.md) - To understand validations and the exception hierarchy, see [VALIDATIONS_AND_EXCEPTIONS](docs/VALIDATIONS_AND_EXCEPTIONS.md) +- It's work checking out + [Fitbit's Best Practices](https://dev.fitbit.com/build/reference/web-api/developer-guide/best-practices/) - For some general development guidelines, see [DEVELOPMENT](docs/DEVELOPMENT.md). - For style guidelines (mostly enforced through varius linters and formatters) diff --git a/TODO.md b/TODO.md index ec98ffa..18b8ea2 100644 --- a/TODO.md +++ b/TODO.md @@ -23,8 +23,6 @@ - Creat and Test that all methods have an alias in `Client` and that the signatures match - - Make sure that `ClientValidationException` is getting used for arbitrary - validations like: ```python if not food_id and not (food_name and calories): @@ -45,41 +43,20 @@ if not food_id and not (food_name and calories): params[str(key)] = float(value) ``` -It needs to change to: - -```python - for key, value in nutritional_values.items(): - if isinstance(key, NutritionalValue): - if key == NutritionalValue.CALORIES_FROM_FAT: - params[key.value] = int(value) - else: - params[key.value] = float(value) - else: - params[str(key)] = float(value) -``` - see: test_create_food_calories_from_fat_must_be_integer(nutrition_resource) - exceptions.py - - Should ClientValidationException really subclass FitbitAPIException? It - doesn't need the API lookup mapping (`exception_type`) or a `status_code`, - so we may just be able to simplify it. The most important thing is that the - user understands that the message came from the client prior to the API - call. - -- Resource docstrings/documentation: + - Should ClientValidationException really subclass FitbitAPIException? IT + SHOULD SUBCLASS ValueError doesn't need the API lookup mapping + (`exception_type`) or a `status_code`, so we may just be able to simplify + it. The most important thing is that the user understands that the message + came from the client prior to the API call. - - Update the list of exceptions that are raised in the doctring for - `base._make_request` - - Review all documentation and between **API docs, return types, and - exceptions**, update the doctrings - - Update "Returns" in all resource docstrings - - Review and add link to - https://dev.fitbit.com/build/reference/web-api/developer-guide/best-practices/ - in README--they still apply! + - Make sure we aren't using -- Set up CI! + - Make sure that `ClientValidationException` is getting used for arbitrary + validations like ## Longer term TODOs diff --git a/fitbit_client/resources/active_zone_minutes.py b/fitbit_client/resources/active_zone_minutes.py index d80b6a1..d8a5a73 100644 --- a/fitbit_client/resources/active_zone_minutes.py +++ b/fitbit_client/resources/active_zone_minutes.py @@ -14,44 +14,62 @@ class ActiveZoneMinutesResource(BaseResource): - """ - Handles Fitbit Active Zone Minutes (AZM) API endpoints for retrieving user's - heart-pumping activity data throughout the day. + """Provides access to Fitbit Active Zone Minutes (AZM) API for heart rate-based activity metrics. + + This resource handles endpoints for retrieving Active Zone Minutes (AZM) data, which measures + the time users spend in target heart rate zones during exercise or daily activities. AZM + is a scientifically-validated way to track activity intensity based on personalized heart + rate zones rather than just steps. - Active Zone Minutes (AZM) measure the time spent in target heart rate zones. Different zones contribute differently to the total AZM count: - - Fat Burn zone: 1 minute = 1 AZM - - Cardio zone: 1 minute = 2 AZM - - Peak zone: 1 minute = 2 AZM + - Fat Burn zone: 1 minute = 1 AZM (moderate intensity) + - Cardio zone: 1 minute = 2 AZM (high intensity) + - Peak zone: 1 minute = 2 AZM (maximum effort) API Reference: https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/ + + Required Scopes: + - activity (for all AZM endpoints) + + Note: + - Heart rate zones are personalized based on the user's resting heart rate and age + - The American Heart Association recommends 150 minutes of moderate (Fat Burn) or + 75 minutes of vigorous (Cardio/Peak) activity per week + - AZM data is available from the date the user first set up their Fitbit device + - Historical data older than 3 years may not be available through the API + - Not all Fitbit devices support AZM tracking (requires heart rate monitoring) + - The date range endpoints are useful for analyzing weekly and monthly AZM totals """ @validate_date_param() def get_azm_timeseries_by_date( self, date: str, period: Period = Period.ONE_DAY, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get Active Zone Minutes time series data for a period starting from the specified date. + """Returns Active Zone Minutes time series data for a period ending on the specified date. API Reference: https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/get-azm-timeseries-by-date/ Args: - date: The end date of the period in yyyy-MM-dd format or 'today' + date: The end date of the period in YYYY-MM-DD format or 'today' period: The range for which data will be returned. Only Period.ONE_DAY (1d) is supported. - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Daily AZM data including: - - activeZoneMinutes: Total count of active zone minutes - - fatBurnActiveZoneMinutes: Minutes in fat burn zone (1 minute = 1 AZM) - - cardioActiveZoneMinutes: Minutes in cardio zone (1 minute = 2 AZM) - - peakActiveZoneMinutes: Minutes in peak zone (1 minute = 2 AZM) + JSONDict: Daily Active Zone Minutes data Raises: ValueError: If period is not Period.ONE_DAY - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + + Note: + - Only Period.ONE_DAY (1d) is currently supported by the Fitbit API + - activeZoneMinutes is the sum total of all zone minutes with cardio and peak + minutes counting double (fatBurn + (cardio × 2) + (peak × 2)) + - Fat burn zone is typically 50-69% of max heart rate (moderate intensity) + - Cardio zone is typically 70-84% of max heart rate (high intensity) + - Peak zone is typically 85%+ of max heart rate (maximum effort) + - Days with no AZM data will show all metrics as zero """ if period != Period.ONE_DAY: raise ValueError("Only 1d period is supported for AZM time series") @@ -67,30 +85,32 @@ def get_azm_timeseries_by_date( def get_azm_timeseries_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get Active Zone Minutes time series data for a specified date range. + """Returns Active Zone Minutes time series data for a specified date range. API Reference: https://dev.fitbit.com/build/reference/web-api/active-zone-minutes-timeseries/get-azm-timeseries-by-interval/ Args: - start_date: The start date in yyyy-MM-dd format or 'today' - end_date: The end date in yyyy-MM-dd format or 'today' - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + start_date: The start date in YYYY-MM-DD format or 'today' + end_date: The end date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Daily AZM data for each date in the range including: - - activeZoneMinutes: Total count of active zone minutes - - fatBurnActiveZoneMinutes: Minutes in fat burn zone (1 minute = 1 AZM) - - cardioActiveZoneMinutes: Minutes in cardio zone (1 minute = 2 AZM) - - peakActiveZoneMinutes: Minutes in peak zone (1 minute = 2 AZM) + JSONDict: Daily Active Zone Minutes data for each date in the range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range is invalid or exceeds 1095 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 1095 days Note: - Maximum date range is 1095 days (approximately 3 years) + - Maximum date range is 1095 days (approximately 3 years) + - Each day's entry includes separate counts for each heart rate zone + - activeZoneMinutes is the total AZM with cardio and peak minutes counting double + - This endpoint is useful for calculating weekly or monthly AZM totals + - Days with no AZM data will have all metrics as zero + - Active Zone Minutes does not support subscription notifications (webhooks), + but can be queried after activity notifications arrive + - Weekly AZM goals can be tracked by summing 7 consecutive days of data """ result = self._make_request( f"activities/active-zone-minutes/date/{start_date}/{end_date}.json", diff --git a/fitbit_client/resources/activity.py b/fitbit_client/resources/activity.py index 16fd58f..faf5405 100644 --- a/fitbit_client/resources/activity.py +++ b/fitbit_client/resources/activity.py @@ -20,13 +20,27 @@ class ActivityResource(BaseResource): - """ - Handles Fitbit Activity API endpoints for recording, retrieving and managing - user activities and goals. + """Provides access to Fitbit Activity API for managing user activities and goals. - Scope: activity; `get_activity_tcx' also requires and location. + This resource handles endpoints for recording, retrieving, and managing various + aspects of user fitness activities including activity logs, goals, favorites, + and lifetime statistics. It supports creating and deleting activity records, + managing activity goals, and retrieving detailed activity information. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/ + + Required Scopes: + - activity (for most activity endpoints) + - location (additionally required for get_activity_tcx) + + Note: + - Activity records include steps, distance, calories, active minutes, and other metrics + - Activity logs can be created manually or automatically by Fitbit devices + - Goals can be set on a daily or weekly basis for various activity metrics + - Lifetime statistics track cumulative totals since the user's account creation + - Activity types are categorized by intensity level and metabolic equivalent (MET) + - Favorite activities can be saved for quick access when logging manual activities + - TCX files (Training Center XML) provide detailed GPS data for activities with location tracking """ def create_activity_goals( @@ -37,23 +51,33 @@ def create_activity_goals( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates or updates a user's daily or weekly activity goal. + """Creates or updates a user's daily or weekly activity goal. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/create-activity-goal/ Args: - period: 'daily' or 'weekly' - type: Goal type from ActivityGoalType enum + period: Goal period (ActivityGoalPeriod.DAILY or ActivityGoalPeriod.WEEKLY) + type: Goal type from ActivityGoalType enum (e.g., ActivityGoalType.STEPS, + ActivityGoalType.FLOORS, ActivityGoalType.ACTIVE_MINUTES) value: Target value for the goal (must be positive) - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains goal object with the updated value + JSONDict: Goal object containing the updated activity goals Raises: - ValidationException: If value is not a positive integer + fitbit_client.exceptions.ValidationException: If value is not a positive integer + + Note: + - This endpoint uses units that correspond to the Accept-Language header provided + - Setting a new goal will override any previously set goal of the same type and period + - The response includes all current goals for the specified period, not just + the one being updated + - Daily goals: typically steps, floors, distance, calories, active minutes + - Weekly goals: typically steps, floors, distance, active minutes + - Not all goal types are available for both periods (e.g., calories is daily only) + - Goal progress can be tracked using the daily activity summary endpoints """ if value <= 0: raise ValidationException( @@ -89,30 +113,42 @@ def create_activity_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Records an activity to the user's log. Can either log a predefined activity by ID - or a custom activity by name with manual calorie entry. + """Records an activity to the user's activity log. + + This endpoint can be used in two ways: + 1. Log a predefined activity by specifying activity_id + 2. Log a custom activity by specifying activity_name and manual_calories API Reference: https://dev.fitbit.com/build/reference/web-api/activity/create-activity-log/ Args: - activity_id: ID of a predefined activity - activity_name: Name for a custom activity - manual_calories: Required when logging custom activity - start_time: Activity start time (HH:mm) + activity_id: ID of a predefined activity (get IDs from get_activity_type endpoint) + activity_name: Name for a custom activity (required if activity_id is not provided) + manual_calories: Calories burned (required when logging custom activity) + start_time: Activity start time in 24-hour format (HH:mm) duration_millis: Duration in milliseconds - date: Log date (YYYY-MM-DD) - distance: Optional distance value - distance_unit: Optional unit for distance - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + date: Log date in YYYY-MM-DD format or 'today' + distance: Optional distance value (required for some activity types) + distance_unit: Optional unit for distance ('steps', 'miles', 'km') + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains the created activity log entry with details + JSONDict: The created activity log entry with details of the recorded activity Raises: ValueError: If neither activity_id nor activity_name/manual_calories pair is provided - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If required parameters are missing + + Note: + - You must provide either activity_id OR both activity_name and manual_calories + - Some activities (like running or cycling) require a distance value + - The activity will be added to the user's activity history and count toward daily goals + - Calories and steps in the response may be estimated based on activity type and duration + - Activity types can be found using get_activity_type and get_frequent_activities endpoints + - Duration should be in milliseconds (e.g., 30 minutes = 1800000) + - Start time should be in 24-hour format (e.g., "14:30" for 2:30 PM) """ if activity_id: params = { @@ -156,31 +192,40 @@ def get_activity_log_list( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves a list of user's activity log entries before or after a given day. + """Returns a list of user's activity log entries before or after a given day. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-activity-log-list/ Args: - before_date: Get entries before this date (YYYY-MM-DD) - after_date: Get entries after this date (YYYY-MM-DD) - sort: Sort order - use 'asc' with after_date, 'desc' with before_date - limit: Number of records to return (max 100) - offset: Offset for pagination - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) - - Note: - Either before_date or after_date must be specified. - The offset parameter only supports 0 and using other values may break your application. - Use the pagination links in the response to iterate through results. + before_date: Return entries before this date (YYYY-MM-DD or 'today'). + You can optionally include time in ISO 8601 format (YYYY-MM-DDThh:mm:ss). + after_date: Return entries after this date (YYYY-MM-DD or 'today'). + You can optionally include time in ISO 8601 format (YYYY-MM-DDThh:mm:ss). + sort: Sort order - must use SortDirection.ASCENDING with after_date and + SortDirection.DESCENDING with before_date (default: DESCENDING) + limit: Number of records to return (max 100, default: 100) + offset: Offset for pagination (only 0 is reliably supported) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing activity logs matching the criteria + JSONDict: Activity logs matching the criteria with pagination information Raises: - ValidationException: If limit exceeds 100 or sort order is invalid - InvalidDateException: If date format is invalid + fitbit_client.exceptions.PaginationException: If neither before_date nor after_date is specified + fitbit_client.exceptions.PaginationException: If limit exceeds 100 or sort direction is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + + Note: + - Either before_date or after_date must be specified, but not both + - The offset parameter only reliably supports 0; use the "next" URL in the + pagination response to iterate through results + - Includes both manual and automatic activity entries + - Each activity entry contains detailed information about the activity, including + duration, calories, heart rate (if available), steps, and other metrics + - Activities are categorized based on Fitbit's internal activity type system + - The source field indicates whether the activity was logged manually by the user + or automatically by a Fitbit device """ params = {"sort": sort.value, "limit": limit, "offset": offset} if before_date: @@ -196,18 +241,31 @@ def get_activity_log_list( def create_favorite_activity( self, activity_id: int, user_id: str = "-", debug: bool = False ) -> Dict[Never, Never]: - """ - Adds an activity to the user's list of favorite activities. + """Adds an activity to the user's list of favorite activities. + + Favorite activities appear in a special section of the Fitbit app and website, + making them easier to access when logging manual activities. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/create-favorite-activity/ Args: - activity_id: ID of the activity to favorite - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + activity_id: ID of the activity to favorite (get IDs from get_activity_type endpoint) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response indicating success + Dict[Never, Never]: Empty dictionary on success, with HTTP 201 status code + + Raises: + fitbit_client.exceptions.InvalidRequestException: If activity_id is invalid + fitbit_client.exceptions.AuthorizationException: If not authorized to access the user + + Note: + - Favorites are used to quickly access common activities when logging manually + - Activity IDs can be obtained from get_activity_type or get_frequent_activities endpoints + - Users can have multiple favorite activities + - Favorites are displayed prominently in the Fitbit app's manual activity logging UI + - To retrieve the list of favorites, use the get_favorite_activities endpoint """ result = self._make_request( f"activities/favorite/{activity_id}.json", @@ -220,18 +278,34 @@ def create_favorite_activity( def delete_activity_log( self, activity_log_id: int, user_id: str = "-", debug: bool = False ) -> Dict[Never, Never]: - """ - Deletes a specific activity log entry from the user's activity history. + """Deletes a specific activity log entry from the user's activity history. + + This endpoint permanently removes an activity from the user's activity history. + Once deleted, the activity will no longer contribute to the user's daily totals + or achievements. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/delete-activity-log/ Args: - activity_log_id: ID of the activity log to delete - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + activity_log_id: ID of the activity log to delete (obtain from get_activity_log_list) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - None + Dict[Never, Never]: Empty dictionary on success, with HTTP 204 status code + + Raises: + fitbit_client.exceptions.InvalidRequestException: If activity_log_id is invalid + fitbit_client.exceptions.NotFoundException: If the activity log doesn't exist + fitbit_client.exceptions.AuthorizationException: If not authorized to delete this activity + + Note: + - Only manually logged activities can be deleted + - Automatic activities detected by Fitbit devices cannot be deleted + - Activity log IDs can be obtained from the get_activity_log_list endpoint + - Deleting an activity permanently removes it from the user's history + - Deletion immediately affects daily totals, goals, and achievements + - The deletion cannot be undone """ result = self._make_request( f"activities/{activity_log_id}.json", user_id=user_id, http_method="DELETE", debug=debug @@ -241,17 +315,31 @@ def delete_activity_log( def delete_favorite_activity( self, activity_id: int, user_id: str = "-", debug: bool = False ) -> None: - """ - Removes an activity from the user's list of favorite activities. + """Removes an activity from the user's list of favorite activities. + + This endpoint unfavorites a previously favorited activity. The activity will + still be available for logging but will no longer appear in the favorites list. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/delete-favorite-activity/ Args: activity_id: ID of the activity to unfavorite - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - None + None: Returns None on success with HTTP 204 status code + + Raises: + fitbit_client.exceptions.InvalidRequestException: If activity_id is invalid + fitbit_client.exceptions.NotFoundException: If the activity is not in favorites list + fitbit_client.exceptions.AuthorizationException: If not authorized to access the user + + Note: + - Removing a favorite doesn't delete the activity type, just removes it from favorites + - To get the list of current favorites, use the get_favorite_activities endpoint + - Activity IDs can be obtained from the get_favorite_activities response + - Unfavoriting an activity only affects the UI display in the Fitbit app """ result = self._make_request( f"activities/favorite/{activity_id}.json", @@ -264,18 +352,34 @@ def delete_favorite_activity( def get_activity_goals( self, period: ActivityGoalPeriod, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves the user's current activity goals for the specified period. + """Returns the user's current activity goals for the specified period. + + This endpoint retrieves the user's activity goals which are targets for steps, + distance, floors, active minutes, and calories that the user aims to achieve + within the specified time period (daily or weekly). API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-activity-goals/ Args: - period: 'daily' or 'weekly' - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + period: Goal period - either ActivityGoalPeriod.DAILY or ActivityGoalPeriod.WEEKLY + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains the current activity goals + JSONDict: The current activity goals containing targets for metrics like steps, + distance, floors, active minutes, and calories + + Raises: + fitbit_client.exceptions.InvalidRequestException: If period is invalid + fitbit_client.exceptions.AuthorizationException: If not authorized to access this user's data + + Note: + - Daily and weekly goals may have different available metrics + - Daily goals typically include: steps, floors, distance, active minutes, calories + - Weekly goals typically include: steps, floors, distance, active minutes (no calories) + - Units (miles/km) depend on the user's account settings + - Goals can be updated using the create_activity_goals endpoint + - The response will only include goals that have been set for the specified period """ result = self._make_request( f"activities/goals/{period.value}.json", user_id=user_id, debug=debug @@ -286,37 +390,67 @@ def get_activity_goals( def get_daily_activity_summary( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves a summary of the user's activities for a specific date. + """Returns a summary of the user's activities for a specific date. + + This endpoint provides a comprehensive summary of all activity metrics for the specified + date, including activity logs, goals, and daily totals for steps, distance, calories, and + active minutes. It serves as a convenient way to get a complete picture of a user's + activity for a single day. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-daily-activity-summary/ Args: - date: Date to get summary for (YYYY-MM-DD) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + date: Date to get summary for (YYYY-MM-DD or 'today') + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains activity summary for the specified date + JSONDict: Activity summary for the specified date containing logged activities, + daily goals, and summary metrics (steps, distance, calories, minutes at + different activity levels) Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + + Note: + The response includes data in the unit system specified by the Accept-Language header. + Daily summary data for elevation (elevation, floors) is only included for users with + a device that has an altimeter. Goals are included only for today and up to 21 days + in the past. The goals section will only include goals that have been set by the user. + Active minutes include veryActiveMinutes, fairlyActiveMinutes, and lightlyActiveMinutes. """ result = self._make_request(f"activities/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) def get_activity_type(self, activity_id: int, debug: bool = False) -> JSONDict: - """ - Gets the details of a single activity type from Fitbit's activity database. + """Returns the details of a single activity type from Fitbit's activity database. + + This endpoint retrieves information about a specific activity type including its name, + description, and MET (Metabolic Equivalent of Task) value. Activity types are + standardized categories like "Running", "Swimming", or "Yoga" that can be used + when logging activities. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-activity-type/ Args: activity_id: ID of the activity type to retrieve - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains activity type details + JSONDict: Activity type details including name, description, MET values, and + different intensity levels (light, moderate, vigorous) + + Raises: + fitbit_client.exceptions.InvalidRequestException: If activity_id is invalid + fitbit_client.exceptions.NotFoundException: If the activity type doesn't exist + + Note: + - This endpoint doesn't require a user_id as it accesses the global activity database + - MET values represent the energy cost of activities (higher values = more intense) + - Activity types are used when logging manual activities via create_activity_log + - To find activity IDs, use get_all_activity_types or get_frequent_activities + - The hasSpeed field indicates whether the activity supports distance tracking + - Activity levels (Light, Moderate, Vigorous) represent intensity variations """ result = self._make_request( f"activities/{activity_id}.json", requires_user_id=False, debug=debug @@ -324,78 +458,155 @@ def get_activity_type(self, activity_id: int, debug: bool = False) -> JSONDict: return cast(JSONDict, result) def get_all_activity_types(self, debug: bool = False) -> JSONDict: - """ - Retrieves the complete list of available activities and their details. + """Returns the complete list of all available activity types in Fitbit's database. + + This endpoint retrieves a comprehensive list of standardized activity types that + can be used when logging manual activities. Each activity includes its ID, name, + description, and MET (Metabolic Equivalent of Task) values. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-all-activity-types/ - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + + Args: + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains list of all activity types + JSONDict: Complete list of activity types organized by categories (Cardio, Sports, etc.), + with each activity containing its ID, name, description, and MET value + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized to access the API + + Note: + - This endpoint doesn't require a user_id as it accesses the global activity database + - Activities are organized into categories (e.g., Cardio, Sports, Water Activities) + - The response can be large as it contains all available activities + - Use the activity IDs from this response when calling create_activity_log + - For a more manageable list, consider using get_frequent_activities instead + - MET values indicate intensity (higher values = more intense activity) """ result = self._make_request("activities.json", requires_user_id=False, debug=debug) return cast(JSONDict, result) def get_favorite_activities(self, user_id: str = "-", debug: bool = False) -> JSONList: - """ - Gets the list of activities that the user has marked as favorite. + """Returns the list of activities that the user has marked as favorites. + + Favorite activities are those the user has explicitly marked for quick access + when manually logging activities. These appear in a dedicated section of the + activity logging interface in the Fitbit app. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-favorite-activities/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains list of favorite activities + JSONList: List of favorite activities with details including activity ID, name, + description, MET value, and when the activity was added as a favorite + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized to access the user's data + + Note: + - Favorites are used for quick access when logging manual activities + - Activities can be added to favorites using create_favorite_activity + - Activities can be removed from favorites using delete_favorite_activity + - The dateAdded field shows when the activity was marked as favorite + - The calories field shows an estimate based on the activity's MET value + - If the user has no favorites, an empty array is returned """ result = self._make_request("activities/favorite.json", user_id=user_id, debug=debug) return cast(JSONList, result) def get_frequent_activities(self, user_id: str = "-", debug: bool = False) -> JSONList: - """ - Gets the list of activities that the user logs most frequently. + """Returns the list of activities that the user logs most frequently. + + This endpoint provides a personalized list of activities based on the user's + activity logging history. It helps provide quick access to activities the user + regularly logs, even if they're not explicitly marked as favorites. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-frequent-activities/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains list of frequently logged activities + JSONList: List of frequently logged activities with details including activity ID, + name, description, MET value, and typical metrics like duration and distance + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized to access the user's data + + Note: + - This list is automatically generated based on the user's activity logging patterns + - Unlike favorites, users cannot directly add or remove activities from this list + - Activities with most logged instances appear in this list + - The dateAdded field shows when the activity was most recently logged + - If the user has no activity history, an empty array is returned + - This list is a good source of relevant activity IDs for create_activity_log """ result = self._make_request("activities/frequent.json", user_id=user_id, debug=debug) return cast(JSONList, result) def get_recent_activity_types(self, user_id: str = "-", debug: bool = False) -> JSONList: - """ - Gets the list of activities that the user has logged recently. + """Returns the list of activities that the user has logged recently. + + This endpoint retrieves activities that the user has manually logged in the + recent past, sorted by most recent first. It provides a chronological view + of the user's activity logging history. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-recent-activity-types/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains list of recent activities + JSONList: List of recently logged activities with details including activity ID, name, + description, MET value, and when each activity was logged + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized to access the user's data + + Note: + - Activities are listed in reverse chronological order (newest first) + - Only manually logged activities appear in this list + - The dateAdded field shows when the activity was logged + - If the user has no recent activity logs, an empty array is returned + - This list differs from get_activity_log_list which shows actual activity instances + - Unlike favorites, this list is purely historical and not for quick access """ result = self._make_request("activities/recent.json", user_id=user_id, debug=debug) return cast(JSONList, result) def get_lifetime_stats(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves the user's lifetime activity statistics and personal records. + """Returns the user's lifetime activity statistics and personal records. + + This endpoint provides cumulative totals of steps, distance, floors, and active minutes, + as well as personal activity records like "most steps in one day". API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-lifetime-stats/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains lifetime activity stats and records + JSONDict: Lifetime statistics containing cumulative totals (steps, distance, floors) + and personal records (best days) for various activity metrics, divided into + "total" (all activities) and "tracker" (device-tracked only) categories + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized to access this user's data + + Note: + - "Total" includes manually logged activities, while "tracker" only includes device-tracked data + - A value of -1 indicates that the metric is not available + - The "best" section contains personal records with dates and values + - Units (miles/km) depend on the user's account settings + - Lifetime stats accumulate from the date the user created their Fitbit account + - Stats are updated in near real-time as new activities are recorded """ result = self._make_request("activities.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -407,23 +618,36 @@ def get_activity_tcx( user_id: str = "-", debug: bool = False, ) -> str: - """ - Retrieves the TCX (Training Center XML) data for a specific activity log. TCX files - contain GPS, heart rate, and lap data recorded during the logged exercise. + """Returns the TCX (Training Center XML) data for a specific activity log. + + TCX (Training Center XML) is a data exchange format developed by Garmin that contains + detailed GPS coordinates, heart rate data, lap information, and other metrics recorded + during GPS-tracked activities like running, cycling, or walking. API Reference: https://dev.fitbit.com/build/reference/web-api/activity/get-activity-tcx/ Args: - log_id: ID of the activity log to retrieve - include_partial_tcx: Include TCX points when GPS data is not available - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + log_id: ID of the activity log to retrieve (obtain from get_activity_log_list) + include_partial_tcx: Include TCX points even when GPS data is not available (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains TCX data for the activity + str: Raw XML string containing TCX data in Training Center XML format + + Raises: + fitbit_client.exceptions.InvalidRequestException: If log_id is invalid + fitbit_client.exceptions.NotFoundException: If the activity log doesn't exist + fitbit_client.exceptions.InsufficientScopeException: If location scope is not authorized Note: - Requires both 'activity' and 'location' scopes to be authorized. + - Requires both 'activity' and 'location' OAuth scopes to be authorized + - The log must be from a GPS-tracked activity (e.g., running, cycling with GPS enabled) + - TCX data includes timestamps, GPS coordinates, elevation, heart rate, and lap data + - TCX files can be imported into third-party fitness analysis tools + - Setting include_partial_tcx=True will include points even if GPS signal was lost + - Not all activities have TCX data available (e.g., manually logged activities) + - To check if an activity has GPS data, look for hasGps=True in the activity log """ params = {"includePartialTCX": include_partial_tcx} if include_partial_tcx else None result = self._make_request( diff --git a/fitbit_client/resources/activity_timeseries.py b/fitbit_client/resources/activity_timeseries.py index 68ead3f..d6a8015 100644 --- a/fitbit_client/resources/activity_timeseries.py +++ b/fitbit_client/resources/activity_timeseries.py @@ -15,10 +15,25 @@ class ActivityTimeSeriesResource(BaseResource): - """ - Handles Fitbit Activity Time Series API endpoints. + """Provides access to Fitbit Activity Time Series API for retrieving historical activity data. + + This resource handles endpoints for retrieving time series data for various activity metrics + such as steps, distance, calories, active minutes, and floors over specified time periods. + Time series data is useful for analyzing trends, creating visualizations, and tracking + progress over time. API Reference: https://dev.fitbit.com/build/reference/web-api/activity-timeseries/ + + Required Scopes: + - activity (for all activity time series endpoints) + + Note: + - Time series data is available from the date the user created their Fitbit account + - Data is organized by date with one data point per day + - Various activity metrics are available including steps, distance, floors, calories, etc. + - Historical data can be accessed either by period (e.g., 1d, 7d, 30d) or date range + - Maximum date ranges vary by resource type (most allow ~3 years of historical data) + - For more granular intraday data, see the Intraday resource """ @validate_date_param() @@ -30,20 +45,37 @@ def get_activity_timeseries_by_date( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Get activity time series data for a period starting from the specified date. + """Returns activity time series data for a period ending on the specified date. + + This endpoint provides historical activity data for a specific time period (e.g., 1d, 7d, 30d) + ending on the specified date. It's useful for retrieving recent activity history with a + consistent timeframe. API Reference: https://dev.fitbit.com/build/reference/web-api/activity-timeseries/get-activity-timeseries-by-date/ - Parameters: - resource_path: The resource path from ActivityTimeSeriesPath enum + Args: + resource_path: The resource path from ActivityTimeSeriesPath enum (e.g., + ActivityTimeSeriesPath.STEPS, ActivityTimeSeriesPath.DISTANCE) date: The end date in YYYY-MM-DD format or "today" - period: Time period to get data for - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + period: Time period to get data for (e.g., Period.ONE_DAY, Period.SEVEN_DAYS, Period.THIRTY_DAYS) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + JSONDict: Activity time series data for the specified activity metric and time period Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidRequestException: If resource_path or period is invalid + + Note: + - The response format varies slightly depending on the resource_path + - All values are returned as strings and need to be converted to appropriate types + - For numeric resources like steps, values should be converted to integers + - The number of data points equals the number of days in the period + - Data is returned in ascending date order (oldest first) + - If no data exists for a particular day, the value may be "0" or the day may be omitted + - Period options include 1d, 7d, 30d, 1w, 1m, 3m, 6m, 1y """ result = self._make_request( f"activities/{resource_path.value}/date/{date}/{period.value}.json", @@ -61,26 +93,40 @@ def get_activity_timeseries_by_date_range( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Get activity time series data for a specified date range. + """Returns activity time series data for a specified date range. + + This endpoint provides historical activity data for a custom date range between two + specified dates. It's useful for analyzing activity patterns over specific time periods + or generating custom reports. API Reference: https://dev.fitbit.com/build/reference/web-api/activity-timeseries/get-activity-timeseries-by-date-range/ - Parameters: - resource_path: The resource path from ActivityTimeSeriesPath enum + Args: + resource_path: The resource path from ActivityTimeSeriesPath enum (e.g., + ActivityTimeSeriesPath.STEPS, ActivityTimeSeriesPath.CALORIES) start_date: The start date in YYYY-MM-DD format or "today" end_date: The end date in YYYY-MM-DD format or "today" - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + JSONDict: Activity time series data for the specified activity metric and date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range is invalid or exceeds maximum allowed days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds maximum allowed days + fitbit_client.exceptions.InvalidRequestException: If resource_path is invalid Note: - Maximum date ranges vary by resource type: - - activityCalories: 30 days - - Most other resources: 1095 days (~3 years) + - Maximum date ranges vary by resource type: + * ActivityTimeSeriesPath.ACTIVITY_CALORIES: 30 days maximum + * Most other resources: 1095 days (~3 years) maximum + - The response format varies slightly depending on the resource_path + - All values are returned as strings and need to be converted to appropriate types + - Data is returned in ascending date order (oldest first) + - The date range is inclusive of both start_date and end_date + - For longer date ranges, consider making multiple requests with smaller ranges + - If no data exists for a particular day, the value may be "0" or the day may be omitted """ result = self._make_request( f"activities/{resource_path.value}/date/{start_date}/{end_date}.json", diff --git a/fitbit_client/resources/base.py b/fitbit_client/resources/base.py index 056b82e..e423e26 100644 --- a/fitbit_client/resources/base.py +++ b/fitbit_client/resources/base.py @@ -42,8 +42,13 @@ class BaseResource: - """ - Base class for all Fitbit API resources. + """Provides foundational functionality for all Fitbit API resource classes. + + The BaseResource class implements core functionality that all specific resource + classes (Activity, Sleep, User, etc.) inherit. It handles API communication, + authentication, error handling, URL construction, logging, and debugging support. + + API Reference: https://dev.fitbit.com/build/reference/web-api/ The Fitbit API has two types of endpoints: 1. Public endpoints: /{endpoint} @@ -51,27 +56,39 @@ class BaseResource: 2. User endpoints: /user/{user_id}/{endpoint} Used for user-specific operations like logging activities and food. - This base class provides common functionality for both types of endpoints, including: - - URL construction - - Request handling and error management - - Response parsing and logging - - Debug capabilities for API interaction + This base class provides: + - URL construction for both endpoint types + - Request handling with comprehensive error management + - Response parsing with type safety + - Detailed logging of requests, responses, and errors + - Debug capabilities for API troubleshooting - OAuth2 authentication management + + Note: + All resource-specific classes inherit from this class and use its _make_request + method to communicate with the Fitbit API. The class handles different response + formats (JSON, XML), empty responses, and various error conditions automatically. """ API_BASE: str = "https://api.fitbit.com" def __init__(self, oauth_session: OAuth2Session, locale: str, language: str) -> None: - """ - Initialize a new resource instance. + """Initialize a new resource instance with authentication and locale settings. Args: oauth_session: Authenticated OAuth2 session for API requests locale: Locale for API responses (e.g., 'en_US') language: Language for API responses (e.g., 'en_US') - The locale and language settings affect how the API formats responses, - particularly for things like dates, times, and measurement units. + The locale and language settings affect how the Fitbit API formats responses, + particularly for things like: + - Date and time formats + - Measurement units (imperial vs metric) + - Number formats (decimal separator, thousands separator) + - Currency symbols and formats + + These settings are passed with each request in the Accept-Locale and + Accept-Language headers. """ self.headers: Dict = {"Accept-Locale": locale, "Accept-Language": language} self.oauth: OAuth2Session = oauth_session @@ -86,8 +103,11 @@ def _build_url( requires_user_id: bool = True, api_version: str = "1", ) -> str: - """ - Build full API URL with support for both public and user-specific endpoints. + """Constructs a complete Fitbit API URL for the specified endpoint. + + This method handles both public endpoints (database-wide operations) and + user-specific endpoints (operations on user data) by constructing the + appropriate URL pattern. Args: endpoint: API endpoint path (e.g., 'foods/log') @@ -96,15 +116,18 @@ def _build_url( api_version: API version to use (default: "1") Returns: - Complete API URL - - By default, endpoints are assumed to be user-specific. Set requires_user_id=False - for public endpoints that operate on Fitbit's global database rather than - user-specific data. + str: Complete API URL for the requested endpoint Example URLs: User endpoint: https://api.fitbit.com/1/user/-/foods/log.json Public endpoint: https://api.fitbit.com/1/foods/search.json + + Note: + By default, endpoints are assumed to be user-specific. Set requires_user_id=False + for public endpoints that operate on Fitbit's global database rather than + user-specific data. The user_id parameter is ignored when requires_user_id is False. + + The special user_id value "-" indicates the currently authenticated user. """ endpoint = endpoint.strip("/") if requires_user_id: @@ -378,43 +401,52 @@ def _make_request( api_version: str = "1", debug: bool = False, ) -> JSONType: - """ - Make a request to the Fitbit API with comprehensive error handling and debugging support. + """Makes a request to the Fitbit API with error handling and debugging support. + + This core method handles all API communication for the library. It constructs URLs, + sends requests with proper authentication, processes responses, handles errors, + and provides debugging capabilities. Args: - endpoint: API endpoint path + endpoint: API endpoint path (e.g., 'activities/steps') data: Optional form data for POST requests json: Optional JSON data for POST requests - params: Optional query parameters + params: Optional query parameters for GET requests headers: Optional dict of additional HTTP headers to add to the request user_id: User ID, defaults to '-' for authenticated user requires_user_id: Whether the endpoint requires user_id in the path http_method: HTTP method to use (GET, POST, DELETE) api_version: API version to use (default: "1") - debug: If True, print curl command instead of making request + debug: If True, prints a curl command to stdout to help with debugging Returns: - - Dict[str, JSONType]: For most JSON object responses - - List[JSONType]: For endpoints that return JSON arrays - - str: For XML/TCX responses - - None: For (most) successful DELETE operations or debug mode + JSONType: The API response in one of these formats: + - Dict[str, Any]: For most JSON object responses + - List[Any]: For endpoints that return JSON arrays + - str: For XML/TCX responses + - None: For successful DELETE operations or debug mode Raises: - FitbitAPIException: Base class for all Fitbit API exceptions - AuthorizationException: When there are authorization errors - ExpiredTokenException: When the OAuth token has expired - InsufficientPermissionsException: When the app lacks required permissions - NotFoundException: When the requested resource doesn't exist - RateLimitExceededException: When rate limits are exceeded - ValidationException: When request parameters are invalid - SystemException: When there are server-side errors - - Debug Mode: + fitbit_client.exceptions.FitbitAPIException: Base class for all Fitbit API exceptions + fitbit_client.exceptions.AuthorizationException: When there are authorization errors + fitbit_client.exceptions.ExpiredTokenException: When the OAuth token has expired + fitbit_client.exceptions.InsufficientPermissionsException: When the app lacks required permissions + fitbit_client.exceptions.NotFoundException: When the requested resource doesn't exist + fitbit_client.exceptions.RateLimitExceededException: When rate limits are exceeded + fitbit_client.exceptions.ValidationException: When request parameters are invalid + fitbit_client.exceptions.SystemException: When there are server-side errors + + Note: + Debug Mode functionality: When debug=True, this method prints a curl command to stdout that can - be used to replicate the request manually. This is useful for: + be used to replicate the request manually, which is useful for: - Testing API endpoints directly - Debugging authentication/scope issues - Verifying request structure + - Troubleshooting permission problems + + The method automatically handles different response formats and appropriate + error types based on the API's response. """ calling_method = self._get_calling_method() url = self._build_url(endpoint, user_id, requires_user_id, api_version) diff --git a/fitbit_client/resources/body.py b/fitbit_client/resources/body.py index c251dc7..42cc63b 100644 --- a/fitbit_client/resources/body.py +++ b/fitbit_client/resources/body.py @@ -12,38 +12,48 @@ class BodyResource(BaseResource): - """ - Handles Fitbit Body API endpoints for managing body measurements. - - This resource provides endpoints for managing: - - Body fat logs and goals - - Weight logs and goals - - BMI data (derived from weight logs) + """Provides access to Fitbit Body API for managing body measurements and goals. - All data is returned in the unit system specified by the Accept-Language header. + This resource handles endpoints for tracking and managing body metrics including + weight, body fat percentage, and BMI. It supports creating and retrieving logs + of measurements, setting goals, and accessing historical body data. API Reference: https://dev.fitbit.com/build/reference/web-api/body/ - Scope: weight + Required Scopes: + - weight: Required for all endpoints in this resource + + Note: + All weight and body fat data is returned in the unit system specified by the + Accept-Language header provided during client initialization (imperial for en_US, + metric for most other locales). BMI values are calculated automatically from + weight logs and user profile data. """ def create_bodyfat_goal(self, fat: float, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Create or update a user's body fat goal. + """Creates or updates a user's body fat percentage goal. + + This endpoint allows setting a target body fat percentage goal that will be + displayed in the Fitbit app and used to track progress over time. API Reference: https://dev.fitbit.com/build/reference/web-api/body/create-bodyfat-goal/ Args: - fat: Target body fat percentage in the format X.XX - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + fat: Target body fat percentage in the format X.XX (e.g., 22.5 for 22.5%) + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing goal object with the updated body fat target value + JSONDict: The created body fat percentage goal Raises: - ValidationException: If fat percentage is not in valid range - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.ValidationException: If fat percentage is not in valid range + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + This endpoint requires the 'weight' OAuth scope. Body fat values should be specified + as a percentage in decimal format (e.g., 22.5 for 22.5%). Typical healthy ranges vary + by age, gender, and fitness level, but generally fall between 10-30%. """ result = self._make_request( "body/log/fat/goal.json", @@ -63,34 +73,33 @@ def create_bodyfat_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Create a body fat log entry. + """Creates a body fat log entry for tracking body composition over time. + + This endpoint allows recording a body fat percentage measurement for a specific + date and time, which will be displayed in the Fitbit app and used in trends. API Reference: https://dev.fitbit.com/build/reference/web-api/body/create-bodyfat-log/ Args: - fat: Body fat measurement in the format X.XX - date: Log date in YYYY-MM-DD format + fat: Body fat measurement in the format X.XX (e.g., 22.5 for 22.5%) + date: Log date in YYYY-MM-DD format or 'today' time: Optional time of measurement in HH:mm:ss format. If not provided, - will default to last second of the day. - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + will default to last second of the day (23:59:59). + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing the created log entry including: - - date: The date the measurement was recorded - - fat: The body fat percentage - - logId: Unique identifier for the log entry - - source: Origin of the data (e.g., "API") - - time: Time the measurement was recorded + JSONDict: The created body fat percentage log entry Raises: - InvalidDateException: If date format is invalid - ValidationException: If fat percentage is not in valid range - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If fat percentage is not in valid range + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: The returned Body Fat Log IDs are unique to the user, but not globally unique. + The 'source' field will be set to "API" for entries created through this endpoint. + Multiple entries can be logged for the same day with different timestamps. """ params = {"fat": fat, "date": date} if time: @@ -109,35 +118,38 @@ def create_weight_goal( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Create or update a user's weight goal. + """Creates or updates a user's weight goal for tracking progress. + + This endpoint sets a target weight goal with starting parameters, which will be + used to track progress in the Fitbit app and determine recommended weekly changes. API Reference: https://dev.fitbit.com/build/reference/web-api/body/create-weight-goal/ Args: - start_date: Weight goal start date in YYYY-MM-DD format + start_date: Weight goal start date in YYYY-MM-DD format or 'today' start_weight: Starting weight before reaching goal in X.XX format weight: Optional target weight goal in X.XX format. Required if user doesn't have an existing weight goal. - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing weight goal details including: - - goalType: The type of goal (e.g., "LOSE") - - startDate: Goal start date - - startWeight: Initial weight - - weight: Target weight - - weightThreshold: Recommended weekly weight change + JSONDict: The created weight goal with goal type and recommended changes Raises: - InvalidDateException: If start_date format is invalid - ValidationException: If weight values are invalid - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.InvalidDateException: If start_date format is invalid + fitbit_client.exceptions.ValidationException: If weight values are invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: Weight values should be specified in the unit system that corresponds - to the Accept-Language header provided during client initialization. + to the Accept-Language header provided during client initialization + (pounds for en_US, kilograms for most other locales). + + The goalType is automatically determined by comparing start_weight to weight: + - If target < start: "LOSE" + - If target > start: "GAIN" + - If target = start: "MAINTAIN" """ params = {"startDate": start_date, "startWeight": start_weight} if weight is not None: @@ -160,37 +172,41 @@ def create_weight_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Create a weight log entry. + """Creates a weight log entry for tracking body weight over time. + + This endpoint allows recording a weight measurement for a specific + date and time, which will be displayed in the Fitbit app and used to + calculate BMI and track progress toward weight goals. API Reference: https://dev.fitbit.com/build/reference/web-api/body/create-weight-log/ Args: - weight: Weight measurement in X.XX format - date: Log date in YYYY-MM-DD format + weight: Weight measurement in X.XX format (in kg or lbs based on user settings) + date: Log date in YYYY-MM-DD format or 'today' time: Optional time of measurement in HH:mm:ss format. If not provided, - will default to last second of the day. - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + will default to last second of the day (23:59:59). + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing the created log entry including: - - bmi: Calculated BMI - - date: Log entry date - - logId: Unique identifier for the weight log - - source: Origin of the data (e.g., "API") - - time: Time of measurement - - weight: Weight value + JSONDict: The created weight log entry with BMI calculation Raises: - InvalidDateException: If date format is invalid - ValidationException: If weight value is invalid - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If weight value is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: Weight values should be in the unit system that corresponds to the - Accept-Language header provided during client initialization. - Weight Log IDs are unique to the user but not globally unique. + Accept-Language header provided during client initialization + (pounds for en_US, kilograms for most other locales). + + BMI (Body Mass Index) is automatically calculated using the provided weight + and the height stored in the user's profile settings. If the user's height + is not set, BMI will not be calculated. + + The 'source' field will be set to "API" for entries created through this endpoint. + Multiple weight entries can be logged for the same day with different timestamps. """ params = {"weight": weight, "date": date} if time: @@ -203,23 +219,30 @@ def create_weight_log( def delete_bodyfat_log( self, bodyfat_log_id: str, user_id: str = "-", debug: bool = False ) -> None: - """ - Delete a body fat log entry. + """Deletes a body fat log entry permanently. + + This endpoint permanently removes a body fat percentage measurement from the user's logs. + Once deleted, the data cannot be recovered. API Reference: https://dev.fitbit.com/build/reference/web-api/body/delete-bodyfat-log/ Args: bodyfat_log_id: ID of the body fat log entry to delete - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - None + None: This endpoint returns an empty response on success Raises: - ValidationException: If bodyfat_log_id is invalid - AuthorizationException: If required scope is not granted - NotFoundException: If log entry does not exist + fitbit_client.exceptions.ValidationException: If bodyfat_log_id is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + fitbit_client.exceptions.NotFoundException: If log entry does not exist + + Note: + Body fat log IDs can be obtained from the get_bodyfat_log method. + Deleting logs will affect historical averages and trends in the Fitbit app. + This operation cannot be undone, so use it cautiously. """ result = self._make_request( f"body/log/fat/{bodyfat_log_id}.json", @@ -232,23 +255,35 @@ def delete_bodyfat_log( def delete_weight_log( self, weight_log_id: str, user_id: str = "-", debug: bool = False ) -> None: - """ - Delete a weight log entry. + """Deletes a weight log entry permanently. + + This endpoint permanently removes a weight measurement from the user's logs. + Once deleted, the data cannot be recovered. API Reference: https://dev.fitbit.com/build/reference/web-api/body/delete-weight-log/ Args: weight_log_id: ID of the weight log entry to delete - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - None + None: This endpoint returns an empty response on success Raises: - ValidationException: If weight_log_id is invalid - AuthorizationException: If required scope is not granted - NotFoundException: If log entry does not exist + fitbit_client.exceptions.ValidationException: If weight_log_id is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + fitbit_client.exceptions.NotFoundException: If log entry does not exist + + Note: + Weight log IDs can be obtained from the get_weight_logs method. + Deleting logs will affect historical averages, BMI calculations, and + trend data in the Fitbit app. + + When the most recent weight log is deleted, the previous weight log + becomes the current weight displayed in the Fitbit app. + + This operation cannot be undone, so use it cautiously. """ result = self._make_request( f"body/log/weight/{weight_log_id}.json", @@ -261,34 +296,37 @@ def delete_weight_log( def get_body_goals( self, goal_type: BodyGoalType, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get a user's body fat or weight goals. + """Retrieves a user's body fat percentage or weight goals. + + This endpoint returns the currently set goals for either body fat percentage + or weight, including target values and, for weight goals, additional parameters + like start weight and recommended weekly changes. API Reference: https://dev.fitbit.com/build/reference/web-api/body/get-body-goals/ Args: - goal_type: Type of goal to retrieve (fat or weight) - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + goal_type: Type of goal to retrieve (BodyGoalType.FAT or BodyGoalType.WEIGHT) + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing goal information. For weight goals includes: - - goalType: Type of goal (e.g., "LOSE") - - startDate: Goal start date - - startWeight: Initial weight - - weight: Target weight - - weightThreshold: Recommended weekly weight change - - For fat goals includes: - - fat: Target body fat percentage + JSONDict: Goal information for either weight or body fat percentage Raises: - ValidationException: If goal_type is invalid - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.ValidationException: If goal_type is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - All weight values are returned in the unit system specified by - the Accept-Language header provided during client initialization. + Weight values are returned in the unit system specified by + the Accept-Language header provided during client initialization + (pounds for en_US, kilograms for most other locales). + + The weightThreshold represents the recommended weekly weight change + (loss or gain) to achieve the goal in a healthy manner. This is + calculated based on the difference between starting and target weight. + + If no goal has been set for the requested type, an empty goal object + will be returned. """ result = self._make_request( f"body/log/{goal_type.value}/goal.json", user_id=user_id, debug=debug @@ -297,73 +335,83 @@ def get_body_goals( @validate_date_param() def get_bodyfat_log(self, date: str, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Get a user's body fat logs for a given date. + """Retrieves a user's body fat percentage logs for a specific date. + + This endpoint returns all body fat percentage measurements recorded on the + specified date, including those logged manually, via the API, or synced from + compatible scales. API Reference: https://dev.fitbit.com/build/reference/web-api/body/get-bodyfat-log/ Args: - date: The date in YYYY-MM-DD format - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + date: The date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing list of fat logs for the date, each including: - - date: When the measurement was recorded - - fat: Body fat percentage - - logId: Unique identifier for this log entry - - source: Origin of the data (e.g., "API", "Aria") - - time: Time of measurement + JSONDict: Body fat percentage logs for the specified date Raises: - InvalidDateException: If date format is invalid - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: The source field indicates how the data was recorded: - - "API": From Web API or manual entry + - "API": From Web API or manual entry in the Fitbit app - "Aria"/"Aria2": From Fitbit Aria scale - "AriaAir": From Fitbit Aria Air scale - - "Withings": From Withings scale + - "Withings": From Withings scale connected to Fitbit + + Body fat percentage is measured differently depending on the source: + - Bioelectrical impedance for compatible scales + - User-entered estimates for manual entries + + Multiple logs may exist for the same date if measurements were taken + at different times or from different sources. """ result = self._make_request(f"body/log/fat/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @validate_date_param() def get_weight_logs(self, date: str, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Get a user's weight logs for a given date. + """Retrieves a user's weight logs for a specific date. + + This endpoint returns all weight measurements recorded on the specified date, + including those logged manually, via the API, or synced from compatible scales. API Reference: https://dev.fitbit.com/build/reference/web-api/body/get-weight-log/ Args: - date: The date in YYYY-MM-DD format - user_id: Optional user ID. Use "-" (dash) for current logged-in user. + date: The date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing list of weight logs for the date, each including: - - bmi: Calculated BMI value - - date: When the measurement was recorded - - logId: Unique identifier for this log entry - - source: Origin of the data (e.g., "API", "Aria") - - time: Time of measurement - - weight: Weight value in specified unit system - - fat: Body fat percentage if available + JSONDict: Weight logs for the specified date with BMI calculations Raises: - InvalidDateException: If date format is invalid - AuthorizationException: If required scope is not granted + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: The source field indicates how the data was recorded: - - "API": From Web API or manual entry + - "API": From Web API or manual entry in the Fitbit app - "Aria"/"Aria2": From Fitbit Aria scale - "AriaAir": From Fitbit Aria Air scale - - "Withings": From Withings scale + - "Withings": From Withings scale connected to Fitbit Weight values are returned in the unit system specified by the - Accept-Language header provided during client initialization. + Accept-Language header provided during client initialization + (pounds for en_US, kilograms for most other locales). + + BMI (Body Mass Index) is automatically calculated using the recorded weight + and the height stored in the user's profile settings. + + The "fat" field is only included when body fat percentage was measured + along with weight (typically from compatible scales like Aria). + + Multiple logs may exist for the same date if measurements were taken + at different times or from different sources. """ result = self._make_request( f"body/log/weight/date/{date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/body_timeseries.py b/fitbit_client/resources/body_timeseries.py index 1d5173b..abb60fc 100644 --- a/fitbit_client/resources/body_timeseries.py +++ b/fitbit_client/resources/body_timeseries.py @@ -16,10 +16,31 @@ class BodyTimeSeriesResource(BaseResource): - """ - Handles Fitbit Body Time Series API endpoints for retrieving body measurements over time. + """Provides access to Fitbit Body Time Series API for retrieving body measurements over time. + + This resource handles endpoints for retrieving historical body measurement data + including weight, body fat percentage, and BMI over specified time periods. + It enables tracking and analysis of body composition changes over time. API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/ + + Required Scopes: + - weight: Required for all endpoints in this resource + + Note: + The Body Time Series API provides access to historical body measurement data, + which is useful for tracking trends and progress over time. Each measurement + type (weight, body fat, BMI) has specific date range limitations: + + - BMI data: Available for up to 1095 days (3 years) + - Body fat data: Available for up to 30 days + - Weight data: Available for up to 31 days + + Measurements are returned in the user's preferred unit system (metric or imperial), + which can be determined by the Accept-Language header provided during API calls. + + Data is recorded when users log body measurements manually or when they use + connected scales that automatically sync with their Fitbit account. """ @validate_date_param() @@ -31,28 +52,48 @@ def get_body_timeseries_by_date( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Get body metrics for a given resource over a period of time by specifying a date and time period. + """Retrieves body metrics for a given resource over a period ending on the specified date. + + This endpoint returns time series data for the specified body measurement type + (BMI, body fat percentage, or weight) over a time period ending on the given date. API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/get-body-timeseries-by-date/ Args: - resource_type: Type of body measurement (bmi, fat, or weight) + resource_type: Type of body measurement (BodyResourceType.BMI, BodyResourceType.FAT, + or BodyResourceType.WEIGHT) date: The end date in YYYY-MM-DD format or 'today' - period: The range for which data will be returned - user_id: Optional user ID, defaults to current user + period: The time period for data retrieval (e.g., BodyTimePeriod.ONE_DAY, + BodyTimePeriod.SEVEN_DAYS, etc.) + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Body measurements for the specified period + JSONDict: Time series data for the specified body measurement type and time period Raises: - InvalidDateException: If date format is invalid - ValidationException: If period is not supported for fat/weight + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If period is not supported for fat/weight + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - For fat and weight resources, only periods up to 1m are supported - 3m, 6m, 1y, max are not available. - Data is returned using units based on the Accept-Language header provided. + For fat and weight resources, only periods up to BodyTimePeriod.ONE_MONTH are supported. + The periods BodyTimePeriod.THREE_MONTHS, BodyTimePeriod.SIX_MONTHS, + BodyTimePeriod.ONE_YEAR, and BodyTimePeriod.MAX are not available for these resources. + + The JSON field name in the response varies based on resource_type: + - BodyResourceType.BMI: "body-bmi" + - BodyResourceType.FAT: "body-fat" + - BodyResourceType.WEIGHT: "body-weight" + + Values are returned as strings representing: + - Weight: kilograms or pounds based on user settings + - Body fat: percentage (e.g., "22.5" means 22.5%) + - BMI: standard BMI value (e.g., "21.3") + + The endpoint returns all available data points within the requested period, + which may include multiple measurements per day if the user logged them. + Days without measurements will not appear in the results. """ # Validate period restrictions for fat and weight if resource_type in (BodyResourceType.FAT, BodyResourceType.WEIGHT): @@ -86,32 +127,52 @@ def get_body_timeseries_by_date_range( debug: bool = False, ) -> ( JSONDict - ): # Trivia! this the one place in the whole API where # it's not called "start-date" ¯\_(ツ)_/¯. - """ - Get body metrics for a given resource over a period of time by specifying a date range. + ): # Note: This is the one place in the whole API where it's called "begin_date" not "start_date" ¯\_(ツ)_/¯ + """Retrieves body metrics for a given resource over a specified date range. + + This endpoint returns time series data for the specified body measurement type + (BMI, body fat percentage, or weight) between two specified dates. API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/get-body-timeseries-by-date-range/ Args: - resource_type: Type of body measurement (bmi, fat, or weight) + resource_type: Type of body measurement (BodyResourceType.BMI, BodyResourceType.FAT, + or BodyResourceType.WEIGHT) begin_date: The start date in YYYY-MM-DD format or 'today' end_date: The end date in YYYY-MM-DD format or 'today' - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Body measurements for the specified date range + JSONDict: Time series data for the specified body measurement type and date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range exceeds maximum allowed days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range exceeds maximum allowed days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Maximum date ranges vary by resource: - - bmi: 1095 days - - fat: 30 days - - weight: 31 days - Data is returned using units based on the Accept-Language header provided. + Maximum date ranges vary by resource type: + - BMI: 1095 days (3 years) + - FAT: 30 days + - WEIGHT: 31 days + + The JSON field name in the response varies based on resource_type: + - BodyResourceType.BMI: "body-bmi" + - BodyResourceType.FAT: "body-fat" + - BodyResourceType.WEIGHT: "body-weight" + + Values are returned as strings representing: + - Weight: kilograms or pounds based on user settings + - Body fat: percentage (e.g., "22.5" means 22.5%) + - BMI: standard BMI value (e.g., "21.3") + + The endpoint returns all available data points within the requested date range, + which may include multiple measurements per day if the user logged them. + Days without measurements will not appear in the results. + + Uniquely in the Fitbit API, this endpoint uses "begin_date" rather than + "start_date" in its URL path (unlike most other Fitbit API endpoints). """ max_days = { BodyResourceType.BMI: MaxRanges.GENERAL, @@ -133,27 +194,34 @@ def get_body_timeseries_by_date_range( def get_bodyfat_timeseries_by_date( self, date: str, period: BodyTimePeriod, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get user's body fat measurements over a period of time by specifying a date and time period. + """Returns body fat percentage data for a specified time period. + + This endpoint retrieves time series data for body fat percentage measurements + over a period ending on the specified date. This provides a convenient way + to track changes in body composition over time. API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/get-bodyfat-timeseries-by-date/ Args: date: The end date in YYYY-MM-DD format or 'today' period: The range for which data will be returned (only up to 1m supported) - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Body fat measurements for the specified period + JSONDict: Body fat percentage time series data for the specified time period Raises: - InvalidDateException: If date format is invalid - ValidationException: If period is not supported + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If period is not supported Note: - Only periods up to 1m are supported - 3m, 6m, 1y, max are not available. - Data is returned using units based on the Accept-Language header provided. + Only periods up to BodyTimePeriod.ONE_MONTH are supported. The periods + BodyTimePeriod.THREE_MONTHS, BodyTimePeriod.SIX_MONTHS, BodyTimePeriod.ONE_YEAR, + and BodyTimePeriod.MAX are not available for body fat data. + + The endpoint will return all available data points within the requested period, + which may include multiple measurements per day if the user logged them. """ if period in ( BodyTimePeriod.THREE_MONTHS, @@ -177,27 +245,37 @@ def get_bodyfat_timeseries_by_date( def get_bodyfat_timeseries_by_date_range( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get user's body fat measurements over a period of time by specifying a date range. + """Retrieves body fat percentage measurements over a specified date range. + + This endpoint returns all body fat percentage logs between the specified start and end dates. + Body fat percentage is a key metric for tracking body composition changes over time. API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/get-bodyfat-timeseries-by-date-range/ Args: start_date: The start date in YYYY-MM-DD format or 'today' end_date: The end date in YYYY-MM-DD format or 'today' - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Body fat measurements for the specified date range + JSONDict: Body fat percentage time series data for the specified date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range exceeds 30 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range exceeds 30 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Maximum range is 30 days - Data is returned using units based on the Accept-Language header provided. + Maximum range is 30 days for body fat percentage data. Requests for longer + periods will result in an InvalidDateRangeException. + + Body fat percentage values are returned as strings representing percentages + (e.g., "22.5" means 22.5% body fat). + + The endpoint returns all available data points within the requested range, + which may include multiple measurements per day if the user logged them. + Days without measurements will not appear in the results. """ result = self._make_request( f"body/log/fat/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug @@ -208,27 +286,39 @@ def get_bodyfat_timeseries_by_date_range( def get_weight_timeseries_by_date( self, date: str, period: BodyTimePeriod, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get user's weight measurements over a period of time by specifying a date and time period. + """Retrieves weight measurements over a period ending on the specified date. + + This endpoint returns weight logs over a time period ending on the specified date. + Weight data is presented in the user's preferred unit system (kilograms or pounds). API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/get-weight-timeseries-by-date/ Args: date: The end date in YYYY-MM-DD format or 'today' - period: The range for which data will be returned (only up to 1m supported) - user_id: Optional user ID, defaults to current user + period: The range for which data will be returned (only up to ONE_MONTH supported) + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Weight measurements for the specified period + JSONDict: Weight time series data for the specified time period Raises: - InvalidDateException: If date format is invalid - ValidationException: If period is not supported + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If period is not supported + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Only periods up to 1m are supported - 3m, 6m, 1y, max are not available. - Data is returned using units based on the Accept-Language header provided. + Only periods up to BodyTimePeriod.ONE_MONTH are supported. The periods + BodyTimePeriod.THREE_MONTHS, BodyTimePeriod.SIX_MONTHS, BodyTimePeriod.ONE_YEAR, + and BodyTimePeriod.MAX are not available for weight data. + + Weight values are returned as strings representing either: + - Kilograms for users with metric settings + - Pounds for users with imperial settings + + The unit system is determined by the user's account settings and + can also be influenced by the Accept-Language header provided + during API calls. """ if period in ( BodyTimePeriod.THREE_MONTHS, @@ -252,27 +342,41 @@ def get_weight_timeseries_by_date( def get_weight_timeseries_by_date_range( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get user's weight measurements over a period of time by specifying a date range. + """Retrieves weight measurements over a specified date range. + + This endpoint returns all weight logs between the specified start and end dates. + Weight data is presented in the user's preferred unit system (kilograms or pounds). API Reference: https://dev.fitbit.com/build/reference/web-api/body-timeseries/get-weight-timeseries-by-date-range/ Args: start_date: The start date in YYYY-MM-DD format or 'today' end_date: The end date in YYYY-MM-DD format or 'today' - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Weight measurements for the specified date range + JSONDict: Weight time series data for the specified date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range exceeds 31 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range exceeds 31 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Maximum range is 31 days - Data is returned using units based on the Accept-Language header provided. + Maximum range is 31 days for weight data. Requests for longer periods + will result in an InvalidDateRangeException. + + Weight values are returned as strings representing either: + - Kilograms for users with metric settings + - Pounds for users with imperial settings + + The endpoint returns all available data points within the requested range, + which may include multiple measurements per day if the user logged them. + Days without measurements will not appear in the results. + + To retrieve weight data for longer historical periods, you can make multiple + requests with different date ranges and combine the results. """ result = self._make_request( f"body/log/weight/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/breathing_rate.py b/fitbit_client/resources/breathing_rate.py index 0dc947e..d4ce868 100644 --- a/fitbit_client/resources/breathing_rate.py +++ b/fitbit_client/resources/breathing_rate.py @@ -12,54 +12,58 @@ class BreathingRateResource(BaseResource): - """ - Handles Fitbit Breathing Rate API endpoints for retrieving breathing rate measurements. - - Breathing Rate (also called Respiratory Rate) provides measurements of average breaths - per minute during sleep. Data is collected during the user's "main sleep" period - - the longest single period of sleep on a given date. + """Provides access to Fitbit Breathing Rate API for retrieving respiratory measurements. - Important Notes: - - Data is only collected during sleep periods of at least 3 hours - - Data is only processed when the user is still - - Data becomes available ~15 minutes after device sync - - Data is tied to the "main sleep" period (longest sleep period) - - Sleep periods may span across midnight, so data might reflect previous day's sleep + This resource handles endpoints for retrieving breathing rate (respiratory rate) data, + which measures the average breaths per minute during sleep. The API provides both daily + summaries and interval-based historical data. API Reference: https://dev.fitbit.com/build/reference/web-api/breathing-rate/ + + Required Scopes: respiratory_rate + + Note: + Data is collected during the user's "main sleep" period (longest sleep period) and + requires specific conditions: + - Sleep periods must be at least 3 hours long + - User must be relatively still during measurement + - Data becomes available approximately 15 minutes after device sync + - Sleep periods may span across midnight, so data might reflect previous day's sleep + - Not all Fitbit devices support breathing rate measurement """ @validate_date_param() def get_breathing_rate_summary_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get breathing rate data for a single date. + """Returns breathing rate data summary for a single date. API Reference: https://dev.fitbit.com/build/reference/web-api/breathing-rate/get-br-summary-by-date/ Args: date: Date in YYYY-MM-DD format or 'today' - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing breathing rate data + JSONDict: Breathing rate data containing average breaths per minute during sleep Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid Note: Data is collected during the user's main sleep period (longest sleep period). The measurement may reflect sleep that began the previous day. - For example, requesting data for 2021-12-22 may include measurements - from sleep that started on 2021-12-21. - - Additional requirements for breathing rate data: - - At least 3 hours of sleep - - User must be relatively still - - Device must be synced - - ~15 minute processing time after sync + For example, requesting data for 2023-01-01 may include measurements + from sleep that started on 2022-12-31. + + Not all fields may be present in the response, depending on the + Fitbit device model and quality of sleep data captured. + + Breathing rate data requires: + - Compatible Fitbit device that supports this measurement + - Sleep period at least 3 hours long + - Relatively still sleep (excessive movement reduces accuracy) """ result = self._make_request(f"br/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -68,34 +72,36 @@ def get_breathing_rate_summary_by_date( def get_breathing_rate_summary_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get breathing rate data for a date range. + """Returns breathing rate data for a specified date range. API Reference: https://dev.fitbit.com/build/reference/web-api/breathing-rate/get-br-summary-by-interval/ Args: start_date: Start date in YYYY-MM-DD format or 'today' end_date: End date in YYYY-MM-DD format or 'today' - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing breathing rate data for the date range + JSONDict: Breathing rate data for each day in the specified date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range exceeds 30 days or start_date is after end_date + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range exceeds 30 days + or start_date is after end_date Note: Maximum date range is 30 days. - Data is collected during each day's main sleep period (longest sleep period). - The measurement may reflect sleep that began the previous day. + Data for each day is collected during that day's main sleep period (longest sleep). + Measurements may reflect sleep that began on the previous day. + + Days without valid breathing rate data (insufficient sleep, device not worn, etc.) + will not appear in the results array. - Additional requirements for breathing rate data: - - At least 3 hours of sleep - - User must be relatively still - - Device must be synced - - ~15 minute processing time after sync + Breathing rate values are typically in the range of 12-20 breaths per minute during + sleep, with deep sleep typically showing slightly lower rates than REM sleep. + The 'lowBreathingRateDisturbances' field counts instances where the breathing rate + dropped significantly below the user's normal range during sleep. """ result = self._make_request( f"br/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/cardio_fitness_score.py b/fitbit_client/resources/cardio_fitness_score.py index d6f3ea8..842f869 100644 --- a/fitbit_client/resources/cardio_fitness_score.py +++ b/fitbit_client/resources/cardio_fitness_score.py @@ -12,44 +12,58 @@ class CardioFitnessScoreResource(BaseResource): - """ - Handles Fitbit Cardio Fitness (VO2 Max) API endpoints for accessing cardio fitness scores. + """Provides access to Fitbit Cardio Fitness Score (VO2 Max) API for cardiovascular fitness data. - Cardio Fitness Score (VO2 Max) represents the maximum rate at which the heart, lungs, - and muscles can effectively use oxygen during exercise. The score is determined by - resting heart rate, age, gender, weight, and other personal information. + This resource handles endpoints for retrieving cardio fitness scores (VO2 Max), which + represent the maximum rate at which the heart, lungs, and muscles can effectively use + oxygen during exercise. Higher values generally indicate better cardiovascular fitness. - Values are always returned in ml/kg/min regardless of the units header. Values may be - returned either as a range (if no run data is available) or as a single numeric value - (if the user uses GPS for runs). + Fitbit estimates VO2 Max based on resting heart rate, exercise heart rate response, + age, gender, weight, and (when available) GPS-tracked runs. The API provides access + to both single-day and multi-day data. API Reference: https://dev.fitbit.com/build/reference/web-api/cardio-fitness-score/ + + Required Scopes: + - cardio_fitness (for all cardio fitness endpoints) + + Note: + - Values are always returned in ml/kg/min regardless of the user's unit preferences + - Values may be returned either as a range (if no run data is available) + or as a single numeric value (if the user uses GPS for runs) + - For most users, cardio fitness scores typically range from 30-60 ml/kg/min + - Not all Fitbit devices support cardio fitness score measurements + - Scores may change throughout the day based on activity levels, heart rate, + weight changes, and other factors + - Data becomes available approximately 15 minutes after device sync """ @validate_date_param() def get_vo2_max_summary_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get cardio fitness (VO2 Max) data for a single date. + """Returns cardio fitness (VO2 Max) data for a single date. API Reference: https://dev.fitbit.com/build/reference/web-api/cardio-fitness-score/get-vo2max-summary-by-date/ Args: date: Date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - VO2 max data for the specified date + JSONDict: VO2 max data for the specified date (either a precise value or a range) Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid Note: - Values may change throughout the day based on activity levels, heart rate, - weight changes and other factors. The response always uses ml/kg/min units - regardless of the Accept-Language header. + - Values are always reported in ml/kg/min units + - If the user has GPS run data, a single "vo2Max" value is returned + - If no GPS run data is available, a "vo2MaxRange" with "low" and "high" values is returned + - A missing date in the response means no cardio score was calculated that day + - Scores may change throughout the day based on activity levels and heart rate + - Higher values (typically 40-60 ml/kg/min) indicate better cardiovascular fitness """ result = self._make_request(f"cardioscore/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -58,29 +72,33 @@ def get_vo2_max_summary_by_date( def get_vo2_max_summary_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get cardio fitness (VO2 Max) data for a date range. + """Returns cardio fitness (VO2 Max) data for a specified date range. API Reference: https://dev.fitbit.com/build/reference/web-api/cardio-fitness-score/get-vo2max-summary-by-interval/ Args: start_date: Start date in YYYY-MM-DD format or "today" end_date: End date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - VO2 max data for each date in the range + JSONDict: VO2 max data for each date in the specified range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If date range exceeds 30 days or start_date is after end_date + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range exceeds 30 days + or start_date is after end_date Note: - Maximum date range is 30 days. - Values may change throughout the day based on activity levels, heart rate, - weight changes and other factors. The response always uses ml/kg/min units - regardless of the Accept-Language header. + - Maximum date range is 30 days + - Values are always reported in ml/kg/min units + - Response may include a mix of exact values and ranges, depending on available data + - Days without a cardio fitness score will not appear in the results + - Each day's entry may contain either: + * "vo2Max": A precise measurement (if GPS run data is available) + * "vo2MaxRange": A range with "low" and "high" values (if no GPS data) + - The data is particularly useful for tracking cardiovascular fitness changes over time """ result = self._make_request( f"cardioscore/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/constants.py b/fitbit_client/resources/constants.py index 5b5a287..ad843bc 100644 --- a/fitbit_client/resources/constants.py +++ b/fitbit_client/resources/constants.py @@ -5,11 +5,17 @@ class Period(str, Enum): - """ - Time periods for Fitbit API endpoints. + """Standard time periods for requesting data from Fitbit API endpoints. + + These period values are used across multiple API endpoints to specify the + timeframe for retrieving data. Each value represents a specific duration + ending on the date specified in the request. - Different resources may support different subsets of these periods. - Check individual resource documentation for supported values. + Note: + Different resources support different subsets of these periods. + Always check individual resource documentation for supported values. + Some resources (like body fat and weight) only support shorter periods, + while others (like activity and sleep) may support longer periods. """ ONE_DAY = "1d" # One day @@ -35,7 +41,16 @@ class ActivityGoalType(str, Enum): class MaxRanges(int, Enum): - """Maximum date ranges for various resources (in days)""" + """Maximum date ranges (in days) allowed for various Fitbit API resources. + + These values define the maximum number of days that can be requested in a single + API call when using date range endpoints. Exceeding these limits will result in + a ValidationException from the API. + + Note: + When requesting data over periods longer than these limits, you must make + multiple API calls with smaller date ranges and combine the results. + """ BREATHING_RATE = 30 BODY_FAT = 30 diff --git a/fitbit_client/resources/device.py b/fitbit_client/resources/device.py index 02eef96..ed3ec73 100644 --- a/fitbit_client/resources/device.py +++ b/fitbit_client/resources/device.py @@ -13,15 +13,22 @@ class DeviceResource(BaseResource): - """ - Handles Fitbit Device API endpoints for managing devices and their alarms. + """Provides access to Fitbit Device API for managing paired devices and alarms. - Note: - Alarm endpoints are only supported for older devices that configure alarms via - the mobile application. Newer devices with on-device alarm applications do not - support these endpoints. + This resource handles endpoints for retrieving information about devices paired to + a user's account, as well as creating, updating, and deleting device alarms. + The API provides device details such as battery level, version, features, sync status, + and more. API Reference: https://dev.fitbit.com/build/reference/web-api/devices/ + + Required Scopes: settings + + Note: + Alarm endpoints (create, update, delete) are only supported for older Fitbit devices + that configure alarms via the mobile application. Newer devices with on-device alarm + applications do not support these endpoints. Currently, only the get_devices method + is fully implemented. """ def create_alarm( @@ -33,36 +40,116 @@ def create_alarm( week_days: List[WeekDay], user_id: str = "-", ) -> JSONDict: - """ - NOT IMPLEMENTED. Create an alarm for a device. + """NOT IMPLEMENTED. Creates an alarm on a paired Fitbit device. + + This endpoint would allow creation of device alarms with various settings including + time, recurrence schedule, and enabled status. + + API Reference: https://dev.fitbit.com/build/reference/web-api/devices/add-alarm/ + + Args: + tracker_id: The ID of the tracker to add the alarm to (from get_devices) + time: Alarm time in HH:MM+XX:XX format (e.g. "07:00+00:00") + enabled: Whether the alarm is enabled (True) or disabled (False) + recurring: Whether the alarm repeats (True) or is a single event (False) + week_days: List of WeekDay enum values indicating which days the alarm repeats + user_id: Optional user ID, defaults to current user ("-") + + Returns: + JSONDict: Details of the created alarm + + Raises: + NotImplementedError: This method is not yet implemented + + Note: + This endpoint only works with older Fitbit devices that configure alarms via + the API. Newer devices with on-device alarm applications do not support this + endpoint. This method is provided for API completeness but is not currently + implemented in this client. """ raise NotImplementedError def delete_alarm(self, tracker_id: str, alarm_id: str, user_id: str = "-") -> None: - """ - NOT IMPLEMENTED. Delete an alarm from a device. + """NOT IMPLEMENTED. Deletes an alarm from a paired Fitbit device. + + This endpoint would allow deletion of existing alarms from Fitbit devices + by specifying the tracker ID and alarm ID. + + API Reference: https://dev.fitbit.com/build/reference/web-api/devices/delete-alarm/ + + Args: + tracker_id: The ID of the tracker containing the alarm (from get_devices) + alarm_id: The ID of the alarm to delete (from get_alarms) + user_id: Optional user ID, defaults to current user ("-") + + Returns: + None + + Raises: + NotImplementedError: This method is not yet implemented + + Note: + This endpoint only works with older Fitbit devices that configure alarms via + the API. Newer devices with on-device alarm applications do not support this + endpoint. This method is provided for API completeness but is not currently + implemented in this client. """ raise NotImplementedError def get_alarms(self, tracker_id: str, user_id: str = "-") -> JSONDict: - """ - NOT IMPLEMENTED. Get list of alarms for a device. + """NOT IMPLEMENTED. Retrieves a list of alarms for a paired Fitbit device. + + This endpoint would return all configured alarms for a specific Fitbit device, + including their time settings, enabled status, and recurrence patterns. + + API Reference: https://dev.fitbit.com/build/reference/web-api/devices/get-alarms/ + + Args: + tracker_id: The ID of the tracker to get alarms for (from get_devices) + user_id: Optional user ID, defaults to current user ("-") + + Returns: + JSONDict: Dictionary containing a list of alarms + + Raises: + NotImplementedError: This method is not yet implemented + + Note: + This endpoint only works with older Fitbit devices that configure alarms via + the API. Newer devices with on-device alarm applications do not support this + endpoint. This method is provided for API completeness but is not currently + implemented in this client. """ raise NotImplementedError def get_devices(self, user_id: str = "-", debug: bool = False) -> JSONList: - """ - Get list of Fitbit devices paired to a user's account. + """Returns a list of Fitbit devices paired to a user's account. + + This endpoint provides information about all devices connected to a user's Fitbit + account, including trackers, watches, and scales. The data includes battery status, + device model, features, sync status, and other device-specific information. API Reference: https://dev.fitbit.com/build/reference/web-api/devices/get-devices/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains list of paired devices with their details including: - battery level, device version, features, last sync time, etc. + JSONList: List of Fitbit devices paired to the user's account with details like battery level, model, and features + + Raises: + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The exact fields returned depend on the device type. Newer devices provide + more detailed information than older models. Some devices may return additional + fields not listed here, such as firmware details, hardware versions, or device + capabilities. + + The 'features' array lists device capabilities like heart rate tracking, + GPS, SpO2 monitoring, etc. These can be used to determine what types of + data are available for a particular device. """ return cast(JSONList, self._make_request("devices.json", user_id=user_id, debug=debug)) @@ -80,7 +167,39 @@ def update_alarm( vibe: Optional[str] = None, user_id: str = "-", ) -> JSONDict: - """ - NOT IMPLEMENTED. Update an existing alarm on a device. + """NOT IMPLEMENTED. Updates an existing alarm on a paired Fitbit device. + + This endpoint would allow modification of alarm settings including time, + recurrence pattern, snooze settings, and labels. + + API Reference: https://dev.fitbit.com/build/reference/web-api/devices/update-alarm/ + + Args: + tracker_id: The ID of the tracker containing the alarm (from get_devices) + alarm_id: The ID of the alarm to update (from get_alarms) + time: Alarm time in HH:MM+XX:XX format (e.g. "07:00+00:00") + enabled: Whether the alarm is enabled (True) or disabled (False) + recurring: Whether the alarm repeats (True) or is a single event (False) + week_days: List of WeekDay enum values indicating which days the alarm repeats + snooze_length: Length of snooze in minutes + snooze_count: Number of times the alarm can be snoozed + label: Optional label for the alarm + vibe: Optional vibration pattern + user_id: Optional user ID, defaults to current user ("-") + + Returns: + JSONDict: Details of the updated alarm + + Raises: + NotImplementedError: This method is not yet implemented + + Note: + This endpoint only works with older Fitbit devices that configure alarms via + the API. Newer devices with on-device alarm applications do not support this + endpoint. This method is provided for API completeness but is not currently + implemented in this client. + + The available vibration patterns (vibe parameter) and supported snooze settings + vary by device model. """ raise NotImplementedError diff --git a/fitbit_client/resources/electrocardiogram.py b/fitbit_client/resources/electrocardiogram.py index cf5841d..dc058d3 100644 --- a/fitbit_client/resources/electrocardiogram.py +++ b/fitbit_client/resources/electrocardiogram.py @@ -15,14 +15,33 @@ class ElectrocardiogramResource(BaseResource): - """ - Handles Fitbit Electrocardiogram (ECG) API endpoints for retrieving ECG readings. + """Provides access to Fitbit Electrocardiogram (ECG) API for retrieving heart rhythm assessments. + + This resource handles endpoints for retrieving ECG readings taken using compatible Fitbit devices. + ECG (electrocardiogram) readings can help detect signs of atrial fibrillation (AFib), + which is an irregular heart rhythm that requires medical attention. + + API Reference: https://dev.fitbit.com/build/reference/web-api/electrocardiogram/ + + Required Scopes: + - electrocardiogram (for all ECG endpoints) Note: The ECG API is for research use or investigational use only, and is not - intended for clinical or diagnostic purposes. + intended for clinical or diagnostic purposes. ECG results do not replace + traditional diagnosis methods and should not be interpreted as medical advice. - API Reference: https://dev.fitbit.com/build/reference/web-api/electrocardiogram/ + ECG readings require a compatible Fitbit device (e.g., Fitbit Sense or newer) + and proper finger placement during measurement. The measurement process takes + approximately 30 seconds. + + ECG results are classified into several categories: + - Normal sinus rhythm: No signs of atrial fibrillation detected + - Atrial fibrillation: Irregular rhythm that may indicate AFib + - Inconclusive: Result could not be classified + - Inconclusive (High heart rate): Heart rate was too high for assessment + - Inconclusive (Low heart rate): Heart rate was too low for assessment + - Inconclusive (Poor reading): Signal quality was insufficient for assessment """ @validate_date_param(field_name="before_date") @@ -38,38 +57,40 @@ def get_ecg_log_list( user_id: str = "-", debug: bool = False, ) -> Dict[str, Any]: - """ - Get a list of user's ECG log entries before or after a given day. + """Returns a list of user's ECG log entries before or after a given day. API Reference: https://dev.fitbit.com/build/reference/web-api/electrocardiogram/get-ecg-log-list/ Args: - before_date: Return entries before this date (YYYY-MM-ddTHH:mm:ss), - only YYYY-MM-dd is required - after_date: Return entries after this date (YYYY-MM-ddTHH:mm:ss), - only YYYY-MM-dd is required - sort: Sort order - use 'asc' with after_date, 'desc' with before_date - limit: Number of entries to return (max 10) - offset: Only 0 is supported - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) - - Note: - Either before_date or after_date must be specified. - The offset parameter only supports 0 and using other values may break your application. - Use the pagination links in the response to iterate through results. + before_date: Return entries before this date (YYYY-MM-DD or 'today'). + You can optionally include time in ISO 8601 format (YYYY-MM-DDThh:mm:ss). + after_date: Return entries after this date (YYYY-MM-DD or 'today'). + You can optionally include time in ISO 8601 format (YYYY-MM-DDThh:mm:ss). + sort: Sort order - must use SortDirection.ASCENDING with after_date and + SortDirection.DESCENDING with before_date (default: DESCENDING) + limit: Number of entries to return (max 10, default: 10) + offset: Pagination offset (only 0 is supported by the Fitbit API) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response contains ECG readings and pagination information. Each reading includes: - start time, average heart rate, waveform samples, result classification, etc. + JSONDict: ECG readings with classifications and pagination information Raises: - PaginatonError: If neither before_date nor after_date is specified - PaginatonError: If offset is not 0 - PaginatonError: If limit exceeds 10 - PaginatonError: If sort is not 'asc' or 'desc' - PaginatonError: If sort direction doesn't match date parameter - InvalidDateException: If date format is invalid + fitbit_client.exceptions.PaginationException: If neither before_date nor after_date is specified + fitbit_client.exceptions.PaginationException: If offset is not 0 + fitbit_client.exceptions.PaginationException: If limit exceeds 10 + fitbit_client.exceptions.PaginationException: If sort direction doesn't match date parameter + (must use ASCENDING with after_date, DESCENDING with before_date) + fitbit_client.exceptions.InvalidDateException: If date format is invalid + + Note: + - Either before_date or after_date must be specified, but not both + - The offset parameter only supports 0; use the "next" URL in the pagination response + to iterate through results + - waveformSamples contains the actual ECG data points (300 samples per second) + - resultClassification indicates the assessment outcome (normal, afib, inconclusive) + - For research purposes only, not for clinical or diagnostic use """ params = {"sort": sort.value, "limit": limit, "offset": offset} diff --git a/fitbit_client/resources/friends.py b/fitbit_client/resources/friends.py index 1f7cd13..d144756 100644 --- a/fitbit_client/resources/friends.py +++ b/fitbit_client/resources/friends.py @@ -9,44 +9,55 @@ class FriendsResource(BaseResource): - """ - Handles Fitbit Friends API endpoints for retrieving a user's friends list and leaderboard data. + """Provides access to Fitbit Friends API for retrieving social connections and leaderboards. + + This resource handles endpoints for accessing a user's friends list and leaderboard data, + which shows step count rankings among connected users. The Friends API allows applications + to display social features like friend lists and competitive step count rankings. API Reference: https://dev.fitbit.com/build/reference/web-api/friends/ + Required Scopes: + - social: Required for all endpoints in this resource + Note: - This resource requires the 'social' scope. - The Fitbit privacy setting 'My Friends' (Private, Friends Only or Public) determines - the access to a user's list of friends. - This scope does not provide access to friends' Fitbit data - those users need to - individually consent to share their data. + The Fitbit privacy setting 'My Friends' (Private, Friends Only, or Public) determines + the access to a user's list of friends. Similarly, the 'Average Daily Step Count' + privacy setting affects whether a user appears on leaderboards. + + This scope does not provide access to friends' Fitbit activity data - those users need to + individually consent to share their data with your application. + + This resource uses API version 1.1 instead of the standard version 1. """ API_VERSION: str = "1.1" def get_friends(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves a list of the specified Fitbit user's friends. + """Returns a list of the specified Fitbit user's friends. + + This endpoint retrieves all social connections (friends) for a Fitbit user. It returns + basic profile information for each friend, including their display name and profile picture. API Reference: https://dev.fitbit.com/build/reference/web-api/friends/get-friends/ Args: - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - A list of friend objects containing: - - type: Type of the data ("person") - - id: Fitbit user id - - attributes: - - avatar: Link to user's avatar picture - - child: Boolean indicating if friend is a child account - - friend: Boolean indicating if user is a friend - - name: Person's display name + JSONDict: List of the user's Fitbit friends with basic profile information + + Raises: + fitbit_client.exceptions.AuthorizationException: If the required scope is not granted + fitbit_client.exceptions.ForbiddenException: If user's privacy settings restrict access Note: The user's privacy settings ('My Friends') determine whether this data is accessible. Access may be restricted based on whether the setting is Private, Friends Only, or Public. + + This endpoint uses API version 1.1, which has a different response format compared to + most other Fitbit API endpoints. """ result = self._make_request( "friends.json", user_id=user_id, api_version=FriendsResource.API_VERSION, debug=debug @@ -54,34 +65,35 @@ def get_friends(self, user_id: str = "-", debug: bool = False) -> JSONDict: return cast(JSONDict, result) def get_friends_leaderboard(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves the user's friends leaderboard showing step counts for the last 7 days. + """Returns the user's friends leaderboard showing step counts for the last 7 days. + + This endpoint retrieves a ranked list of the user and their friends based on step counts + over the past 7 days (previous 6 days plus current day in real time). This can be used + to display competitive step count rankings among connected users. API Reference: https://dev.fitbit.com/build/reference/web-api/friends/get-friends-leaderboard/ Args: - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - A dictionary containing leaderboard data with: - - data: List of ranked users including: - - type: User type ('ranked-user' or 'inactive-user') - - id: Fitbit user id - - attributes: - - step-rank: Ranking among friends - - step-summary: Weekly step count - - included: List of person details including: - - avatar: Profile picture URL - - child: Boolean for child account status - - friend: Boolean (always true) - - name: Display name + JSONDict: Ranked list of the user and their friends based on step counts over the past 7 days + + Raises: + fitbit_client.exceptions.AuthorizationException: If the required scope is not granted + fitbit_client.exceptions.ForbiddenException: If user's privacy settings restrict access Note: - - Includes data for the previous 6 days plus current day in real time - - Authorized user (self) is included in the response - - The 'Average Daily Step Count' privacy setting affects leaderboard inclusion - - Both active (ranked-user) and inactive (inactive-user) friends are included + - The leaderboard includes data for the previous 6 days plus the current day in real time + - The authorized user (self) is included in the response + - The 'Average Daily Step Count' privacy setting affects whether users appear on leaderboards + - Both active ('ranked-user') and inactive ('inactive-user') friends are included + - Inactive users have no step-rank or step-summary values + - The 'included' section provides profile information for all users in the 'data' section + + This endpoint uses API version 1.1, which has a different response format compared to + most other Fitbit API endpoints. """ result = self._make_request( "leaderboard/friends.json", diff --git a/fitbit_client/resources/heartrate_timeseries.py b/fitbit_client/resources/heartrate_timeseries.py index 8db4cea..d7192f2 100644 --- a/fitbit_client/resources/heartrate_timeseries.py +++ b/fitbit_client/resources/heartrate_timeseries.py @@ -13,16 +13,21 @@ class HeartrateTimeSeriesResource(BaseResource): - """ - Handles Fitbit Heart Rate Time Series API endpoints for retrieving time-series heart rate data. + """Provides access to Fitbit Heart Rate Time Series API for retrieving heart rate data. + + This resource handles endpoints for retrieving daily heart rate summaries including + heart rate zones, resting heart rate, and time spent in each zone. It provides data + for specific dates or date ranges. API Reference: https://dev.fitbit.com/build/reference/web-api/heartrate-timeseries/ + Required Scopes: heartrate + Note: - This resource requires the 'heartrate' scope. - Data is limited to the user's join date or first log entry date. - Responses include daily summary values including heart rate zones and resting heart rate. - For intraday resolution, see the IntradayResource class. + Data availability is limited to the user's join date or first log entry date. + Responses include daily summary values but not minute-by-minute data. + For intraday (minute-level) heart rate data, use the IntradayResource class. + This resource requires a heart rate capable Fitbit device. """ @validate_date_param(field_name="date") @@ -34,35 +39,48 @@ def get_heartrate_timeseries_by_date( timezone: Optional[str] = None, debug: bool = False, ) -> JSONDict: - """ - Retrieves heart rate time series data for a period starting from the specified date. + """Returns heart rate time series data for a period ending on the specified date. + + This endpoint retrieves daily heart rate summaries for a specified time period ending + on the given date. The data includes resting heart rate and time spent in different + heart rate zones for each day in the period. API Reference: https://dev.fitbit.com/build/reference/web-api/heartrate-timeseries/get-heartrate-timeseries-by-date/ Args: - date: The end date in yyyy-MM-dd format or 'today' - period: Time period to retrieve data for. - Supported values: 1d, 7d, 30d, 1w, 1m - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. + date: The end date in YYYY-MM-DD format or 'today' + period: Time period to retrieve data for (must be one of: Period.ONE_DAY, + Period.SEVEN_DAYS, Period.THIRTY_DAYS, Period.ONE_WEEK, Period.ONE_MONTH) + user_id: Optional user ID, defaults to current user ("-") timezone: Optional timezone (only 'UTC' supported) - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Heart rate data including: - - Date/time of measurement - - Custom heart rate zones with calories, ranges, and minutes - - Standard heart rate zones (Out of Range, Fat Burn, Cardio, Peak) - - Resting heart rate if available + JSONDict: Heart rate data for each day in the period, including heart rate zones and resting heart rate Raises: - ValueError: If period is not supported - ValueError: If timezone is not 'UTC' - InvalidDateException: If date format is invalid + ValueError: If period is not one of the supported period values + ValueError: If timezone is provided and not 'UTC' + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If the required scope is not granted Note: Resting heart rate is calculated from measurements throughout the day, prioritizing sleep periods. If insufficient data exists for a day, - resting heart rate may not be available. + the restingHeartRate field may be missing from that day's data. + + Each heart rate zone contains: + - name: Zone name (Out of Range, Fat Burn, Cardio, Peak) + - min/max: The heart rate boundaries for this zone in beats per minute + - minutes: Total time spent in this zone in minutes + - caloriesOut: Estimated calories burned while in this zone + + The standard zones are calculated based on the user's profile data (age, gender, etc.) + and represent different exercise intensities: + - Out of Range: Below 50% of max heart rate + - Fat Burn: 50-69% of max heart rate + - Cardio: 70-84% of max heart rate + - Peak: 85-100% of max heart rate """ supported_periods = { Period.ONE_DAY, @@ -98,35 +116,46 @@ def get_heartrate_timeseries_by_date_range( timezone: Optional[str] = None, debug: bool = False, ) -> JSONDict: - """ - Retrieves heart rate time series data for a specified date range. + """Returns heart rate time series data for a specified date range. + + This endpoint retrieves daily heart rate summaries for each day in the specified date range. + The data includes resting heart rate and time spent in different heart rate zones for each + day in the range. API Reference: https://dev.fitbit.com/build/reference/web-api/heartrate-timeseries/get-heartrate-timeseries-by-date-range/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") timezone: Optional timezone (only 'UTC' supported) - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Heart rate data including: - - Date/time of measurement - - Custom heart rate zones with calories, ranges, and minutes - - Standard heart rate zones (Out of Range, Fat Burn, Cardio, Peak) - - Resting heart rate if available + JSONDict: Heart rate data for each day in the date range, including heart rate zones and resting heart rate Raises: - ValueError: If timezone is not 'UTC' - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date + ValueError: If timezone is provided and not 'UTC' + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date or + if the date range exceeds the maximum allowed (1095 days) + fitbit_client.exceptions.AuthorizationException: If the required scope is not granted Note: - Maximum date range is 1 year. + Maximum date range is 1095 days (approximately 3 years). + Resting heart rate is calculated from measurements throughout the day, - prioritizing sleep periods. If insufficient data exists for a day, - resting heart rate may not be available. + prioritizing sleep periods. If insufficient data exists for a particular day, + the restingHeartRate field may be missing from that day's data. + + Each heart rate zone contains: + - name: Zone name (Out of Range, Fat Burn, Cardio, Peak) + - min/max: The heart rate boundaries for this zone in beats per minute + - minutes: Total time spent in this zone in minutes + - caloriesOut: Estimated calories burned while in this zone + + This endpoint returns the same data format as the get_heartrate_timeseries_by_date + method, but allows for more precise control over the date range. """ if timezone is not None and timezone != "UTC": raise ValueError("Only 'UTC' timezone is supported") diff --git a/fitbit_client/resources/heartrate_variability.py b/fitbit_client/resources/heartrate_variability.py index f344b59..d2d3c74 100644 --- a/fitbit_client/resources/heartrate_variability.py +++ b/fitbit_client/resources/heartrate_variability.py @@ -11,52 +11,68 @@ class HeartrateVariabilityResource(BaseResource): - """ - Handles Fitbit Heart Rate Variability (HRV) API endpoints for retrieving daily and interval HRV data. + """Provides access to Fitbit Heart Rate Variability (HRV) API for retrieving daily metrics. + + This resource handles endpoints for retrieving HRV measurements taken during sleep, which + is a key indicator of recovery, stress, and overall autonomic nervous system health. API Reference: https://dev.fitbit.com/build/reference/web-api/heartrate-variability/ + Required Scopes: + - heartrate: Required for all endpoints in this resource + Note: - This resource requires the 'heartrate' scope. - HRV data is collected only during the user's "main sleep" (longest sleep period) and requires: + Heart Rate Variability (HRV) measures the variation in time between consecutive + heartbeats. High HRV generally indicates better cardiovascular fitness, stress + resilience, and recovery capacity. Low HRV may indicate stress, fatigue, or illness. + + HRV is calculated using the RMSSD (Root Mean Square of Successive Differences) + measurement in milliseconds. Typical healthy adult values range from 20-100ms, + with higher values generally indicating better autonomic function. + + HRV data collection requirements: - Enabled Health Metrics tile in mobile app dashboard - Minimum 3 hours of sleep - Creation of sleep stages log during main sleep period - Device compatibility (check Fitbit Product page for supported devices) + - No Premium subscription required Data processing occurs after device sync and takes ~15 minutes to become available. HRV measurements span sleep periods that may begin on the previous day. - Premium subscription is not required for HRV data collection. """ @validate_date_param(field_name="date") def get_hrv_summary_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves HRV summary data for a single date. + """Retrieves HRV summary data for a single date. API Reference: https://dev.fitbit.com/build/reference/web-api/heartrate-variability/get-hrv-summary-by-date/ Args: - date: Date in yyyy-MM-dd format or 'today' + date: Date in YYYY-MM-DD format or 'today' user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - HRV data including: - - dateTime: Sleep log date - - value: - - dailyRmssd: Daily heart rate variability (RMSSD) in milliseconds - - deepRmssd: Deep sleep heart rate variability (RMSSD) in milliseconds + JSONDict: HRV data containing daily and deep sleep RMSSD measurements for the requested date Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: Data reflects the main sleep period, which may have started the previous day. - A 200 status code indicates successful execution, even if no data exists. - For reliable data collection, users should remain still during sleep measurement. + The response may be empty if no HRV data was collected for the requested date. + + Two RMSSD values are provided: + - dailyRmssd: Calculated across all sleep stages during the main sleep session + - deepRmssd: Calculated only during deep sleep, which typically shows lower + HRV values due to increased parasympathetic nervous system dominance + + For reliable data collection, users should wear their device properly and + remain still during sleep measurement. Environmental factors like alcohol, + caffeine, or stress can affect HRV measurements. """ result = self._make_request(f"hrv/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -65,33 +81,40 @@ def get_hrv_summary_by_date( def get_hrv_summary_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves HRV summary data for a date range. + """Retrieves HRV summary data for a date range. API Reference: https://dev.fitbit.com/build/reference/web-api/heartrate-variability/get-hrv-summary-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - HRV data for each date including: - - dateTime: Sleep log date - - value: - - dailyRmssd: Daily heart rate variability (RMSSD) in milliseconds - - deepRmssd: Deep sleep heart rate variability (RMSSD) in milliseconds + JSONDict: HRV data containing daily and deep sleep RMSSD measurements for multiple dates in the requested range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Maximum date range is 30 days. - Data reflects main sleep periods, which may have started the day before each date. - A 200 status code indicates successful execution, even if no data exists. - Since HRV data requires sleep, consider querying once or twice daily (e.g., noon and midnight). + Maximum date range is 30 days. Requests exceeding this limit will return an error. + + The response includes entries only for dates where HRV data was collected. + Dates without data will not appear in the results array. + + HRV data is calculated from sleep sessions, which may have started on the + previous day. The dateTime field represents the date the sleep session + ended, not when it began. + + Tracking HRV over time provides insights into: + - Recovery status and adaptation to training + - Potential early warning signs of overtraining or illness + - Effects of lifestyle changes on autonomic nervous system function + + For optimal trend analysis, collect data consistently at the same time each day. """ result = self._make_request( f"hrv/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/intraday.py b/fitbit_client/resources/intraday.py index 20b60fe..86a7fd5 100644 --- a/fitbit_client/resources/intraday.py +++ b/fitbit_client/resources/intraday.py @@ -16,13 +16,32 @@ class IntradayResource(BaseResource): - """ - Access to Fitbit Intraday API endpoints for detailed within-day data. + """Provides access to Fitbit Intraday API for retrieving detailed within-day time series data. - OAuth 2.0 Application Type must be set to "Personal" to use Intraday data. - All other application types require special approval from Fitbit. + This resource handles endpoints for retrieving minute-by-minute or second-by-second data + for various metrics including activity (steps, calories), heart rate, SpO2, breathing rate, + heart rate variability (HRV), and active zone minutes. The intraday data provides much more + granular insights than the daily summary endpoints. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/ + + Required Scopes: + - activity: For activity-related intraday data + - heartrate: For heart rate intraday data + - respiratory_rate: For breathing rate intraday data + - oxygen_saturation: For SpO2 intraday data + - cardio_fitness: For heart rate variability intraday data + + Note: + OAuth 2.0 Application Type must be set to "Personal" to use intraday data. + All other application types require special approval from Fitbit. + + Intraday data is much more detailed than daily summaries and has strict + limitations on date ranges (usually 24 hours or 30 days maximum). + + Different metrics support different granularity levels. For example, + heart rate data is available at 1-second or 1-minute intervals, while + activity data is available at 1-minute, 5-minute, or 15-minute intervals. """ @validate_date_param(field_name="date") @@ -35,28 +54,47 @@ def get_azm_intraday_by_date( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves intraday active zone minutes time series data for a single date. + """Retrieves intraday active zone minutes time series data for a single date. - Intraday support can extend the detail-level response to include 1min, 5min and 15min - for Active Zone Minutes. + This endpoint provides minute-by-minute active zone minutes data, showing the + intensity of activity throughout the day. Active Zone Minutes are earned when + in the fat burn, cardio, or peak heart rate zones. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-azm-intraday-by-date/ Args: - date: The date in yyyy-MM-dd format or 'today' - detail_level: Level of detail. Options: 1min, 5min, 15min - start_time: Optional start time in HH:mm format - end_time: Optional end time in HH:mm format - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + date: The date in YYYY-MM-DD format or 'today' + detail_level: Level of detail (ONE_MINUTE, FIVE_MINUTES, or FIFTEEN_MINUTES) + start_time: Optional start time in HH:mm format to limit the time window + end_time: Optional end time in HH:mm format to limit the time window + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday active zone minutes data + JSONDict: Active zone minutes data containing daily summary and intraday time series Raises: - IntradayValidationException: If detail_level is invalid - InvalidDateException: If date format is invalid + fitbit_client.exceptions.IntradayValidationException: If detail_level is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Active Zone Minutes measure time spent in heart rate zones that count toward + your weekly goals. Different detail levels change the granularity of the data: + - ONE_MINUTE (1min): Shows minute-by-minute values + - FIVE_MINUTES (5min): Shows values averaged over 5-minute intervals + - FIFTEEN_MINUTES (15min): Shows values averaged over 15-minute intervals + + The "activities-active-zone-minutes" section contains daily summary data, + while the "intraday" section contains the detailed time-specific data. + + AZM values are categorized by intensity zones: + - Fat Burn: Moderate intensity (1 Active Zone Minute per minute) + - Cardio: High intensity (2 Active Zone Minutes per minute) + - Peak: Very high intensity (2 Active Zone Minutes per minute) + + Personal applications automatically have access to intraday data. + Other application types require special approval from Fitbit. """ valid_levels = [ IntradayDetailLevel.ONE_MINUTE, @@ -91,30 +129,48 @@ def get_azm_intraday_by_interval( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves intraday active zone minutes time series data for a date range. + """Retrieves intraday active zone minutes time series data for a date range. - Intraday support can extend the detail-level response to include 1min, 5min and 15min - for Active Zone Minutes. Maximum date range is 24 hours. + This endpoint provides minute-by-minute active zone minutes data across multiple days, + showing the intensity of activity throughout the specified period. The maximum date + range is limited to 24 hours. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-azm-intraday-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - detail_level: Level of detail. Options: 1min, 5min, 15min - start_time: Optional start time in HH:mm format - end_time: Optional end time in HH:mm format - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + detail_level: Level of detail (ONE_MINUTE, FIVE_MINUTES, or FIFTEEN_MINUTES) + start_time: Optional start time in HH:mm format to limit the time window + end_time: Optional end time in HH:mm format to limit the time window + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday active zone minutes data + JSONDict: Active zone minutes data containing daily summaries and intraday time series Raises: - IntradayValidationException: If detail_level is invalid - InvalidDateException: If date formats are invalid - InvalidDateRangeException: If date range is invalid or exceeds 24 hours + fitbit_client.exceptions.IntradayValidationException: If detail_level is invalid + fitbit_client.exceptions.InvalidDateException: If date formats are invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 24 hours + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Important limitations: + - Maximum date range is 24 hours (1 day), even if start_date and end_date differ by more + - For longer periods, make multiple requests with consecutive date ranges + + The different detail levels change the granularity of the data: + - ONE_MINUTE (1min): Shows minute-by-minute values + - FIVE_MINUTES (5min): Shows values averaged over 5-minute intervals + - FIFTEEN_MINUTES (15min): Shows values averaged over 15-minute intervals + + The time window parameters (start_time/end_time) can be useful to limit the + amount of data returned, especially when you're only interested in activity + during specific hours of the day. + + Personal applications automatically have access to intraday data. + Other application types require special approval from Fitbit. """ valid_levels = [ IntradayDetailLevel.ONE_MINUTE, @@ -149,33 +205,53 @@ def get_activity_intraday_by_date( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves intraday activity time series data for a single date. + """Retrieves intraday activity time series data for a single date. - Intraday support can extend the detail-level response to include 1min, 5min and 15min - for Activity data. + This endpoint provides detailed activity metrics (steps, calories, distance, etc.) + at regular intervals throughout the day, allowing analysis of activity patterns + with much greater precision than daily summaries. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-activity-intraday-by-date/ Args: - date: The date in yyyy-MM-dd format or 'today' - resource_path: The resource type to fetch. Valid options: - calories, distance, elevation, floors, steps, swimming-strokes - detail_level: Level of detail. Options: 1min, 5min, 15min - start_time: Optional start time in HH:mm format - end_time: Optional end time in HH:mm format - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + date: The date in YYYY-MM-DD format or 'today' + resource_path: The activity metric to fetch (e.g., "steps", "calories", "distance") + detail_level: Level of detail (ONE_MINUTE, FIVE_MINUTES, or FIFTEEN_MINUTES) + start_time: Optional start time in HH:mm format to limit the time window + end_time: Optional end time in HH:mm format to limit the time window + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday activity data for the specified resource + JSONDict: Activity data with daily summary and intraday time series for the specified metric Raises: - IntradayValidationException: If detail_level or resource_path is invalid - InvalidDateException: If date format is invalid + fitbit_client.exceptions.IntradayValidationException: If detail_level or resource_path is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Activity intraday data cannot be retrieved for more than a 24 hour period. + Valid resource_path options: + - "calories": Calories burned per interval + - "steps": Step count per interval + - "distance": Distance covered per interval (in miles or kilometers) + - "floors": Floors climbed per interval + - "elevation": Elevation change per interval (in feet or meters) + - "swimming-strokes": Swimming strokes per interval + + Different detail levels change the granularity of the data: + - ONE_MINUTE (1min): Shows minute-by-minute values + - FIVE_MINUTES (5min): Shows values averaged or summed over 5-minute intervals + - FIFTEEN_MINUTES (15min): Shows values averaged or summed over 15-minute intervals + + The response format changes based on the resource_path, with the appropriate + field names ("activities-steps", "activities-calories", etc.), but the + overall structure remains the same. + + Activity units are based on the user's profile settings: + - Imperial: miles, feet + - Metric: kilometers, meters + Personal applications automatically have access to intraday data. Other application types require special approval from Fitbit. """ @@ -227,35 +303,54 @@ def get_activity_intraday_by_interval( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves intraday activity time series data for a date range. + """Retrieves intraday activity time series data for a date range. - Intraday support can extend the detail-level response to include 1min, 5min and 15min - for Activity data. + This endpoint provides detailed activity metrics across multiple days, with the + same level of granularity as the single-date endpoint. The maximum date range + is limited to 24 hours to keep response sizes manageable. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-activity-intraday-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - resource_path: The resource type to fetch. Valid options: - calories, distance, elevation, floors, steps, swimming-strokes - detail_level: Level of detail. Options: 1min, 5min, 15min - start_time: Optional start time in HH:mm format - end_time: Optional end time in HH:mm format - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + resource_path: The activity metric to fetch (e.g., "steps", "calories", "distance") + detail_level: Level of detail (ONE_MINUTE, FIVE_MINUTES, or FIFTEEN_MINUTES) + start_time: Optional start time in HH:mm format to limit the time window + end_time: Optional end time in HH:mm format to limit the time window + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday activity data for the specified resource + JSONDict: Activity data with daily summaries and intraday time series for the specified metric Raises: - IntradayValidationException: If detail_level or resource_path is invalid - InvalidDateException: If date formats are invalid - InvalidDateRangeException: If date range is invalid or exceeds 24 hours + fitbit_client.exceptions.IntradayValidationException: If detail_level or resource_path is invalid + fitbit_client.exceptions.InvalidDateException: If date formats are invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 24 hours + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Activity intraday data cannot be retrieved for more than a 24 hour period. + Important limitations: + - Maximum date range is 24 hours (1 day), even if start_date and end_date differ by more + - For longer periods, make multiple requests with consecutive date ranges + + Valid resource_path options: + - "calories": Calories burned per interval + - "steps": Step count per interval + - "distance": Distance covered per interval (in miles or kilometers) + - "floors": Floors climbed per interval + - "elevation": Elevation change per interval (in feet or meters) + - "swimming-strokes": Swimming strokes per interval + + Different detail levels change the granularity of the data: + - ONE_MINUTE (1min): Shows minute-by-minute values + - FIVE_MINUTES (5min): Shows values averaged or summed over 5-minute intervals + - FIFTEEN_MINUTES (15min): Shows values averaged or summed over 15-minute intervals + + The response format will differ based on the resource_path, but the overall + structure remains the same. + Personal applications automatically have access to intraday data. Other application types require special approval from Fitbit. """ @@ -299,23 +394,44 @@ def get_activity_intraday_by_interval( def get_breathing_rate_intraday_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves intraday breathing rate data for a single date. + """Retrieves intraday breathing rate data for a single date. + + This endpoint returns detailed breathing rate measurements recorded during sleep. + Breathing rate data provides insights into respiratory health, sleep quality, + and potential health issues. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-br-intraday-by-date/ Args: - date: The date in yyyy-MM-dd format or 'today' - start_time: Optional start time in HH:mm format - end_time: Optional end time in HH:mm format - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + date: The date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday breathing rate data + JSONDict: Breathing rate data including summary and detailed measurements during sleep Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Breathing rate data is collected during sleep periods and is measured in + breaths per minute (BPM). Typical adult resting breathing rates range from + 12-20 breaths per minute. + + The data is collected in approximately 15-minute intervals during sleep. + Each measurement includes a confidence level indicating the reliability + of the reading. + + Different sleep stages normally have different breathing rates: + - Deep sleep: Typically slower, more regular breathing + - REM sleep: Variable breathing rate, may be faster or more irregular + + Breathing rate data requires a compatible Fitbit device with the appropriate + sensors and the Health Metrics Dashboard enabled in the Fitbit app. + + This data is associated with the date the sleep ends, even if the sleep + session began on the previous day. """ endpoint = f"br/date/{date}/all.json" return cast(JSONDict, self._make_request(endpoint, user_id=user_id, debug=debug)) @@ -324,24 +440,47 @@ def get_breathing_rate_intraday_by_date( def get_breathing_rate_intraday_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves intraday breathing rate data for a date range. + """Retrieves intraday breathing rate data for a date range. + + This endpoint returns detailed breathing rate measurements recorded during sleep + across multiple days, up to a maximum range of 30 days. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-br-intraday-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday breathing rate data + JSONDict: Breathing rate data including daily summaries and detailed measurements during sleep Raises: - IntradayValidationException: If detail_level is invalid - InvalidDateException: If date formats are invalid - InvalidDateRangeException: If date range is invalid or exceeds 30 days + fitbit_client.exceptions.InvalidDateException: If date formats are invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 30 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The maximum date range for this endpoint is 30 days. For longer historical + periods, you will need to make multiple requests with different date ranges. + + Breathing rate data is collected during sleep periods and is measured in + breaths per minute (BPM). The returned data includes: + - Daily summary values for different sleep stages + - Detailed intraday measurements throughout each sleep session + + Each day's data is associated with the date the sleep ends, even if the sleep + session began on the previous day. + + This endpoint requires the "respiratory_rate" OAuth scope and a compatible + Fitbit device with the appropriate sensors and the Health Metrics Dashboard + enabled in the Fitbit app. + + Analyzing breathing rate trends over time can provide insights into: + - Sleep quality and patterns + - Recovery from exercise or illness + - Potential respiratory issues """ endpoint = f"br/date/{start_date}/{end_date}/all.json" @@ -357,25 +496,42 @@ def get_heartrate_intraday_by_date( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves intraday heart rate time series data for a single date. + """Returns detailed heart rate data at minute or second intervals for a single date. + + This endpoint retrieves heart rate measurements at the specified granularity (detail level) + for a specific date. It can optionally be limited to a specific time window within the day. + This provides much more detailed heart rate data than the daily summary endpoints. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-heartrate-intraday-by-date/ Args: - date: The date in yyyy-MM-dd format or 'today' - detail_level: Level of detail - start_time: Optional start time in HH:mm format - end_time: Optional end time in HH:mm format - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + date: The date in YYYY-MM-DD format or 'today' + detail_level: Level of detail (IntradayDetailLevel.ONE_SECOND or IntradayDetailLevel.ONE_MINUTE) + start_time: Optional start time in HH:mm format to limit the time window + end_time: Optional end time in HH:mm format to limit the time window + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday heart rate data + JSONDict: Heart rate data including daily summary and detailed time series Raises: - IntradayValidationException: If detail_level is invalid - InvalidDateException: If date format is invalid + fitbit_client.exceptions.IntradayValidationException: If detail_level is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The "activities-heart" section contains the same data as the daily heart rate summary. + The "activities-heart-intraday" section contains the detailed, minute-by-minute or + second-by-second heart rate measurements. + + For one-second detail level (1sec), the dataset can be very large, potentially + containing up to 86,400 data points for a full day. For applications handling + large volumes of data, consider using time windows (start_time/end_time) + to limit the response size. + + Personal applications automatically have access to intraday data. + Other application types require special approval from Fitbit. """ if detail_level not in IntradayDetailLevel: raise IntradayValidationException( @@ -401,25 +557,50 @@ def get_heartrate_intraday_by_interval( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves intraday heart rate time series data for a date range. + """Retrieves intraday heart rate time series data for a date range. + + This endpoint provides second-by-second or minute-by-minute heart rate measurements + across multiple days. This allows for detailed analysis of heart rate patterns + and trends over extended periods. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-heartrate-intraday-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - detail_level: Level of detail. Options: 1sec, 1min - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + detail_level: Level of detail (ONE_SECOND or ONE_MINUTE, default: ONE_MINUTE) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday heart rate data + JSONDict: Heart rate data including daily summaries and detailed time series Raises: - IntradayValidationException: If detail_level is invalid - InvalidDateException: If date formats are invalid - InvalidDateRangeException: If date range is invalid + fitbit_client.exceptions.IntradayValidationException: If detail_level is invalid + fitbit_client.exceptions.InvalidDateException: If date formats are invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + This endpoint supports two levels of detail: + - ONE_SECOND (1sec): Heart rate readings every second, providing maximum granularity + - ONE_MINUTE (1min): Heart rate readings every minute, for more manageable data size + + For ONE_SECOND detail level, the response can be extremely large for longer + date ranges, potentially containing up to 86,400 data points per day. Consider + using ONE_MINUTE detail level unless you specifically need second-level detail. + + Unlike most other intraday endpoints, there is no explicit maximum date range + for this endpoint. However, requesting too much data at once can result in + timeouts or very large responses. For best performance, limit requests to + a few days at a time, especially with ONE_SECOND detail level. + + Heart rate data is recorded continuously when a compatible Fitbit device + is worn, with gaps during times when the device is not worn or cannot + get a reliable reading. + + Personal applications automatically have access to intraday data. + Other application types require special approval from Fitbit. """ valid_levels = [IntradayDetailLevel.ONE_SECOND, IntradayDetailLevel.ONE_MINUTE] if detail_level not in valid_levels: @@ -437,32 +618,52 @@ def get_heartrate_intraday_by_interval( def get_hrv_intraday_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves intraday heart rate variability (HRV) data for a single date. + """Retrieves intraday heart rate variability (HRV) data for a single date. + + This endpoint returns detailed heart rate variability measurements taken during sleep. + HRV is a key indicator of autonomic nervous system health, stress levels, and recovery. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-hrv-intraday-by-date/ Args: - date: The date in yyyy-MM-dd format or 'today' - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + date: The date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday HRV data including rmssd, coverage, hf, and lf measurements + JSONDict: Heart rate variability data including daily summary and detailed measurements Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - * HRV data applies specifically to a user's "main sleep" period - * Data is returned every 5 minutes during sleep - * Values usually reflect sleep that began the previous day - * Requires: - - Health Metrics tile enabled in mobile app - - Minimum 3 hours of sleep - - Sleep stages log creation - - Device compatibility (see Fitbit Product page) - * Processing takes ~15 minutes after device sync + HRV data is collected specifically during the user's "main sleep" period + (typically the longest sleep of the day). Key information: + + - RMSSD (Root Mean Square of Successive Differences): The primary HRV + metric measured in milliseconds. Higher values typically indicate better + recovery and lower stress levels. Normal adult ranges vary widely from + approximately 20-100ms. + + - Data is collected in 5-minute intervals during sleep. + + - HF (High Frequency) power: Associated with parasympathetic nervous system + activity (rest and recovery). + + - LF (Low Frequency) power: Influenced by both sympathetic (stress response) + and parasympathetic nervous systems. + + - Coverage: Indicates the quality of the data collection during each interval. + + Requirements for HRV data collection: + - Health Metrics tile enabled in the Fitbit mobile app + - Minimum 3 hours of sleep + - Sleep stages log creation (depends on device having heart rate sensor) + - Compatible Fitbit device + + Data processing takes approximately 15 minutes after device sync. + The date represents when the sleep ended, even if it began on the previous day. """ endpoint = f"hrv/date/{date}/all.json" return cast(JSONDict, self._make_request(endpoint, user_id=user_id, debug=debug)) @@ -471,35 +672,54 @@ def get_hrv_intraday_by_date( def get_hrv_intraday_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves intraday heart rate variability (HRV) data for a date range. + """Retrieves intraday heart rate variability (HRV) data for a date range. + + This endpoint returns detailed heart rate variability measurements taken during + sleep across multiple days, up to a maximum range of 30 days. This is useful for + analyzing trends in recovery and stress levels over time. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-hrv-intraday-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday HRV data including rmssd, coverage, hf, and lf measurements + JSONDict: Heart rate variability data including daily summaries and detailed measurements Raises: - InvalidDateException: If date formats are invalid - InvalidDateRangeException: If date range is invalid or exceeds 30 days + fitbit_client.exceptions.InvalidDateException: If date formats are invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 30 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - * HRV data applies specifically to a user's "main sleep" period - * Data is returned every 5 minutes during sleep - * Values usually reflect sleep that began the previous day - * Maximum date range is 30 days - * Requires: - - Health Metrics tile enabled in mobile app - - Minimum 3 hours of sleep - - Sleep stages log creation - - Device compatibility (see Fitbit Product page) - * Processing takes ~15 minutes after device sync + The maximum date range for this endpoint is 30 days. For longer historical + periods, you will need to make multiple requests with different date ranges. + + HRV data is collected specifically during the user's "main sleep" period + each day. The data includes: + - Daily summary values (dailyRmssd, deepRmssd) + - Detailed 5-minute interval measurements throughout each sleep session + + RMSSD (Root Mean Square of Successive Differences) is measured in milliseconds, + with higher values typically indicating better recovery and lower stress levels. + + Analyzing HRV trends over time can provide insights into: + - Recovery status and adaptation to training + - Stress levels and potential burnout + - Sleep quality + - Overall autonomic nervous system balance + + Requirements for HRV data collection: + - Health Metrics tile enabled in the Fitbit mobile app + - Minimum 3 hours of sleep each night + - Sleep stages log creation (requires heart rate sensor) + - Compatible Fitbit device + + Each day's data is associated with the date the sleep ends, even if the sleep + session began on the previous day. """ endpoint = f"hrv/date/{start_date}/{end_date}/all.json" return cast(JSONDict, self._make_request(endpoint, user_id=user_id, debug=debug)) @@ -508,31 +728,49 @@ def get_hrv_intraday_by_interval( def get_spo2_intraday_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves intraday SpO2 (blood oxygen saturation) data for a single date. + """Retrieves intraday SpO2 (blood oxygen saturation) data for a single date. + + This endpoint returns detailed SpO2 measurements taken during sleep. Blood oxygen + saturation is an important health metric that reflects how well the body is + supplying oxygen to the blood. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-spo2-intraday-by-date/ Args: - date: The date in yyyy-MM-dd format or 'today' - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + date: The date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday SpO2 percentage values with timestamps + JSONDict: SpO2 data including daily summary and detailed measurements Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - * SpO2 data applies specifically to a user's "main sleep" period - * Values calculated on a 5-minute exponentially-moving average - * Values usually reflect sleep that began the previous day - * Requires: - - Minimum 3 hours of quality sleep - - Limited physical movement - - Device compatibility (see Fitbit Product page) - * Processing takes up to 1 hour after device sync + SpO2 (Blood Oxygen Saturation) data is collected during the user's "main sleep" + period (typically the longest sleep of the day). Key information: + + - SpO2 is measured as a percentage, with normal values typically ranging + from 95-100% for healthy individuals at rest. + + - Values below 90% may indicate potential health concerns, though Fitbit + devices are not medical devices and should not be used for diagnosis. + + - Data is calculated using a 5-minute exponentially-moving average to + smooth out short-term fluctuations. + + - Measurements are taken approximately every 5 minutes during sleep. + + Requirements for SpO2 data collection: + - Minimum 3 hours of quality sleep + - Limited physical movement during sleep + - Compatible Fitbit device with SpO2 monitoring capabilities + - SpO2 tracking enabled in device settings + + Data processing can take up to 1 hour after device sync. + The date represents when the sleep ended, even if it began on the previous day. """ endpoint = f"spo2/date/{date}/all.json" return cast(JSONDict, self._make_request(endpoint, user_id=user_id, debug=debug)) @@ -541,34 +779,56 @@ def get_spo2_intraday_by_date( def get_spo2_intraday_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Retrieves intraday SpO2 (blood oxygen saturation) data for a date range. + """Retrieves intraday SpO2 (blood oxygen saturation) data for a date range. + + This endpoint returns detailed SpO2 measurements taken during sleep across + multiple days, up to a maximum range of 30 days. This is useful for + analyzing trends in blood oxygen levels over time. API Reference: https://dev.fitbit.com/build/reference/web-api/intraday/get-spo2-intraday-by-interval/ Args: - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - user_id: User ID, defaults to current user - debug: If True, prints the curl command instead of making the request + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing intraday SpO2 percentage values with timestamps + JSONDict: SpO2 data including daily summaries and detailed measurements Raises: - InvalidDateException: If date formats are invalid - InvalidDateRangeException: If date range is invalid or exceeds 30 days + fitbit_client.exceptions.InvalidDateException: If date formats are invalid + fitbit_client.exceptions.InvalidDateRangeException: If date range is invalid or exceeds 30 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - * SpO2 data applies specifically to a user's "main sleep" period - * Values calculated on a 5-minute exponentially-moving average - * Values usually reflect sleep that began the previous day - * Maximum date range is 30 days - * Requires: - - Minimum 3 hours of quality sleep - - Limited physical movement - - Device compatibility (see Fitbit Product page) - * Processing takes up to 1 hour after device sync + The maximum date range for this endpoint is 30 days. For longer historical + periods, you will need to make multiple requests with different date ranges. + + SpO2 (Blood Oxygen Saturation) data is collected during each day's "main sleep" + period. The data includes: + - Daily summary values (average, minimum, maximum) + - Detailed measurements taken approximately every 5 minutes during sleep + + SpO2 is measured as a percentage, with normal values typically ranging + from 95-100% for healthy individuals at rest. Consistent readings below 95% + might warrant discussion with a healthcare provider, though Fitbit devices + are not medical devices and should not be used for diagnosis. + + Analyzing SpO2 trends over time can provide insights into: + - Sleep quality + - Respiratory health + - Altitude acclimation + - Potential sleep-related breathing disorders + + Requirements for SpO2 data collection: + - Minimum 3 hours of quality sleep each night + - Limited physical movement during sleep + - Compatible Fitbit device with SpO2 monitoring capabilities + - SpO2 tracking enabled in device settings + + Each day's data is associated with the date the sleep ends, even if the sleep + session began on the previous day. """ endpoint = f"spo2/date/{start_date}/{end_date}/all.json" return cast(JSONDict, self._make_request(endpoint, user_id=user_id, debug=debug)) diff --git a/fitbit_client/resources/irregular_rhythm_notifications.py b/fitbit_client/resources/irregular_rhythm_notifications.py index 17f398b..5b072b8 100644 --- a/fitbit_client/resources/irregular_rhythm_notifications.py +++ b/fitbit_client/resources/irregular_rhythm_notifications.py @@ -13,22 +13,30 @@ class IrregularRhythmNotificationsResource(BaseResource): - """ - Handles Fitbit Irregular Rhythm Notifications (IRN) API endpoints for retrieving - notifications and user engagement data. + """Provides access to Fitbit Irregular Rhythm Notifications (IRN) API for heart rhythm monitoring. + + This resource handles endpoints for retrieving Irregular Rhythm Notifications (IRN), + which are alerts sent to users when their device detects signs of atrial fibrillation (AFib). + The API can be used to access notification history and user enrollment status. API Reference: https://dev.fitbit.com/build/reference/web-api/irregular-rhythm-notifications/ - Scope: rregular_rhythm_notifications + Required Scopes: + - irregular_rhythm_notifications (for all IRN endpoints) Important: The IRN API is for research use or investigational use only, and is not intended - for clinical or diagnostic purposes. + for clinical or diagnostic purposes. IRN results do not replace traditional diagnosis + methods and should not be interpreted as medical advice. Note: - Only alerts that have been read by the user in the Fitbit app are accessible. - IRN does not support subscription notifications (webhooks). - Data is available after device sync and user interaction with notifications. + - Only alerts that have been read by the user in the Fitbit app are accessible + - IRN does not support subscription notifications (webhooks) + - Data becomes available after device sync and user interaction with notifications + - IRN requires a compatible Fitbit device with heart rate monitoring capabilities + - Users must complete an on-device enrollment flow to enable the IRN feature + - Notifications are analyzed based on heart rate data collected during sleep + - IRN is not a continuous monitoring system and is not designed to detect heart attacks """ @validate_date_param(field_name="before_date") @@ -44,40 +52,46 @@ def get_irn_alerts_list( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves a paginated list of IRN alerts and their tachograms. + """Returns a paginated list of Irregular Rhythm Notifications (IRN) alerts. + + This endpoint retrieves alerts generated when the user's device detected signs of + possible atrial fibrillation (AFib). Only alerts that have been viewed by the user + in their Fitbit app will be returned. API Reference: https://dev.fitbit.com/build/reference/web-api/irregular-rhythm-notifications/get-irn-alerts-list/ Args: - before_date: Date in yyyy-MM-ddTHH:mm:ss format (at least yyyy-MM-dd required) - after_date: Date in yyyy-MM-ddTHH:mm:ss format (at least yyyy-MM-dd required) - sort: Sort order - use 'asc' with after_date, 'desc' with before_date - limit: Number of entries to return (max 10) - offset: Pagination offset (only 0 is supported) - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) - - Note: - Either before_date or after_date must be specified. - The offset parameter only supports 0 and using other values may break your application. - Use the pagination links in the response to iterate through results. + before_date: Return entries before this date (YYYY-MM-DD or 'today'). + You can optionally include time in ISO 8601 format (YYYY-MM-DDThh:mm:ss). + after_date: Return entries after this date (YYYY-MM-DD or 'today'). + You can optionally include time in ISO 8601 format (YYYY-MM-DDThh:mm:ss). + sort: Sort order - must use SortDirection.ASCENDING with after_date and + SortDirection.DESCENDING with before_date (default: DESCENDING) + limit: Number of entries to return (max 10, default: 10) + offset: Pagination offset (only 0 is supported by the Fitbit API) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dictionary containing: - - alerts: List of IRN alerts with detection times and heart rate data - - pagination: Information for retrieving next/previous pages + JSONDict: Contains IRN alerts and pagination information for the requested period Raises: - PaginatonError: If neither before_date nor after_date is specified - PaginatonError: If offset is not 0 - PaginatonError: If limit exceeds 10 - PaginatonError: If sort is not 'asc' or 'desc' - PaginatonError: If sort direction doesn't match date parameter - InvalidDateException: If date format is invalid + fitbit_client.exceptions.PaginationException: If neither before_date nor after_date is specified + fitbit_client.exceptions.PaginationException: If offset is not 0 + fitbit_client.exceptions.PaginationException: If limit exceeds 10 + fitbit_client.exceptions.PaginationException: If sort direction doesn't match date parameter + (must use ASCENDING with after_date, DESCENDING with before_date) + fitbit_client.exceptions.InvalidDateException: If date format is invalid Note: - Either before_date or after_date must be specified. + - Either before_date or after_date must be specified, but not both + - The offset parameter only supports 0; use the "next" URL in the pagination response + to iterate through results + - Tachogram data represents the time between heartbeats in milliseconds + - The algorithm analyzes heart rate irregularity patterns during sleep + - For research purposes only, not for clinical or diagnostic use + - The alertTime is when the notification was generated, while detectedTime is + when the irregular rhythm was detected (usually during sleep) """ params = {"sort": sort.value, "limit": limit, "offset": offset} @@ -92,20 +106,32 @@ def get_irn_alerts_list( return cast(JSONDict, result) def get_irn_profile(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves the user's IRN feature engagement status. + """Returns the user's Irregular Rhythm Notifications (IRN) feature engagement status. + + This endpoint retrieves information about the user's enrollment status for the + Irregular Rhythm Notifications feature, including whether they've completed the + required onboarding process and when their data was last analyzed. API Reference: https://dev.fitbit.com/build/reference/web-api/irregular-rhythm-notifications/get-irn-profile/ Args: - user_id: The encoded ID of the user. Use "-" (dash) for current logged-in user. - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dictionary containing: - - onboarded: Whether user has completed IRN feature onboarding - - enrolled: Whether user is enrolled for IRN data processing - - lastUpdated: Timestamp of last analyzable data sync + JSONDict: User's IRN feature engagement status including onboarding and enrollment information + + Raises: + fitbit_client.exceptions.AuthorizationException: If missing the irregular_rhythm_notifications scope + fitbit_client.exceptions.InvalidRequestException: If the user is not eligible for IRN + + Note: + - "onboarded": True if the user has completed the IRN feature onboarding process + - "enrolled": True if the user is actively enrolled and receiving notifications + - "lastUpdated": Timestamp of when analyzable data was last synced to Fitbit servers + - Users must complete an on-device onboarding flow to enable IRN + - Enrollment can be paused/resumed by the user in their Fitbit app settings + - Analyzing the data requires a compatible device, sufficient sleep data, and proper wear """ result = self._make_request("irn/profile.json", user_id=user_id, debug=debug) return cast(JSONDict, result) diff --git a/fitbit_client/resources/nutrition.py b/fitbit_client/resources/nutrition.py index 7a2cc14..e98b4c1 100644 --- a/fitbit_client/resources/nutrition.py +++ b/fitbit_client/resources/nutrition.py @@ -24,25 +24,56 @@ class NutritionResource(BaseResource): - """ - Handles Fitbit Nutrition API endpoints for managing food logs, meals, and nutritional goals. + """Provides access to Fitbit Nutrition API for managing food and water tracking. + + This resource handles endpoints for logging food intake, creating and managing custom foods + and meals, tracking water consumption, setting nutritional goals, and retrieving food + database information. It supports comprehensive tracking of dietary intake and hydration. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/ + + Required Scopes: + - nutrition: Required for all endpoints in this resource + + Note: + The Nutrition API is one of the most comprehensive Fitbit APIs, with functionality for: + - Logging foods and water consumption + - Creating custom foods and meals + - Managing favorites and frequently used items + - Searching the Fitbit food database + - Setting and retrieving nutritional goals + - Retrieving nutritional unit information + + Nutrition data is always associated with a specific date, and most logging + endpoints require a valid foodId from either the Fitbit food database or + from custom user-created food entries. Meals are collections of food entries + that can be reused for convenience. + + All nutritional values are specified in the units set in the user's Fitbit + account settings (metric or imperial). """ def add_favorite_foods(self, food_id: int, user_id: str = "-", debug: bool = False) -> None: """ - Adds a food with the given ID to the user's list of favorite foods. + Adds a food to the user's list of favorite foods. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/add-favorite-foods/ Args: - food_id: ID of the food to add to favorites - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + food_id: ID of the food to add to favorites (from Fitbit's food database) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Response indicating success + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.ValidationException: If the food ID is invalid or already a favorite + fitbit_client.exceptions.NotFoundException: If the food ID doesn't exist + + Note: + Favorite foods are displayed prominently when logging meals, making + it easier to log frequently consumed items. """ result = self._make_request( f"foods/log/favorite/{food_id}.json", user_id=user_id, http_method="POST", debug=debug @@ -65,24 +96,45 @@ def create_food( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates a new private food entry for a user. + """Creates a new private custom food entry for a user. + + This endpoint allows users to create their own custom food entries that can be + reused when logging meals. Custom foods are private to the user's account and + include detailed nutritional information. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/create-food/ Args: name: Name of the food being created - default_food_measurement_unit_id: ID from the food units endpoint + default_food_measurement_unit_id: ID from the food units endpoint (get_food_units) default_serving_size: Size of default serving with nutritional values calories: Number of calories for default serving size description: Description of the food - form_type: Food texture - either LIQUID or DRY + form_type: Food texture - either FoodFormType.LIQUID or FoodFormType.DRY nutritional_values: Dictionary mapping NutritionalValue constants to their amounts - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Created food entry details including food ID and nutritional values + JSONDict: Created food entry details containing the food object with ID, name, nutritional values, + and other metadata about the custom food + + Raises: + fitbit_client.exceptions.ValidationException: If required parameters are invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The nutritional_values dictionary accepts any nutrition constants defined in + the NutritionalValue enum, including macronutrients (protein, carbs, fat) and + micronutrients (vitamins, minerals). Values should be provided in the units + specified in the user's account settings. + + Food measurement unit IDs can be retrieved using the get_food_units method. + Common values include: + - 226: gram + - 180: ounce + - 147: tablespoon + - 328: milliliter """ params: ParamDict = { "name": name, @@ -133,13 +185,15 @@ def create_food_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates a food log entry for a given day. + """Creates a food log entry for tracking nutrition on a specific day. + + This endpoint allows recording food consumption either from the Fitbit food database + or as a custom food entry with nutritional information. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/create-food-log/ Args: - date: Log date in yyyy-MM-dd format + date: Log date in YYYY-MM-DD format or 'today' meal_type_id: Meal type (BREAKFAST, MORNING_SNACK, LUNCH, etc.) unit_id: Unit ID from food units endpoint amount: Amount consumed in specified unit (X.XX format) @@ -150,23 +204,36 @@ def create_food_log( calories: Optional calories for custom foods nutritional_values: Optional dictionary mapping NutritionalValue constants to their amounts (only used with custom foods) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Created food log entry details and daily summary + JSONDict: Created food log entry containing the food details, nutritional values, + and a summary of the day's total nutritional intake Raises: - ValueError: Must provide either food_id or (food_name and calories) - InvalidDateException: If date format is invalid + fitbit_client.exceptions.ClientValidationException: Must provide either food_id or (food_name and calories) + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Either food_id or (food_name and calories) must be provided. - Using food_id is recommended when possible. - Nutritional values are only used when creating custom food logs. - unit_id must match one of the units associated with the food. It may - also be that it can only match the default_food_measurement_unit_id - for a food you created. + There are two ways to log foods: + 1. Using food_id from the Fitbit database: Provide food_id, unit_id, and amount + 2. Custom food entry: Provide food_name, calories, unit_id, and amount + + The favorite parameter (when set to True) will automatically add the food + to the user's favorites list when using a food_id. + + For custom food entries, you can optionally provide: + - brand_name: To specify the brand of the food + - nutritional_values: To specify detailed nutritional information + + The unit_id must match one of the units associated with the food. For + existing foods, valid units can be found in the food details. For custom + foods, any valid unit ID from the get_food_units method can be used. + + The response includes both the created food log entry and a summary of + the day's nutritional totals after adding this entry. """ if not food_id and not (food_name and calories): raise ClientValidationException( @@ -208,8 +275,10 @@ def create_food_goal( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates or updates a user's daily calorie consumption goal or food plan. + """Creates or updates a user's daily calorie consumption goal or food plan. + + This endpoint allows setting either a simple calorie goal or a more complex + food plan linked to weight management goals. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/create-food-goal/ @@ -218,15 +287,35 @@ def create_food_goal( intensity: Optional food plan intensity (MAINTENANCE, EASIER, MEDIUM, KINDAHARD, HARDER) personalized: Optional food plan type (true/false) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Updated food goal information + JSONDict: Updated food goal information containing calorie goals and food plan details + (if enabled) + + Raises: + ValueError: If neither calories nor intensity is provided + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + fitbit_client.exceptions.ValidationException: If parameters are invalid Note: - Either calories or intensity must be provided. - Food plan requires an active weight goal. + There are two ways to set nutrition goals: + 1. Simple calorie goal: Provide only the calories parameter + 2. Food plan: Provide the intensity parameter, with optional personalized parameter + + A food plan is linked to weight management and requires an active weight goal + to be set using the create_weight_goal method in the BodyResource. + + The food plan intensity levels determine calorie deficit or surplus: + - MAINTENANCE: Maintain current weight + - EASIER: Small deficit/surplus for gradual change + - MEDIUM: Moderate deficit/surplus for steady change + - KINDAHARD: Large deficit/surplus for faster change + - HARDER: Maximum recommended deficit/surplus + + The personalized parameter, when set to true, creates a food plan that + accounts for the user's activity levels rather than a fixed calorie goal. """ if not calories and not intensity: raise ValueError("Must provide either calories or intensity") @@ -252,8 +341,10 @@ def create_meal( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates a meal with the given foods. + """Creates a reusable meal template with the specified foods. + + This endpoint creates a saved meal template that can be used for easier + logging of frequently consumed food combinations. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/create-meal/ @@ -264,14 +355,32 @@ def create_meal( food_id: ID of food to include in meal unit_id: ID of units used amount: Amount consumed (X.XX format) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Created meal details including meal ID and food information + JSONDict: Created meal details containing the meal's ID, name, description, and the + list of foods included in the meal + + Raises: + fitbit_client.exceptions.ValidationException: If food objects are incorrectly formatted + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Meals are always associated with meal type "Anytime" (7). + Meals are simply templates that can be reused for easier logging. + Creating a meal does not automatically log those foods to any date. + To log these foods on a specific date, you must still use create_food_log + for each food item in the meal. + + Meals are always associated with meal type "Anytime" (7) when created, + but individual foods can be assigned to specific meal types when logged. + + Each food object in the foods list requires: + - food_id: Identifier for the food from the Fitbit database or custom foods + - unit_id: Unit identifier (see get_food_units for available options) + - amount: Quantity in specified units + + Food IDs can be obtained from food search results or the user's custom foods. """ # snakes to camels foods = [{to_camel_case(k): v for k, v in d.items()} for d in foods] @@ -282,18 +391,36 @@ def create_meal( return cast(JSONDict, result) def create_water_goal(self, target: float, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Creates or updates a user's daily water consumption goal. + """Creates or updates a user's daily water consumption goal. + + This endpoint sets a target for daily water intake, which is used to track + hydration progress in the Fitbit app. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/create-water-goal/ Args: - target: Target water goal in the unit system matching locale - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + target: Target water goal in the unit system matching locale (mL or fl oz) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Updated water goal information + JSONDict: Updated water goal information containing the target amount and start date + + Raises: + fitbit_client.exceptions.ValidationException: If the target value is not positive + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The target value should be specified in the unit system that corresponds + to the Accept-Language header provided during client initialization + (fluid ounces for en_US, milliliters for most other locales). + + Typical daily water intake recommendations range from 1500-3000mL + (50-100 fl oz) depending on factors like body weight, activity level, + and climate. + + Progress toward this goal can be tracked by logging water consumption + using the create_water_log method. """ result = self._make_request( "foods/log/water/goal.json", @@ -313,27 +440,46 @@ def create_water_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates a water log entry. + """Creates a water log entry for tracking hydration on a specific day. + + This endpoint allows recording water consumption for a given date, which + contributes to the user's daily hydration tracking. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/create-water-log/ Args: amount: Amount of water consumed (X.X format) - date: Log date in yyyy-MM-dd format - unit: Optional unit ('ml', 'fl oz', 'cup') - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + date: Log date in YYYY-MM-DD format or 'today' + unit: Optional unit (WaterUnit.ML, WaterUnit.FL_OZ, or WaterUnit.CUP) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Created water log entry details + JSONDict: Created water log entry containing amount, ID, date, and unit information + (if unit was explicitly provided) Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If amount format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - If unit is not specified, uses unit system from Accept-Language - header. This can be specified when the client is initialized. + Water logs track hydration over time and contribute to daily water totals. + Multiple entries can be logged on the same day to track water consumption + throughout the day. + + If unit is not specified, the API uses the unit system from the Accept-Language + header which can be specified when the client is initialized: + - For en_US locale: fluid ounces (fl oz) + - For other locales: milliliters (mL) + + Available water units from the WaterUnit enum: + - WaterUnit.ML: milliliters (metric) + - WaterUnit.FL_OZ: fluid ounces (imperial) + - WaterUnit.CUP: cups (common) + + Water logs contribute to the daily water total shown in the Fitbit app and + progress toward any water goal set using create_water_goal. """ params: ParamDict = {"amount": amount, "date": date} if unit: @@ -344,14 +490,36 @@ def create_water_log( return cast(JSONDict, result) def delete_custom_food(self, food_id: int, user_id: str = "-", debug: bool = False) -> None: - """ - Deletes a custom food created by the user. + """Deletes a custom food permanently from the user's account. + + This endpoint permanently removes a custom food that was previously created + by the user. This cannot be used to delete foods from the Fitbit database. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/delete-custom-food/ Args: - food_id: ID of the food to delete - user_id: Optional user ID, defaults to current user + food_id: ID of the custom food to delete + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.NotFoundException: If the food ID doesn't exist + fitbit_client.exceptions.ValidationException: If attempting to delete a non-custom food + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Only custom foods created by the user (accessLevel: "PRIVATE") can be deleted. + Foods from the Fitbit database (accessLevel: "PUBLIC") cannot be deleted. + + Deleting a custom food will also remove it from favorites if it was marked + as a favorite. Any existing food logs using this food will remain intact, + but you won't be able to create new logs with this food ID. + + Custom food IDs can be obtained from the search_foods method or from + previously created custom foods using create_food. """ result = self._make_request( f"foods/{food_id}.json", user_id=user_id, http_method="DELETE", debug=debug @@ -359,14 +527,37 @@ def delete_custom_food(self, food_id: int, user_id: str = "-", debug: bool = Fal return cast(None, result) def delete_favorite_foods(self, food_id: int, user_id: str = "-", debug: bool = False) -> None: - """ - Removes a food from user's list of favorite foods. + """Removes a food from the user's list of favorite foods. + + This endpoint removes a food from the user's favorites list without + deleting the food itself from the database. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/delete-favorite-foods/ Args: food_id: ID of the food to remove from favorites - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.NotFoundException: If the food ID doesn't exist + fitbit_client.exceptions.ValidationException: If the food isn't in favorites + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + This endpoint only removes the food from the favorites list. The food itself + (whether from the Fitbit database or a custom food) remains available for + logging. This affects which foods appear in the favorites section of the + Fitbit app when logging meals. + + Foods can be added to favorites using the add_favorite_foods method or + by setting the favorite parameter to True when using create_food_log. + + Food IDs can be obtained from the get_favorite_foods, search_foods, + get_frequent_foods, or get_recent_foods methods. """ result = self._make_request( f"foods/log/favorite/{food_id}.json", user_id=user_id, http_method="DELETE", debug=debug @@ -376,14 +567,34 @@ def delete_favorite_foods(self, food_id: int, user_id: str = "-", debug: bool = delete_favorite_food = delete_favorite_foods # semantically correct alias def delete_food_log(self, food_log_id: int, user_id: str = "-", debug: bool = False) -> None: - """ - Deletes a food log entry. + """Deletes a food log entry permanently. + + This endpoint permanently removes a specific food log entry from the user's + food diary for a particular date. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/delete-food-log/ Args: food_log_id: ID of the food log to delete - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.NotFoundException: If the food log ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Deleting a food log entry removes it from the daily total calculations and + nutritional summaries for that day. The deletion is permanent and cannot be undone. + + This operation deletes a specific food log entry (a record of consuming a + food on a particular date), not the food itself from the database. + + Food log IDs can be obtained from the get_food_log method, which returns + all food logs for a specific date. """ result = self._make_request( f"foods/log/{food_log_id}.json", user_id=user_id, http_method="DELETE", debug=debug @@ -391,14 +602,34 @@ def delete_food_log(self, food_log_id: int, user_id: str = "-", debug: bool = Fa return cast(None, result) def delete_meal(self, meal_id: int, user_id: str = "-", debug: bool = False) -> None: - """ - Deletes a meal. + """Deletes a meal template permanently. + + This endpoint permanently removes a meal template from the user's saved meals. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/delete-meal/ Args: meal_id: ID of the meal to delete - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.NotFoundException: If the meal ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Meal templates are simply collections of foods that can be reused for + easier logging. Deleting a meal template does not affect any food logs + that may have been previously created using that meal. + + This operation removes the meal template itself, not the constituent foods + from the database. + + Meal IDs can be obtained from the get_meals method, which returns + all saved meal templates for the user. """ result = self._make_request( f"meals/{meal_id}.json", user_id=user_id, http_method="DELETE", debug=debug @@ -406,14 +637,33 @@ def delete_meal(self, meal_id: int, user_id: str = "-", debug: bool = False) -> return cast(None, result) def delete_water_log(self, water_log_id: int, user_id: str = "-", debug: bool = False) -> None: - """ - Deletes a water log entry. + """Deletes a water log entry permanently. + + This endpoint permanently removes a specific water log entry from the user's + hydration tracking for a particular date. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/delete-water-log/ Args: water_log_id: ID of the water log to delete - user_id: Optional user ID, defaults to current user + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.NotFoundException: If the water log ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Deleting a water log entry removes it from the daily hydration total calculations + for that day. The deletion is permanent and cannot be undone. + + This affects progress toward any water goal that may be set for the user. + + Water log IDs can be obtained from the get_water_log method, which returns + all water logs for a specific date. """ result = self._make_request( f"foods/log/water/{water_log_id}.json", @@ -424,210 +674,356 @@ def delete_water_log(self, water_log_id: int, user_id: str = "-", debug: bool = return cast(None, result) def get_food(self, food_id: int, debug: bool = False) -> JSONDict: - """ - Retrieves details of a specific food from Fitbit's database or user's private foods. + """Returns details of a specific food from Fitbit's database or user's private foods. + + This endpoint retrieves comprehensive information about a food item, including + nutritional values, serving sizes, and brand information. It can be used to retrieve + both public foods from Fitbit's database and private custom foods created by the user. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-food/ Args: food_id: ID of the food to retrieve - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Food details including nutritional information and available units + JSONDict: Food details containing name, brand, calories, available units, and nutritional + values (for private foods only) + + Raises: + fitbit_client.exceptions.NotFoundException: If the food ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If the user lacks permission to access the food + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Nutritional values are only included for PRIVATE foods. + Nutritional values are only included for PRIVATE (custom) foods. + For foods from the Fitbit database, only basic information is provided. + + Food IDs are unique across both the Fitbit database and user's private foods. + These IDs are used when logging food consumption with the create_food_log method. + + This endpoint is public and does not require a user_id parameter. """ result = self._make_request(f"foods/{food_id}.json", requires_user_id=False, debug=debug) return cast(JSONDict, result) def get_food_goals(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves the user's daily calorie consumption goal and/or food plan. + """Retrieves the user's daily calorie consumption goal and/or food plan. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-food-goals/ Args: user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dict containing calorie goal and food plan if enabled + JSONDict: Food goal information containing calorie goals and food plan details (if enabled) + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized for the nutrition scope + fitbit_client.exceptions.InvalidRequestException: If the user ID is invalid Note: Food plan data is only included if the feature is enabled. + The food plan is tied to the user's weight goals and activity level. """ result = self._make_request("foods/log/goal.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @validate_date_param(field_name="date") def get_food_log(self, date: str, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves a summary of all food log entries for a given day. + """Retrieves a summary of all food log entries for a given day. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-food-log/ Args: - date: The date in yyyy-MM-dd format or 'today' + date: The date in YYYY-MM-DD format or 'today' user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dictionary containing food log entries and daily summary + JSONDict: Food log data containing an array of logged foods, nutritional goals, and + a summary of the day's total nutritional values Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + + Note: + The response includes both individual food entries grouped by meal type + and a daily summary of total nutritional values. Each food entry contains + both the logged food details and its nutritional contribution to the daily total. """ result = self._make_request(f"foods/log/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) def get_food_locales(self, debug: bool = False) -> JSONList: - """ - Retrieves the list of food locales used for searching and creating foods. + """Retrieves the list of food locales used for searching and creating foods. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-food-locales/ - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + + Args: + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of supported locales with regional settings + JSONList: List of supported locales with country name, language, and locale identifier + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized for the nutrition scope + fitbit_client.exceptions.ServiceUnavailableException: If the Fitbit service is unavailable + + Note: + Locale settings affect food database searches and the units used for + nutritional values. The selected locale determines which regional + food database is used for searches and which measurement system + (metric or imperial) is used for logging values. """ result = self._make_request("foods/locales.json", requires_user_id=False, debug=debug) return cast(JSONList, result) def get_food_units(self, debug: bool = False) -> JSONList: - """ - Retrieves list of valid Fitbit food units. + """Retrieves list of valid Fitbit food units. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-food-units/ - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + + Args: + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of available measurement units for food logging + JSONList: List of available food measurement units with their IDs, names, and plural forms + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized for the nutrition scope + fitbit_client.exceptions.ServiceUnavailableException: If the Fitbit service is unavailable + + Note: + Unit IDs are used in several nutrition endpoints, including: + - create_food: For specifying default measurement units for custom foods + - create_food_log: For specifying the measurement units for logged food quantities + - update_food_log: For changing the measurement units of existing logs + + Common unit IDs include: + - Weight units: 226 (gram), 180 (ounce) + - Volume units: 328 (milliliter), 218 (fluid ounce), 147 (tablespoon), + 182 (cup), 189 (pint), 204 (quart) + - Count units: 221 (serving) """ result = self._make_request("foods/units.json", requires_user_id=False, debug=debug) return cast(JSONList, result) def get_frequent_foods(self, user_id: str = "-", debug: bool = False) -> JSONList: - """ - Retrieves a list of user's frequently consumed foods. + """Retrieves a list of user's frequently consumed foods. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-frequent-foods/ Args: user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of frequently logged foods with serving information + JSONList: List of frequently logged foods with details including food ID, name, brand, + calories, and available measurement units + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized for the nutrition scope + fitbit_client.exceptions.InvalidRequestException: If the user ID is invalid Note: - Foods in the response can be quickly logged using create_food_log. + The frequent foods endpoint returns foods that the user has logged + multiple times, making it easier to quickly log commonly consumed items. + + Each food entry contains essential information needed for logging, including: + - Food identification (foodId, name, brand) + - Calorie information + - Available measurement units + - Default unit for serving size + + These foods can be efficiently logged using the create_food_log method + with their foodId values. """ result = self._make_request("foods/log/frequent.json", user_id=user_id, debug=debug) return cast(JSONList, result) def get_recent_foods(self, user_id: str = "-", debug: bool = False) -> JSONList: - """ - Retrieves a list of user's recently consumed foods. + """Retrieves a list of user's recently consumed foods. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-recent-foods/ Args: user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of recently logged foods with dates and details + JSONList: List of recently logged foods with details including food ID, log ID, name, + brand, calories, log date, and available measurement units + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized for the nutrition scope + fitbit_client.exceptions.InvalidRequestException: If the user ID is invalid + + Note: + The recent foods endpoint returns foods that the user has most recently + logged, sorted with the most recent entries first. Unlike the frequent + foods endpoint, this includes one-time or infrequently consumed items. + + Each food entry includes both the food details needed for logging again + (foodId, name, units) and information about the previous log (logId, logDate). + + These foods can be efficiently logged again using the create_food_log method + with their foodId values. """ result = self._make_request("foods/log/recent.json", user_id=user_id, debug=debug) return cast(JSONList, result) def get_favorite_foods(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves a list of user's favorite foods. + """Retrieves a list of user's favorite foods. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-favorite-foods/ Args: user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of foods marked as favorites + JSONDict: Dictionary containing an array of the user's favorite foods with their + details including name, brand, calories, and available units + + Raises: + fitbit_client.exceptions.AuthorizationException: If not authorized to access this data + fitbit_client.exceptions.InvalidRequestException: If the request parameters are invalid + + Note: + Favorite foods are those explicitly marked as favorites by the user + using the add_favorite_foods method. These are displayed prominently + in the Fitbit app and are intended to provide quick access to frequently + used items. + + Foods can be added to favorites with the add_favorite_foods method and + removed with delete_favorite_foods. When logging foods with create_food_log, + the favorite parameter can be used to automatically add a food to favorites. """ result = self._make_request("foods/log/favorite.json", user_id=user_id, debug=debug) return cast(JSONDict, result) def get_meal(self, meal_id: int, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves a single meal from user's food log. + """Retrieves a single meal from user's food log. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-meal/ Args: meal_id: ID of the meal to retrieve user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Meal details including constituent foods and nutritional info + JSONDict: Single meal details containing name, description, ID, and the list of + foods included in the meal with their amounts and nutritional information + + Raises: + fitbit_client.exceptions.NotFoundException: If the meal ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - All meals are associated with meal type "Anytime" (7). + Meals in Fitbit are user-defined collections of foods that can be logged + together for convenience. All meals are associated with meal type "Anytime" (7), + regardless of when they're consumed. When logging a meal, the individual + food items can be assigned to specific meal types (breakfast, lunch, etc.). + + Meals can be created with the create_meal method, updated with update_meal, + and deleted with delete_meal. """ result = self._make_request(f"meals/{meal_id}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) def get_meals(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves list of all user's saved meals. + """Retrieves list of all user's saved meals. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-meals/ Args: user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of saved meals with their foods and nutritional info + JSONDict: Dictionary containing an array of all user-defined meals with their details + and constituent foods + + Raises: + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Meals provide a way to save commonly eaten food combinations for + easy logging. Unlike individual food logs which are associated with + specific dates, meals are reusable templates that can be applied to + any date when needed. + + Each meal has: + - A unique ID for referencing in other API calls + - A name and description for identification + - A list of constituent foods with their amounts and units + - Calorie information for each food component + + To log a meal on a specific date, you would need to individually log + each food in the meal using the create_food_log method. """ result = self._make_request("meals.json", user_id=user_id, debug=debug) return cast(JSONDict, result) def get_water_goal(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves user's daily water consumption goal. + """Retrieves user's daily water consumption goal. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-water-goal/ Args: user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Water goal amount and start date + JSONDict: Water goal information containing the target amount and when the goal was set + + Raises: + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The target value is expressed in the user's preferred unit system + (milliliters for metric, fluid ounces for imperial), determined by + the user's locale settings. The create_water_goal method can be used + to update this target value. + + Water consumption is tracked separately from other nutrients but is + included in the daily summary returned by get_food_log. """ result = self._make_request("foods/log/water/goal.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @validate_date_param(field_name="date") def get_water_log(self, date: str, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Retrieves water log entries for a specific date. + """Retrieves water log entries for a specific date. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/get-water-log/ Args: - date: Log date in yyyy-MM-dd format + date: Log date in YYYY-MM-DD format or 'today' user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Water consumption summary and individual log entries + JSONDict: Water log data containing individual water entries and a summary of + total water consumption for the day Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Water logs represent individual entries of water consumption throughout + the day. The summary provides the total amount for the day, while the + water array contains each individual log entry. + + Amounts are expressed in the user's preferred unit system (milliliters for + metric, fluid ounces for imperial), determined by the user's locale settings. + + Water logs can be created with create_water_log, updated with update_water_log, + and deleted with delete_water_log. """ result = self._make_request( f"foods/log/water/date/{date}.json", user_id=user_id, debug=debug @@ -635,22 +1031,36 @@ def get_water_log(self, date: str, user_id: str = "-", debug: bool = False) -> J return cast(JSONDict, result) def search_foods(self, query: str, debug: bool = False) -> JSONDict: - """ - Searches Fitbit's food database and user's custom foods. + """Searches Fitbit's food database and user's custom foods. + + This endpoint allows searching both the Fitbit food database and the user's custom + foods by name. The search results include basic nutritional information and can + be used to retrieve food IDs for logging consumption. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/search-foods/ Args: query: Search string to match against food names - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of matching foods with nutritional information + JSONDict: Dictionary containing an array of foods matching the search query, with + details including name, brand, calories, and available units + + Raises: + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: Results include both PUBLIC (Fitbit database) and PRIVATE (user-created) foods. - Search uses the locale specified in accept-language header. This can be + Search uses the locale specified in the Accept-Language header, which can be specified when the client is initialized. + + This endpoint is public and does not require a user_id parameter, but will + still return PRIVATE foods for the authenticated user. + + The search results provide enough information to display basic food details, + but for comprehensive nutritional information, use the get_food method with + the returned foodId values. """ result = self._make_request( "foods/search.json", params={"query": query}, requires_user_id=False, debug=debug @@ -667,27 +1077,38 @@ def update_food_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Updates an existing food log entry. + """Updates an existing food log entry. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/update-food-log/ Args: food_log_id: ID of the food log to update - meal_type_id: 1=Breakfast, 2=Morning Snack, 3=Lunch, 4=Afternoon Snack, - 5=Dinner, 7=Anytime + meal_type_id: Meal type (BREAKFAST, MORNING_SNACK, LUNCH, etc.) unit_id: Optional unit ID (required for foods with foodId) amount: Optional amount in specified unit (required for foods with foodId) calories: Optional calories (only for custom food logs) user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Updated food log entry + JSONDict: Updated food log entry containing the modified food details with updated + amount, calories, and nutritional values reflecting the changes + + Raises: + fitbit_client.exceptions.ValueError: If neither (unit_id and amount) nor calories are provided + fitbit_client.exceptions.NotFoundException: If the food log ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Either (unit_id and amount) or calories must be provided. - Only food logs with valid foodId can be updated with unit/amount. + Either (unit_id and amount) or calories must be provided: + - For foods with a valid foodId, provide unit_id and amount to update the serving size + - For custom food logs (without foodId), provide calories to update the calorie count + + This method allows changing the meal type (breakfast, lunch, etc.) for a food log + entry as well as its quantity. The nutritional values are automatically recalculated + based on the updated amount. + + Food log IDs can be obtained from the get_food_log method. """ params: ParamDict = {"mealTypeId": int(meal_type_id.value)} if unit_id is not None and amount is not None: @@ -716,8 +1137,7 @@ def update_meal( debug: bool = False, user_id: str = "-", ) -> JSONDict: - """ - Updates an existing meal. + """Updates an existing meal. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/update-meal/ @@ -729,11 +1149,31 @@ def update_meal( food_id: ID of food to include in meal unit_id: ID of units used amount: Amount consumed (X.XX format) + debug: If True, prints a curl command to stdout to help with debugging (default: False) user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) Returns: - Updated meal information + JSONDict: Updated meal information containing the modified meal details with name, + description, ID, and the updated list of foods + + Raises: + fitbit_client.exceptions.NotFoundException: If the meal ID doesn't exist + fitbit_client.exceptions.ValidationException: If food objects are incorrectly formatted + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + This method completely replaces the existing meal with the new definition. + All foods must be specified in the foods list, even those that were previously + part of the meal and should remain unchanged. Any foods not included in the + update will be removed from the meal. + + Each food object in the foods list requires: + - food_id: Identifier for the food from the Fitbit database or custom foods + - unit_id: Unit identifier (see get_food_units for available options) + - amount: Quantity in specified units + + Meal IDs can be obtained from the get_meals method. Updating a meal does not + affect any food logs that were previously created using this meal. """ foods = [{to_camel_case(k): v for k, v in d.items()} for d in foods] data = {"name": name, "description": description, "mealFoods": foods} @@ -750,8 +1190,7 @@ def update_water_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Updates an existing water log entry. + """Updates an existing water log entry. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition/update-water-log/ @@ -760,14 +1199,30 @@ def update_water_log( amount: New amount consumed (X.X format) unit: Optional unit ('ml', 'fl oz', 'cup') user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Updated water log entry + JSONDict: Updated water log entry containing the modified amount, log ID, date, + and unit information (if unit was explicitly provided) + + Raises: + fitbit_client.exceptions.NotFoundException: If the water log ID doesn't exist + fitbit_client.exceptions.ValidationException: If amount format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - If unit is not specified, uses unit system from Accept-Language - header. This can be specified when the client is initialized. + If unit is not specified, the API uses the unit system from the Accept-Language + header which can be specified when the client is initialized (metric or imperial). + + Available water units are defined in the WaterUnit enum: + - WaterUnit.ML: milliliters (metric) + - WaterUnit.FL_OZ: fluid ounces (imperial) + - WaterUnit.CUP: cups (common) + + Water log IDs can be obtained from the get_water_log method. + + After updating a water log, the daily summary values are automatically recalculated + to reflect the new hydration total. """ params: ParamDict = {"amount": amount} if unit: diff --git a/fitbit_client/resources/nutrition_timeseries.py b/fitbit_client/resources/nutrition_timeseries.py index e779b05..331c35d 100644 --- a/fitbit_client/resources/nutrition_timeseries.py +++ b/fitbit_client/resources/nutrition_timeseries.py @@ -13,14 +13,28 @@ class NutritionTimeSeriesResource(BaseResource): - """ - Handles Fitbit Nutrition Time Series API endpoints for retrieving historical food and water data. + """Provides access to Fitbit Nutrition Time Series API for retrieving historical nutrition data. + + This resource handles endpoints for retrieving historical food and water consumption data + over time. It provides daily summaries of calorie and water intake, allowing applications + to display trends and patterns in nutritional data over various time periods. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition-timeseries/ - This resource provides access to daily summaries of: - - Calorie consumption - - Water consumption + Required Scopes: + - nutrition: Required for all endpoints in this resource + + Note: + This resource provides access to daily summaries of: + - Calorie consumption (caloriesIn) + - Water consumption (water) + + The data is always returned with date values and can be queried either by + specifying a base date and period, or by providing explicit start and end dates. + + All water measurements are returned in the unit system specified by the Accept-Language + header provided during client initialization (fluid ounces for en_US, milliliters + for most other locales). """ @validate_date_param(field_name="date") @@ -32,27 +46,36 @@ def get_nutrition_timeseries_by_date( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves nutrition data for a period starting from a specified date. + """Returns nutrition data for a period ending on the specified date. + + This endpoint retrieves daily summaries of calorie intake or water consumption + for a specified time period ending on the given date. It provides historical + nutrition data that can be used to analyze trends over time. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition-timeseries/get-nutrition-timeseries-by-date/ Args: - resource: Resource to query (CALORIES_IN or WATER) - date: The end date in yyyy-MM-dd format or 'today' - period: Time period for data (1d, 7d, 30d, 1w, 1m, 3m, 6m, 1y) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + resource: Resource to query (NutritionResource.CALORIES_IN or NutritionResource.WATER) + date: The end date in YYYY-MM-DD format or 'today' + period: Time period for data (e.g., Period.ONE_DAY, Period.ONE_WEEK, Period.ONE_MONTH) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dictionary containing daily summary values for the specified period + JSONDict: Dictionary containing daily summary values for calorie intake or water consumption, + with dates and corresponding values for each day in the period Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Returns data using units corresponding to Accept-Language header. - Only returns data since user's join date or first log entry. + Data is returned in chronological order (oldest first). The API only returns + data from the user's join date or first log entry onward. Days with no logged + data may be omitted from the response. + + Water values are returned in the unit system specified by the Accept-Language + header (fluid ounces for en_US, milliliters for most other locales). """ result = self._make_request( f"foods/log/{resource.value}/date/{date}/{period.value}.json", @@ -70,29 +93,43 @@ def get_nutrition_timeseries_by_date_range( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Retrieves nutrition data for a specified date range. + """Returns nutrition data for a specified date range. + + This endpoint retrieves daily summaries of calorie intake or water consumption + for a specific date range. It allows for more precise control over the time + period compared to the period-based endpoint. API Reference: https://dev.fitbit.com/build/reference/web-api/nutrition-timeseries/get-nutrition-timeseries-by-date-range/ Args: - resource: Resource to query (CALORIES_IN or WATER) - start_date: Start date in yyyy-MM-dd format or 'today' - end_date: End date in yyyy-MM-dd format or 'today' - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + resource: Resource to query (NutritionResource.CALORIES_IN or NutritionResource.WATER) + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Dictionary containing daily summary values for the date range + JSONDict: Dictionary containing daily summary values for calorie intake or water consumption, + with dates and corresponding values for each day in the specified date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date or date range exceeds 1095 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date + or date range exceeds 1095 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Maximum range is 1095 days (~3 years). - Returns data using units corresponding to Accept-Language header. - Only returns data since user's join date or first log entry. + Maximum date range is 1095 days (approximately 3 years). + + Data is returned in chronological order (oldest first). The API only returns + data from the user's join date or first log entry onward. Days with no logged + data may be omitted from the response. + + Water values are returned in the unit system specified by the Accept-Language + header (fluid ounces for en_US, milliliters for most other locales). + + This endpoint returns the same data format as get_nutrition_timeseries_by_date, + but allows for more precise control over the date range. """ result = self._make_request( f"foods/log/{resource.value}/date/{start_date}/{end_date}.json", diff --git a/fitbit_client/resources/sleep.py b/fitbit_client/resources/sleep.py index d2702f8..f05dcb9 100644 --- a/fitbit_client/resources/sleep.py +++ b/fitbit_client/resources/sleep.py @@ -16,14 +16,19 @@ class SleepResource(BaseResource): - """ - Handles Fitbit Sleep API endpoints for recording, retrieving and managing - user sleep data and goals. + """Provides access to Fitbit Sleep API for recording, retrieving and managing sleep data. + + This resource handles endpoints for creating and retrieving sleep logs, setting sleep goals, + and accessing detailed sleep statistics and patterns. The API provides information about + sleep duration, efficiency, and stages (light, deep, REM, awake periods). API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/ + Required Scopes: sleep + Note: - All Sleep endpoints use API version 1.2 + All Sleep endpoints use API version 1.2, unlike most other Fitbit API endpoints + which use version 1. """ API_VERSION: str = "1.2" @@ -32,17 +37,25 @@ def create_sleep_goals( self, min_duration: int, user_id: str = "-", debug: bool = False ) -> JSONDict: """ - Creates or updates a user's sleep goal. + Creates or updates a user's sleep duration goal. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/create-sleep-goals/ Args: - min_duration: Length of sleep goal in minutes - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + min_duration: Target sleep duration in minutes (must be positive) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Sleep goal details including min duration and update timestamp + JSONDict: Sleep goal details including minimum duration and update timestamp + + Raises: + ValueError: If min_duration is not positive + + Note: + Sleep goals help users track and maintain healthy sleep habits. + The typical recommended sleep duration for adults is 420-480 minutes + (7-8 hours) per night. """ if min_duration <= 0: raise ValueError("min_duration must be positive") @@ -68,30 +81,40 @@ def create_sleep_log( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates a log entry for a sleep event. + """Creates a manual log entry for a sleep event. + + This endpoint allows creating manual sleep log entries to track sleep that + wasn't automatically detected by a device. This is useful for tracking naps + or sleep periods without wearing a tracker. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/create-sleep-log/ Args: - date: Log date in YYYY-MM-DD format - duration_millis: Duration in milliseconds - start_time: Activity start time (HH:mm) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + date: Log date in YYYY-MM-DD format or 'today' + duration_millis: Duration in milliseconds (e.g., 28800000 for 8 hours) + start_time: Sleep start time in HH:mm format (e.g., "23:30") + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Created sleep log entry details + JSONDict: Created sleep log entry with sleep metrics and summary information Raises: ValueError: If duration_millis is not positive - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.ValidationException: If time or duration is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - It is NOT possible to create overlapping log entries - The dateOfSleep in the response is the date on which the sleep event ends - Manual logs default to "classic" type since they lack the device heart rate and movement data needed for "stages" type + + Duration is provided in milliseconds (1 hour = 3,600,000 ms), while most of the + response values are in minutes for easier readability. + + This endpoint uses API version 1.2, unlike most other Fitbit API endpoints. """ if duration_millis <= 0: raise ValueError("duration_millis must be positive") @@ -108,15 +131,33 @@ def create_sleep_log( return cast(JSONDict, result) def delete_sleep_log(self, log_id: int, user_id: str = "-", debug: bool = False) -> None: - """ - Deletes a specific sleep log entry. + """Deletes a specific sleep log entry permanently. + + This endpoint permanently removes a sleep log entry from the user's history. + This can be used for both automatically tracked and manually entered sleep logs. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/delete-sleep-log/ Args: log_id: ID of the sleep log to delete - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + None: This endpoint returns an empty response on success + + Raises: + fitbit_client.exceptions.NotFoundException: If the log ID doesn't exist + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + Deleting a sleep log entry permanently removes it from the user's history + and daily summaries. This operation cannot be undone. + + Sleep log IDs can be obtained from the get_sleep_log_by_date or + get_sleep_log_list methods. + + This endpoint uses API version 1.2, unlike most other Fitbit API endpoints. """ result = self._make_request( f"sleep/{log_id}.json", @@ -128,20 +169,34 @@ def delete_sleep_log(self, log_id: int, user_id: str = "-", debug: bool = False) return cast(None, result) def get_sleep_goals(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Gets a user's current sleep goal. + """Retrieves a user's current sleep goal settings. + + This endpoint returns the user's target sleep duration goal and related settings. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/get-sleep-goals/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Sleep goal details including: - - minDuration: Length of sleep goal in minutes - - consistency: Sleep goal consistency flow status - - updatedOn: Last update timestamp + JSONDict: Sleep goal details including target sleep duration (in minutes), + consistency level, and last update timestamp + + Raises: + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + + Note: + The minDuration value represents the target sleep duration in minutes. + Typical recommended sleep durations are: + - 420-480 minutes (7-8 hours) for adults + - 540-600 minutes (9-10 hours) for teenagers + - 600-660 minutes (10-11 hours) for children + + The consistency value indicates the user's adherence to a regular + sleep schedule over time, with higher values indicating better consistency. + + This endpoint uses API version 1.2, unlike most other Fitbit API endpoints. """ result = self._make_request( "sleep/goal.json", user_id=user_id, api_version=SleepResource.API_VERSION, debug=debug @@ -152,28 +207,39 @@ def get_sleep_goals(self, user_id: str = "-", debug: bool = False) -> JSONDict: @validate_date_param(field_name="date") def get_sleep_log_by_date(self, date: str, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Gets sleep logs for a specific date. + """Returns sleep logs for a specific date. + + This endpoint retrieves all sleep logs (both automatically tracked and manually entered) + for a specific date. The response includes detailed information about sleep duration, + efficiency, and sleep stages if available. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/get-sleep-log-by-date/ Args: - date: The date in YYYY-MM-DD format - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + date: The date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Sleep logs and summary for the specified date including: - - Classic logs: asleep, restless, awake levels (60-sec granularity) - - Stages logs: deep, light, rem, wake levels (30-sec granularity) + JSONDict: Sleep logs and summary for the specified date, including duration, efficiency and sleep stages Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - The data returned can include a sleep period that began on the previous - date. For example, requesting logs for 2021-12-22 may return a log entry - that began on 2021-12-21 but ended on 2021-12-22. + The data returned includes all sleep periods that ended on the specified date. + This means a sleep period that began on the previous date but ended on the + requested date will be included in the response. + + There are two types of sleep data that may be returned: + - "classic": Basic sleep with 60-second resolution, showing asleep, restless, and awake states + - "stages": Advanced sleep with 30-second resolution, showing deep, light, REM, and wake stages + + Stages data is only available for compatible devices with heart rate tracking. + Manual entries always use the "classic" type. + + This endpoint uses API version 1.2, unlike most other Fitbit API endpoints. """ result = self._make_request( f"sleep/date/{date}.json", @@ -187,26 +253,40 @@ def get_sleep_log_by_date(self, date: str, user_id: str = "-", debug: bool = Fal def get_sleep_log_by_date_range( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Gets sleep logs for a date range. + """Retrieves sleep logs for a specified date range. + + This endpoint returns all sleep data (including automatically tracked and manually + entered sleep logs) for the specified date range, with detailed information about + sleep duration, efficiency, and sleep stages when available. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/get-sleep-log-by-date-range/ Args: - start_date: Start date in YYYY-MM-DD format - end_date: End date in YYYY-MM-DD format - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + start_date: Start date in YYYY-MM-DD format or 'today' + end_date: End date in YYYY-MM-DD format or 'today' + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Sleep logs for the specified date range + JSONDict: Sleep logs for the specified date range with aggregated sleep statistics Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date or date range exceeds 100 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date or range exceeds 100 days + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Maximum date range is 100 days + The maximum date range is 100 days. For longer historical periods, you + will need to make multiple requests with different date ranges. + + The data returned includes all sleep periods that ended within the + specified date range. This means a sleep period that began before the + start_date but ended within the range will be included in the response. + + As with the single-date endpoint, both "classic" and "stages" sleep data + may be included depending on device compatibility and how the sleep was logged. + + This endpoint uses API version 1.2, unlike most other Fitbit API endpoints. """ result = self._make_request( f"sleep/date/{start_date}/{end_date}.json", @@ -229,36 +309,46 @@ def get_sleep_log_list( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Gets a list of sleep logs before or after a given date. + """Retrieves a paginated list of sleep logs filtered by date. + + This endpoint returns sleep logs before or after a specified date with + pagination support. It provides an alternative to date-based queries + when working with large amounts of sleep data. API Reference: https://dev.fitbit.com/build/reference/web-api/sleep/get-sleep-log-list/ Args: - before_date: Get entries before this date (YYYY-MM-DD) - after_date: Get entries after this date (YYYY-MM-DD) - sort: Sort order ('asc' or 'desc') + before_date: Get entries before this date in YYYY-MM-DD format + after_date: Get entries after this date in YYYY-MM-DD format + sort: Sort direction (SortDirection.ASCENDING or SortDirection.DESCENDING) limit: Number of records to return (max 100) - offset: Offset for pagination (use 0) - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + offset: Offset for pagination (must be 0) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Paginated list of sleep logs + JSONDict: Paginated sleep logs with navigation links and sleep entries + + Raises: + fitbit_client.exceptions.PaginationError: If parameters are invalid (see Notes) + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Either before_date or after_date must be specified. - The offset parameter only supports 0 and using other values may break your application. - Use the pagination links in the response to iterate through results. + Important pagination requirements: + - Either before_date or after_date MUST be specified (not both) + - The offset parameter must be 0 (Fitbit API limitation) + - If before_date is used, sort must be DESCENDING + - If after_date is used, sort must be ASCENDING - Raises: - PaginatonError: If neither before_date nor after_date is specified - PaginatonError: If offset is not 0 - PaginatonError: If limit exceeds 10 - PaginatonError: If sort is not 'asc' or 'desc' - PaginatonError: If sort direction doesn't match date parameter - InvalidDateException: If date format is invalid + To handle pagination properly, use the URLs provided in the "pagination.next" + and "pagination.previous" fields of the response. This is more reliable than + manually incrementing the offset. + + This endpoint returns the same sleep data structure as get_sleep_log_by_date, + but organized in a paginated format rather than grouped by date. + This endpoint uses API version 1.2, unlike most other Fitbit API endpoints. """ params = {"sort": sort.value, "limit": limit, "offset": offset} if before_date: diff --git a/fitbit_client/resources/spo2.py b/fitbit_client/resources/spo2.py index ef48447..ede8486 100644 --- a/fitbit_client/resources/spo2.py +++ b/fitbit_client/resources/spo2.py @@ -12,44 +12,69 @@ class SpO2Resource(BaseResource): - """ - Handles Fitbit SpO2 (Blood Oxygen Saturation) API endpoints for retrieving - oxygen saturation measurements taken during sleep. + """Provides access to Fitbit SpO2 API for retrieving blood oxygen saturation data. - The data represents measurements taken during the user's "main sleep" period - (longest sleep period) and typically spans two dates since measurements are - taken during overnight sleep. + This resource handles endpoints for retrieving blood oxygen saturation (SpO2) measurements + taken during sleep. SpO2 data provides insights into breathing patterns and potential + sleep-related breathing disturbances. Normal SpO2 levels during sleep typically range + between 95-100%. API Reference: https://dev.fitbit.com/build/reference/web-api/spo2/ + + Required Scopes: + - oxygen_saturation: Required for all endpoints in this resource + + Note: + SpO2 data represents measurements taken during the user's "main sleep" period + (longest sleep period) and typically spans two dates since measurements are + taken during overnight sleep. The data is usually associated with the date + the user wakes up, not the date they went to sleep. + + SpO2 measurements require compatible Fitbit devices with SpO2 monitoring capability, + such as certain Sense, Versa, and Charge models with the SpO2 clock face or app installed. + + The data is calculated on a 5-minute basis during sleep and requires at least 3 hours + of quality sleep with minimal movement to generate readings. """ @validate_date_param(field_name="date") def get_spo2_summary_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get SpO2 summary data for a specific date. + """Returns SpO2 (blood oxygen saturation) summary data for a specific date. + + This endpoint provides daily summary statistics for blood oxygen saturation levels + measured during sleep, including average, minimum, and maximum values. These metrics + help monitor breathing quality during sleep. API Reference: https://dev.fitbit.com/build/reference/web-api/spo2/get-spo2-summary-by-date/ Args: date: Date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - SpO2 summary including average, minimum and maximum levels for the date. - Note that data typically reflects sleep that began the previous day. + JSONDict: SpO2 summary with average, minimum and maximum blood oxygen percentage values Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - SpO2 data requires: - - At least 3 hours of quality sleep - - Minimal movement during sleep - - Device sync after waking + SpO2 data requires all of the following conditions: + - Compatible device with SpO2 monitoring capability + - SpO2 clock face or app installed and configured + - At least 3 hours of quality sleep with minimal movement + - Device sync after waking up - Up to 1 hour processing time after sync + + The date requested typically corresponds to the wake-up date, not the date + when sleep began. For example, for overnight sleep from June 14 to June 15, + the data would be associated with June 15. + + If no SpO2 data is available for the requested date, the API will return an empty + response: {"dateTime": "2022-06-15"} """ result = self._make_request(f"spo2/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -58,28 +83,42 @@ def get_spo2_summary_by_date( def get_spo2_summary_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONList: - """ - Get SpO2 summary data for a date range. + """Returns SpO2 (blood oxygen saturation) summary data for a date range. + + This endpoint provides daily summary statistics for blood oxygen saturation levels + over a specified date range. It returns the same data as get_spo2_summary_by_date + but for multiple days, allowing for trend analysis over time. API Reference: https://dev.fitbit.com/build/reference/web-api/spo2/get-spo2-summary-by-interval/ Args: start_date: Start date in YYYY-MM-DD format or "today" end_date: End date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of daily SpO2 summaries including average, minimum and maximum - levels for each date in the range. + JSONList: List of daily SpO2 summaries for the specified date range Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date + fitbit_client.exceptions.AuthorizationException: If required scope is not granted Note: - Unlike many other endpoints, there is no maximum date range limit - for this endpoint. + Unlike many other Fitbit API endpoints, there is no maximum date range limit + for this endpoint. However, requesting very large date ranges may impact + performance and is generally not recommended. + + Days with no available SpO2 data will still be included in the response, but + without the "value" field: {"dateTime": "2022-06-17"} + + SpO2 data requirements: + - Compatible device with SpO2 monitoring capability + - SpO2 clock face or app installed and configured + - At least 3 hours of quality sleep with minimal movement + - Device sync after waking up + - Up to 1 hour processing time after sync """ result = self._make_request( f"spo2/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/subscription.py b/fitbit_client/resources/subscription.py index d4dcb06..55a0b09 100644 --- a/fitbit_client/resources/subscription.py +++ b/fitbit_client/resources/subscription.py @@ -11,22 +11,40 @@ class SubscriptionResource(BaseResource): - """ - Handles Fitbit Subscription API endpoints for managing webhook notifications - when users have new data available. This prevents the need to poll for updates. - - Subscriptions can be created for specific data categories or for all categories. - Note that creating both specific and all-category subscriptions will result in - duplicate notifications. - - This is essential reading for understanding how subscriptions work, including - how to verify subscribers: - https://dev.fitbit.com/build/reference/web-api/developer-guide/using-subscriptions/ + """Provides access to Fitbit Subscription API for managing webhook notifications. - As of now, only `get_subscription_list` is implemented. The verification tool does - not support self-signed certs, so you need a real cert to execute the workflow. + This resource enables applications to receive real-time notifications when users have + new data available, eliminating the need to continuously poll the API. Subscriptions + can be created for specific data categories (activities, body, foods, sleep) or for + all categories at once. + Developer Guide: https://dev.fitbit.com/build/reference/web-api/developer-guide/using-subscriptions/ API Reference: https://dev.fitbit.com/build/reference/web-api/subscription/ + + Required Scopes: + - For activity subscriptions: activity + - For body subscriptions: weight + - For foods subscriptions: nutrition + - For sleep subscriptions: sleep + - For all-category subscriptions: all relevant scopes above + + Implementation Requirements: + 1. A verification endpoint that responds to GET requests with verification challenges + 2. A notification endpoint that processes POST requests with updates + 3. Proper SSL certificates (self-signed certificates are not supported) + 4. Adherence to rate limits and notification processing timeouts + + Note: + Currently only `get_subscription_list` is fully implemented in this library. + The `create_subscription` and `delete_subscription` methods are defined but raise + NotImplementedError. Their documentation is provided as a reference for future implementation. + + Creating both specific and all-category subscriptions will result in duplicate + notifications for the same data changes, so choose one approach. + + Subscription notifications are sent as JSON payloads with information about what + changed, but not the actual data. Your application still needs to make API calls + to retrieve the updated data. """ def create_subscription( @@ -37,29 +55,34 @@ def create_subscription( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Creates a subscription to notify the application when a user has new data. + """Creates a subscription to notify the application when a user has new data. API Reference: https://dev.fitbit.com/build/reference/web-api/subscription/create-subscription/ Args: subscription_id: Unique ID for this subscription (max 50 chars) - category: Optional specific data category to subscribe to - subscriber_id: Optional subscriber ID from dev.fitbit.com - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + category: Optional specific data category to subscribe to (e.g., SubscriptionCategory.ACTIVITIES, + SubscriptionCategory.BODY). If None, subscribes to all categories. + subscriber_id: Optional subscriber ID from dev.fitbit.com app settings + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Subscription details including category and owner info + JSONDict: Subscription details including collection type, owner and subscription identifiers Raises: - ValidationException: If subscription_id exceeds 50 characters - InvalidRequestException: If subscriber_id is invalid + fitbit_client.exceptions.ValidationException: If subscription_id exceeds 50 characters + fitbit_client.exceptions.InvalidRequestException: If subscriber_id is invalid + fitbit_client.exceptions.InsufficientScopeException: If missing required OAuth scopes for the category Note: Each subscriber can only have one subscription per user's category. If no category is specified, all categories will be subscribed, - but this requires all relevant OAuth scopes. + but this requires all relevant OAuth scopes (activity, weight, nutrition, sleep). + + Subscribers must implement a verification endpoint that can respond to both + GET (verification) and POST (notification) requests. See the API documentation + for details on endpoint requirements. """ raise NotImplementedError # if len(subscription_id) > 50: @@ -91,17 +114,31 @@ def delete_subscription( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Deletes a subscription for a specific user. + """Deletes a subscription for a specific user. API Reference: https://dev.fitbit.com/build/reference/web-api/subscription/delete-subscription/ Args: subscription_id: ID of the subscription to delete - category: Optional specific data category subscription - subscriber_id: Optional subscriber ID from dev.fitbit.com - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + category: Optional specific data category subscription (e.g., SubscriptionCategory.ACTIVITIES, + SubscriptionCategory.BODY). Must match the category used when creating the subscription. + subscriber_id: Optional subscriber ID from dev.fitbit.com app settings + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) + + Returns: + JSONDict: Empty dictionary on successful deletion + + Raises: + fitbit_client.exceptions.InvalidRequestException: If subscription_id is invalid + fitbit_client.exceptions.NotFoundException: If subscription doesn't exist + fitbit_client.exceptions.AuthorizationException: If authentication fails or insufficient permissions + + Note: + When deleting a subscription: + - You must specify the same category that was used when creating the subscription + - After deletion, your application will no longer receive notifications for that user's data + - You may want to maintain a local record of active subscriptions to ensure proper cleanup """ raise NotImplementedError # endpoint = ( @@ -125,23 +162,37 @@ def get_subscription_list( user_id: str = "-", debug: bool = False, ) -> JSONDict: - """ - Gets a list of subscriptions created by your application for a user. + """Returns a list of subscriptions created by your application for a user. API Reference: https://dev.fitbit.com/build/reference/web-api/subscription/get-subscription-list/ Args: - category: Optional specific data category to list - subscriber_id: Optional subscriber ID from dev.fitbit.com - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + category: Optional specific data category to filter by (e.g., SubscriptionCategory.ACTIVITIES, + SubscriptionCategory.BODY). If omitted, returns all subscriptions. + subscriber_id: Optional subscriber ID from your app settings on dev.fitbit.com + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of subscription details including categories and owner info + JSONDict: List of active subscriptions with their collection types and identifiers + + Raises: + fitbit_client.exceptions.InvalidRequestException: If request parameters are invalid + fitbit_client.exceptions.AuthorizationException: If authentication fails + fitbit_client.exceptions.InsufficientScopeException: If missing scopes for requested categories Note: - For best practice, maintain this list in your application and only use - this endpoint periodically to ensure data consistency. + For best practice, maintain subscription information in your own database + and only use this endpoint periodically to ensure data consistency. + + Each subscription requires the appropriate OAuth scope for that category: + - activities: activity scope + - body: weight scope + - foods: nutrition scope + - sleep: sleep scope + + This endpoint returns all subscriptions for a user across all applications + associated with your subscriber ID. """ endpoint = ( f"{category.value}/apiSubscriptions.json" if category else "apiSubscriptions.json" diff --git a/fitbit_client/resources/temperature.py b/fitbit_client/resources/temperature.py index 507c936..01f44b9 100644 --- a/fitbit_client/resources/temperature.py +++ b/fitbit_client/resources/temperature.py @@ -11,39 +11,56 @@ class TemperatureResource(BaseResource): - """ - Handles Fitbit Temperature API endpoints for retrieving both manually logged - core temperature data and automatically measured skin temperature data. + """Provides access to Fitbit Temperature API for retrieving temperature measurements. + + This resource handles endpoints for retrieving two types of temperature data: + 1. Core temperature: Manually logged by users (e.g., using a thermometer) + 2. Skin temperature: Automatically measured during sleep by compatible Fitbit devices - Core temperature data is manually logged by users, while skin temperature is - measured during sleep periods using either dedicated temperature sensors or - other device sensors. + The API provides methods to retrieve data for single dates or date ranges. + Temperature data is useful for tracking fever, monitoring menstrual cycles, + and identifying potential health changes. API Reference: https://dev.fitbit.com/build/reference/web-api/temperature/ + + Required Scopes: + - temperature (for all temperature endpoints) + + Note: + - Core temperature is in absolute values (e.g., 37.0°C) + - Skin temperature is reported as variation from baseline (e.g., +0.5°C) + - Temperature units (Celsius vs Fahrenheit) are determined by the Accept-Language header + - Not all Fitbit devices support skin temperature measurements + - Skin temperature measurements require at least 3 hours of quality sleep """ @validate_date_param(field_name="date") def get_temperature_core_summary_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get core temperature summary data for a single date. Temperature - (Core) data applies specifically to data logged manually by the user throughout - the day. + """Returns core temperature summary data for a single date. + + This endpoint retrieves temperature data that was manually logged by the user + on the specified date, typically using a thermometer. API Reference: https://dev.fitbit.com/build/reference/web-api/temperature/get-temperature-core-summary-by-date Args: date: Date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Core temperature measurements manually logged by the user for the date. - Values are in Celsius or Fahrenheit based on Accept-Language header. + JSONDict: Core temperature measurements containing date, time and temperature values Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid + + Note: + - Temperature values are in Celsius or Fahrenheit based on the Accept-Language header + - Core temperature is the body's internal temperature, not skin temperature + - Normal core temperature range is typically 36.5°C to 37.5°C (97.7°F to 99.5°F) + - If no temperature was logged for the date, an empty array is returned """ result = self._make_request(f"temp/core/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -52,29 +69,33 @@ def get_temperature_core_summary_by_date( def get_temperature_core_summary_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get core temperature data for a date range. Temperature (Core) data applies - specifically to data logged manually by the user on a given day. Maximum date - range cannot exceed 30 days. + """Returns core temperature data for a specified date range. + + This endpoint retrieves temperature data that was manually logged by the user + across the specified date range, typically using a thermometer. API Reference: https://dev.fitbit.com/build/reference/web-api/temperature/get-temperature-core-summary-by-interval Args: start_date: Start date in YYYY-MM-DD format or "today" end_date: End date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Core temperature measurements for each date in the range. Values use units - that correspond to the Accept-Language header provided. + JSONDict: Core temperature measurements for each date in the range with time and temperature values Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date or date range exceeds 30 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date or + date range exceeds 30 days Note: - Maximum date range is 30 days + - Maximum date range is 30 days + - Temperature values are in Celsius or Fahrenheit based on the Accept-Language header + - Days with no logged temperature data will not appear in the results + - Multiple temperature entries on the same day will all be included + - The datetime field includes the specific time the measurement was logged """ result = self._make_request( f"temp/core/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug @@ -85,31 +106,34 @@ def get_temperature_core_summary_by_interval( def get_temperature_skin_summary_by_date( self, date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get skin temperature data for a single date. Temperature (Skin) data applies - specifically to a user's "main sleep," which is the longest single period of time - asleep on a given date. + """Returns skin temperature data for a single date. + + This endpoint retrieves skin temperature data that was automatically measured during + the user's main sleep period (longest sleep) on the specified date. Skin temperature + is reported as variation from the user's baseline, not absolute temperature. API Reference: https://dev.fitbit.com/build/reference/web-api/temperature/get-temperature-skin-summary-by-date Args: date: Date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Skin temperature measurements from the user's main sleep period. Values are relative - to baseline temperature in Celsius or Fahrenheit based on Accept-Language header. + JSONDict: Skin temperature measurements containing date and nightly relative values Raises: - InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateException: If date format is invalid Note: - - Requires at least 3 hours of quality sleep + - Requires compatible Fitbit device with skin temperature measurement capability + - Values are relative to the user's baseline (e.g., +0.5°C, -0.2°C) + - Requires at least 3 hours of quality sleep for measurement - Data typically spans two dates since it's measured during overnight sleep - Takes ~15 minutes after device sync for data to be available - - Requires minimal movement during sleep for accurate measurements - The data returned usually reflects a sleep period that began the day before + - Significant temperature variations may indicate illness, menstrual cycle changes, + or changes in sleeping environment """ result = self._make_request(f"temp/skin/date/{date}.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -118,34 +142,36 @@ def get_temperature_skin_summary_by_date( def get_temperature_skin_summary_by_interval( self, start_date: str, end_date: str, user_id: str = "-", debug: bool = False ) -> JSONDict: - """ - Get skin temperature data for a date range. It only returns a value for dates - on which the Fitbit device was able to record Temperature (skin) data and the - maximum date range cannot exceed 30 days. + """Returns skin temperature data for a specified date range. + + This endpoint retrieves skin temperature data that was automatically measured during + the user's main sleep periods across the specified date range. It only returns values + for dates when the Fitbit device successfully recorded skin temperature data. API Reference: https://dev.fitbit.com/build/reference/web-api/temperature/get-temperature-skin-summary-by-interval Args: start_date: Start date in YYYY-MM-DD format or "today" end_date: End date in YYYY-MM-DD format or "today" - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Skin temperature measurements for the main sleep period of each date. - Only includes dates where valid measurements were taken. Values use units - that correspond to the Accept-Language header provided. + JSONDict: Skin temperature measurements for each date in the range with nightly relative values Raises: - InvalidDateException: If date format is invalid - InvalidDateRangeException: If start_date is after end_date or date range exceeds 30 days + fitbit_client.exceptions.InvalidDateException: If date format is invalid + fitbit_client.exceptions.InvalidDateRangeException: If start_date is after end_date or + date range exceeds 30 days Note: - Maximum date range is 30 days + - Values are relative to the user's baseline (e.g., +0.5°C, -0.2°C) + - Days without valid measurements will not appear in the results - Data typically spans two dates since it's measured during overnight sleep - - Requires at least 3 hours of quality sleep - - Takes ~15 minutes after device sync for data to be available - - The data returned usually reflects sleep periods that began the day before + - The "nightlyRelative" value shows how the measured temperature differs from + the user's baseline, which is calculated from approximately 30 days of data + - Tracking trends over time can be more informative than individual readings """ result = self._make_request( f"temp/skin/date/{start_date}/{end_date}.json", user_id=user_id, debug=debug diff --git a/fitbit_client/resources/user.py b/fitbit_client/resources/user.py index b16e52a..2a6efaf 100644 --- a/fitbit_client/resources/user.py +++ b/fitbit_client/resources/user.py @@ -14,32 +14,65 @@ class UserResource(BaseResource): - """ - Handles Fitbit User API endpoints for managing user profile information, - regional/language settings, and achievement badges. + """Provides access to Fitbit User API for managing profile and badge information. - Scope: profile + This resource handles endpoints for retrieving and updating user profile information, + including personal details, regional/language settings, measurement preferences, and + achievement badges. It allows applications to personalize user experiences and display + user accomplishments. API Reference: https://dev.fitbit.com/build/reference/web-api/user/ + + Required Scopes: + - profile: Required for basic profile information access and updates + - location: Required for accessing and updating regional settings (country, state, city) + - nutrition: Required for updating food preferences (foods locale, water units) + + Note: + The User API contains core user information that affects how data is displayed across + the entire Fitbit platform. Settings such as measurement units, locale preferences, + and timezone determine how data is formatted in all other API responses. + + Access to other users' profile information is subject to their privacy settings, + particularly the "Personal Info" privacy setting, which must be set to either + "Friends" or "Public" to allow access. + + While the profile endpoint requires minimal scope, updating some profile fields + (like location and food preferences) requires additional scopes. """ def get_profile(self, user_id: str = "-", debug: bool = False) -> JSONDict: - """ - Get user profile information. + """Returns a user's profile information. + + This endpoint retrieves detailed information about a user's profile, including + personal details, preferences, and settings. This data can be used to personalize + the application experience and ensure correct data formatting. API Reference: https://dev.fitbit.com/build/reference/web-api/user/get-profile/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - User profile data including personal info, preferences, and settings. - Access to other users' data is subject to their privacy settings. + JSONDict: User profile data containing personal information (name, gender, birth date), + activity metrics (height, weight, stride length), and preferences (units, + timezone, locale settings) + + Raises: + fitbit_client.exceptions.AuthorizationException: If required scope is not granted + fitbit_client.exceptions.ForbiddenException: If privacy settings restrict access Note: - Numerical values are returned in units specified by Accept-Language header. - The profile includes all badges visible in the user's badge locker. + Numerical values (height, weight) are returned in units specified by + the Accept-Language header provided during client initialization. + + Access to other users' profile data is subject to their privacy settings. + The "Personal Info" privacy setting must be set to either "Friends" or + "Public" to allow access to other users' profiles. + + Some fields may be missing if they haven't been set by the user or if + privacy settings restrict access to them. """ result = self._make_request("profile.json", user_id=user_id, debug=debug) return cast(JSONDict, result) @@ -72,44 +105,47 @@ def update_profile( debug: bool = False, ) -> JSONDict: """ - Update user profile data. + Updates the user's profile information. API Reference: https://dev.fitbit.com/build/reference/web-api/user/update-profile/ Args: - gender: User's gender identity (MALE/FEMALE/NA) + gender: User's gender identity (Gender.MALE, Gender.FEMALE, or Gender.NA) birthday: Date of birth in YYYY-MM-DD format - height: Height in X.XX format (units based on Accept-Language) + height: Height in X.XX format (units based on Accept-Language header) about_me: Text for "About Me" profile field full_name: User's full name country: Two-character country code (requires location scope) state: Two-character state code, valid only for US (requires location scope) city: City name (requires location scope) - clock_time_display_format: 12 or 24 hour format - start_day_of_week: Whether week starts on Sunday or Monday - locale: Website locale (e.g., en_US, fr_FR) - locale_lang: Language code (xx format, used if locale not specified) - locale_country: Country code (xx format, used if locale not specified) + clock_time_display_format: 12 or 24 hour format (ClockTimeFormat.TWELVE_HOUR + or ClockTimeFormat.TWENTY_FOUR_HOUR) + start_day_of_week: First day of week (StartDayOfWeek.SUNDAY or StartDayOfWeek.MONDAY) + locale: Website locale (e.g., "en_US", "fr_FR") + locale_lang: Language code (e.g., "en", used if locale not specified) + locale_country: Country code (e.g., "US", used if locale not specified) timezone: Timezone (e.g., "America/Los_Angeles") - foods_locale: Food database locale (requires nutrition scope) - glucose_unit: Glucose unit preference (en_US or METRIC) - height_unit: Height unit preference (en_US or METRIC) + foods_locale: Food database locale (e.g., "en_US", requires nutrition scope) + glucose_unit: Glucose unit preference ("en_US" or "METRIC") + height_unit: Height unit preference ("en_US" or "METRIC") water_unit: Water unit preference (requires nutrition scope) - weight_unit: Weight unit preference + weight_unit: Weight unit preference ("en_US" or "METRIC") stride_length_walking: Walking stride length in X.XX format stride_length_running: Running stride length in X.XX format - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - Updated user profile data + JSONDict: Updated user profile data with the same structure as get_profile() Raises: - InvalidDateException: If birthday format is invalid + fitbit_client.exceptions.InvalidDateException: If birthday format is invalid Note: All parameters are optional. Only specified fields will be updated. - Units for numerical values should match Accept-Language header. + Units for numerical values should match the Accept-Language header. + Updating location information (country, state, city) requires the 'location' scope. + Updating food preferences requires the 'nutrition' scope. """ updates = { "gender": gender.value if gender is not None else None, @@ -145,21 +181,27 @@ def update_profile( def get_badges(self, user_id: str = "-", debug: bool = False) -> JSONDict: """ - Get list of user's earned achievement badges. + Returns a list of the user's earned achievement badges. API Reference: https://dev.fitbit.com/build/reference/web-api/user/get-badges/ Args: - user_id: Optional user ID, defaults to current user - debug: If True, a prints a curl command to stdout to help with debugging (default: False) + user_id: Optional user ID, defaults to current user ("-") + debug: If True, prints a curl command to stdout to help with debugging (default: False) Returns: - List of badges earned by the user + JSONDict: Contains categorized lists of badges earned by the user (all badges, daily goal badges, + lifetime achievement badges, and weight goal badges), with detailed information about + each badge including description, achievement date, and visual elements + + Raises: + fitbit_client.exceptions.AuthorizationException: If required profile scope is not granted + fitbit_client.exceptions.ForbiddenException: If privacy settings restrict access Note: Access to badges requires user's "My Achievements" privacy setting to allow access. Weight badges are only included if "My Body" privacy - setting allows access. + setting allows access. Some fields may not be present for all badges. """ result = self._make_request("badges.json", user_id=user_id, debug=debug) return cast(JSONDict, result) diff --git a/tests/resources/test_docstrings.py b/tests/resources/test_docstrings.py index 873025e..28299e4 100644 --- a/tests/resources/test_docstrings.py +++ b/tests/resources/test_docstrings.py @@ -3,12 +3,15 @@ # Standard library imports from inspect import getdoc from inspect import getmembers +from inspect import isclass from inspect import isfunction from re import search from typing import List +from typing import Type # Third party imports from pytest import fail +from pytest import mark # fmt: off # isort: off @@ -44,11 +47,9 @@ def get_public_methods(cls) -> List[str]: ] -def test_api_reference_in_docstrings(): - """Test that every public method in resource modules has an API Reference URL""" - - # Get all resource modules - resources = [ +def get_all_resource_classes() -> List[Type]: + """Return all resource classes for testing""" + return [ ActiveZoneMinutesResource, ActivityResource, ActivityTimeSeriesResource, @@ -72,10 +73,79 @@ def test_api_reference_in_docstrings(): UserResource, ] + +def test_class_docstring_has_api_reference(): + """Test that every resource class docstring includes API Reference URL""" + failures = [] + + for resource_cls in get_all_resource_classes(): + docstring = getdoc(resource_cls) + api_ref_pattern = r"API Reference: https://dev\.fitbit\.com/build/reference/web-api/.+" + + if not docstring or not search(api_ref_pattern, docstring): + failures.append(f"{resource_cls.__name__}") + + if failures: + fail_msg = "The following resource classes are missing API Reference URLs in their class docstrings:\n" + fail_msg += "\n".join(failures) + fail(fail_msg) + + +def test_class_docstring_has_required_scopes(): + """Test that every resource class docstring includes Required Scopes section""" failures = [] - # Check each resource class - for resource_cls in resources: + for resource_cls in get_all_resource_classes(): + docstring = getdoc(resource_cls) + required_scopes_pattern = r"Required Scopes:" + + if not docstring or not search(required_scopes_pattern, docstring): + failures.append(f"{resource_cls.__name__}") + + if failures: + fail_msg = "The following resource classes are missing Required Scopes section in their class docstrings:\n" + fail_msg += "\n".join(failures) + fail(fail_msg) + + +@mark.parametrize("section_name", ["Args:", "Returns:", "Raises:", "API Reference:", "Note:"]) +def test_method_docstring_has_required_section(section_name): + """Test that every public method in resource modules has the required docstring sections""" + failures = [] + + for resource_cls in get_all_resource_classes(): + methods = get_public_methods(resource_cls) + + for method_name in methods: + method = getattr(resource_cls, method_name) + + # Skip aliased methods that share docstrings + if method.__doc__ is None: + continue + + docstring = getdoc(method) + + # Allow NotImplemented methods to skip sections + if docstring and "NOT IMPLEMENTED" in docstring: + continue + + # Check for the specified section + if not docstring or not search(rf"{section_name}", docstring): + failures.append(f"{resource_cls.__name__}.{method_name} missing {section_name}") + + if failures: + fail_msg = ( + f"The following methods are missing the {section_name} section in their docstrings:\n" + ) + fail_msg += "\n".join(failures) + fail(fail_msg) + + +def test_api_reference_in_method_docstrings(): + """Test that every public method in resource modules has an API Reference URL""" + failures = [] + + for resource_cls in get_all_resource_classes(): methods = get_public_methods(resource_cls) for method_name in methods: @@ -100,3 +170,47 @@ def test_api_reference_in_docstrings(): fail_msg = "The following methods are missing API Reference URLs in their docstrings:\n" fail_msg += "\n".join(failures) fail(fail_msg) + + +def test_docstring_sections_order(): + """Test that docstring sections appear in the correct order when present""" + expected_order = ["Args:", "Returns:", "Raises:", "Note:"] + failures = [] + + for resource_cls in get_all_resource_classes(): + methods = get_public_methods(resource_cls) + + for method_name in methods: + method = getattr(resource_cls, method_name) + + # Skip aliased methods that share docstrings + if method.__doc__ is None: + continue + + docstring = getdoc(method) + + # Allow NotImplemented methods to skip checks + if docstring and "NOT IMPLEMENTED" in docstring: + continue + + # Find positions of each section in the docstring + positions = {} + for section in expected_order: + match = search(rf"{section}", docstring) + if match: + positions[section] = match.start() + + # Check if found sections are in the correct order + sections_found = sorted(positions.keys(), key=lambda k: positions[k]) + expected_sections = [s for s in expected_order if s in positions] + + if sections_found != expected_sections: + failures.append( + f"{resource_cls.__name__}.{method_name} has incorrect section order: " + f"found {sections_found}, expected {expected_sections}" + ) + + if failures: + fail_msg = "The following methods have docstring sections in the wrong order:\n" + fail_msg += "\n".join(failures) + fail(fail_msg)