diff options
| author | CoprDistGit <infra@openeuler.org> | 2023-06-20 04:14:03 +0000 |
|---|---|---|
| committer | CoprDistGit <infra@openeuler.org> | 2023-06-20 04:14:03 +0000 |
| commit | d12ce101672d4c5b831c987c73c9098bd9cb117b (patch) | |
| tree | 19bb24941d94bc52190c390c957304a6a3cfb3ef | |
| parent | 9cf293709204805f8288358146da261c7ea0967e (diff) | |
automatic import of python-garminconnectopeneuler20.03
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | python-garminconnect.spec | 1788 | ||||
| -rw-r--r-- | sources | 1 |
3 files changed, 1790 insertions, 0 deletions
@@ -0,0 +1 @@ +/garminconnect-0.1.55.tar.gz diff --git a/python-garminconnect.spec b/python-garminconnect.spec new file mode 100644 index 0000000..8823e3e --- /dev/null +++ b/python-garminconnect.spec @@ -0,0 +1,1788 @@ +%global _empty_manifest_terminate_build 0 +Name: python-garminconnect +Version: 0.1.55 +Release: 1 +Summary: Python 3 API wrapper for Garmin Connect +License: MIT license +URL: https://github.com/cyberjunky/python-garminconnect +Source0: https://mirrors.aliyun.com/pypi/web/packages/87/7f/ff45ac4ed757230bc7945e06c6dfc0f3598ce08d1c431914d58da712d33e/garminconnect-0.1.55.tar.gz +BuildArch: noarch + + +%description +[](https://www.paypal.me/cyberjunkynl/) + +# Python: Garmin Connect + +Python 3 API wrapper for Garmin Connect to get your statistics. + +## About + +This package allows you to request garmin device, activity and health data from your Garmin Connect account. +See <https://connect.garmin.com/> + +## Installation + +```bash +pip3 install garminconnect +``` + +## API Demo Program + +I wrote this for testing and playing with all available/known API calls. +If you run it from the python-garmin connect directory it will use the library code beneath it, so you can develop without reinstalling the package. + +The code also demonstrates how to implement session saving and re-using of the cookies. + +You can set environment variables with your credentials like so, this is optional: + +```bash +export EMAIL=<your garmin email> +export PASSWORD=<your garmin password> +``` + +Install the pre-requisites for the example program (not all are needed for using the library package): + +```bash +pip3 install cloudscraper readchar requests pwinput +``` + +Or you can just run the program and enter your credentials when asked, it will create and save a session file and use that until it's outdated/invalid. + +``` +python3 ./example.py +*** Garmin Connect API Demo by cyberjunky *** + +1 -- Get full name +2 -- Get unit system +3 -- Get activity data for '2023-03-10' +4 -- Get activity data for '2023-03-10' (compatible with garminconnect-ha) +5 -- Get body composition data for '2023-03-10' (compatible with garminconnect-ha) +6 -- Get body composition data for from '2023-03-03' to '2023-03-10' (to be compatible with garminconnect-ha) +7 -- Get stats and body composition data for '2023-03-10' +8 -- Get steps data for '2023-03-10' +9 -- Get heart rate data for '2023-03-10' +0 -- Get training readiness data for '2023-03-10' +- -- Get daily step data for '2023-03-03' to '2023-03-10' +/ -- Get body battery data for '2023-03-03' to '2023-03-10' +! -- Get floors data for '2023-03-03' +? -- Get blood pressure data for '2023-03-03' to '2023-03-10' +. -- Get training status data for '2023-03-10' +a -- Get resting heart rate data for 2023-03-10' +b -- Get hydration data for '2023-03-10' +c -- Get sleep data for '2023-03-10' +d -- Get stress data for '2023-03-10' +e -- Get respiration data for '2023-03-10' +f -- Get SpO2 data for '2023-03-10' +g -- Get max metric data (like vo2MaxValue and fitnessAge) for '2023-03-10' +h -- Get personal record for user +i -- Get earned badges for user +j -- Get adhoc challenges data from start '0' and limit '100' +k -- Get available badge challenges data from '1' and limit '100' +l -- Get badge challenges data from '1' and limit '100' +m -- Get non completed badge challenges data from '1' and limit '100' +n -- Get activities data from start '0' and limit '100' +o -- Get last activity +p -- Download activities data by date from '2023-03-03' to '2023-03-10' +r -- Get all kinds of activities data from '0' +s -- Upload activity data from file 'MY_ACTIVITY.fit' +t -- Get all kinds of Garmin device info +u -- Get active goals +v -- Get future goals +w -- Get past goals +y -- Get all Garmin device alarms +x -- Get Heart Rate Variability data (HRV) for '2023-03-10' +z -- Get progress summary from '2023-03-03' to '2023-03-10' for all metrics +A -- Get gear, the defaults, activity types and statistics +Z -- Logout Garmin Connect portal +q -- Exit + +Make your selection: + +``` + +This is some example code, and probably older than the latest code which can be found in 'example.py'. + +```python +#!/usr/bin/env python3 +""" +pip3 install cloudscraper requests readchar pwinput + +export EMAIL=<your garmin email> +export PASSWORD=<your garmin password> + +""" +import datetime +import json +import logging +import os +import sys + +import requests +import pwinput +import readchar + +from garminconnect import ( + Garmin, + GarminConnectAuthenticationError, + GarminConnectConnectionError, + GarminConnectTooManyRequestsError, +) + +# Configure debug logging +# logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Load environment variables if defined +email = os.getenv("EMAIL") +password = os.getenv("PASSWORD") +api = None + +# Example selections and settings +today = datetime.date.today() +startdate = today - datetime.timedelta(days=7) # Select past week +start = 0 +limit = 100 +start_badge = 1 # Badge related calls calls start counting at 1 +activitytype = "" # Possible values are: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other +activityfile = "MY_ACTIVITY.fit" # Supported file types are: .fit .gpx .tcx + +menu_options = { + "1": "Get full name", + "2": "Get unit system", + "3": f"Get activity data for '{today.isoformat()}'", + "4": f"Get activity data for '{today.isoformat()}' (compatible with garminconnect-ha)", + "5": f"Get body composition data for '{today.isoformat()}' (compatible with garminconnect-ha)", + "6": f"Get body composition data for from '{startdate.isoformat()}' to '{today.isoformat()}' (to be compatible with garminconnect-ha)", + "7": f"Get stats and body composition data for '{today.isoformat()}'", + "8": f"Get steps data for '{today.isoformat()}'", + "9": f"Get heart rate data for '{today.isoformat()}'", + "0": f"Get training readiness data for '{today.isoformat()}'", + "-": f"Get daily step data for '{startdate.isoformat()}' to '{today.isoformat()}'", + "/": f"Get body battery data for '{startdate.isoformat()}' to '{today.isoformat()}'", + "!": f"Get floors data for '{startdate.isoformat()}'", + "?": f"Get blood pressure data for '{startdate.isoformat()}' to '{today.isoformat()}'", + ".": f"Get training status data for '{today.isoformat()}'", + "a": f"Get resting heart rate data for {today.isoformat()}'", + "b": f"Get hydration data for '{today.isoformat()}'", + "c": f"Get sleep data for '{today.isoformat()}'", + "d": f"Get stress data for '{today.isoformat()}'", + "e": f"Get respiration data for '{today.isoformat()}'", + "f": f"Get SpO2 data for '{today.isoformat()}'", + "g": f"Get max metric data (like vo2MaxValue and fitnessAge) for '{today.isoformat()}'", + "h": "Get personal record for user", + "i": "Get earned badges for user", + "j": f"Get adhoc challenges data from start '{start}' and limit '{limit}'", + "k": f"Get available badge challenges data from '{start_badge}' and limit '{limit}'", + "l": f"Get badge challenges data from '{start_badge}' and limit '{limit}'", + "m": f"Get non completed badge challenges data from '{start_badge}' and limit '{limit}'", + "n": f"Get activities data from start '{start}' and limit '{limit}'", + "o": "Get last activity", + "p": f"Download activities data by date from '{startdate.isoformat()}' to '{today.isoformat()}'", + "r": f"Get all kinds of activities data from '{start}'", + "s": f"Upload activity data from file '{activityfile}'", + "t": "Get all kinds of Garmin device info", + "u": "Get active goals", + "v": "Get future goals", + "w": "Get past goals", + "y": "Get all Garmin device alarms", + "x": f"Get Heart Rate Variability data (HRV) for '{today.isoformat()}'", + "z": f"Get progress summary from '{startdate.isoformat()}' to '{today.isoformat()}' for all metrics", + "A": "Get gear, the defaults, activity types and statistics", + "Z": "Logout Garmin Connect portal", + "q": "Exit", +} + +def display_json(api_call, output): + """Format API output for better readability.""" + + dashed = "-"*20 + header = f"{dashed} {api_call} {dashed}" + footer = "-"*len(header) + + print(header) + print(json.dumps(output, indent=4)) + print(footer) + +def display_text(output): + """Format API output for better readability.""" + + dashed = "-"*60 + header = f"{dashed}" + footer = "-"*len(header) + + print(header) + print(json.dumps(output, indent=4)) + print(footer) + +def get_credentials(): + """Get user credentials.""" + email = input("Login e-mail: ") + password = pwinput.pwinput(prompt='Password: ') + + return email, password + + +def init_api(email, password): + """Initialize Garmin API with your credentials.""" + + try: + ## Try to load the previous session + with open("session.json") as f: + saved_session = json.load(f) + + print( + "Login to Garmin Connect using session loaded from 'session.json'...\n" + ) + + # Use the loaded session for initializing the API (without need for credentials) + api = Garmin(session_data=saved_session) + + # Login using the + api.login() + + except (FileNotFoundError, GarminConnectAuthenticationError): + # Login to Garmin Connect portal with credentials since session is invalid or not present. + print( + "Session file not present or turned invalid, login with your Garmin Connect credentials.\n" + "NOTE: Credentials will not be stored, the session cookies will be stored in 'session.json' for future use.\n" + ) + try: + # Ask for credentials if not set as environment variables + if not email or not password: + email, password = get_credentials() + + api = Garmin(email, password) + api.login() + + # Save session dictionary to json file for future use + with open("session.json", "w", encoding="utf-8") as f: + json.dump(api.session_data, f, ensure_ascii=False, indent=4) + except ( + GarminConnectConnectionError, + GarminConnectAuthenticationError, + GarminConnectTooManyRequestsError, + requests.exceptions.HTTPError, + ) as err: + logger.error("Error occurred during Garmin Connect communication: %s", err) + return None + + return api + + +def print_menu(): + """Print examples menu.""" + for key in menu_options.keys(): + print(f"{key} -- {menu_options[key]}") + print("Make your selection: ", end="", flush=True) + + +def switch(api, i): + """Run selected API call.""" + + # Exit example program + if i == "q": + print("Bye!") + sys.exit() + + # Skip requests if login failed + if api: + try: + print(f"\n\nExecuting: {menu_options[i]}\n") + + # USER BASICS + if i == "1": + # Get full name from profile + display_json("api.get_full_name()", api.get_full_name()) + elif i == "2": + # Get unit system from profile + display_json("api.get_unit_system()", api.get_unit_system()) + + # USER STATISTIC SUMMARIES + elif i == "3": + # Get activity data for 'YYYY-MM-DD' + display_json(f"api.get_stats('{today.isoformat()}')", api.get_stats(today.isoformat())) + elif i == "4": + # Get activity data (to be compatible with garminconnect-ha) + display_json(f"api.get_user_summary('{today.isoformat()}')", api.get_user_summary(today.isoformat())) + elif i == "5": + # Get body composition data for 'YYYY-MM-DD' (to be compatible with garminconnect-ha) + display_json(f"api.get_body_composition('{today.isoformat()}')", api.get_body_composition(today.isoformat())) + elif i == "6": + # Get body composition data for multiple days 'YYYY-MM-DD' (to be compatible with garminconnect-ha) + display_json(f"api.get_body_composition('{startdate.isoformat()}', '{today.isoformat()}')", + api.get_body_composition(startdate.isoformat(), today.isoformat()) + ) + elif i == "7": + # Get stats and body composition data for 'YYYY-MM-DD' + display_json(f"api.get_stats_and_body('{today.isoformat()}')", api.get_stats_and_body(today.isoformat())) + + # USER STATISTICS LOGGED + elif i == "8": + # Get steps data for 'YYYY-MM-DD' + display_json(f"api.get_steps_data('{today.isoformat()}')", api.get_steps_data(today.isoformat())) + elif i == "9": + # Get heart rate data for 'YYYY-MM-DD' + display_json(f"api.get_heart_rates('{today.isoformat()}')", api.get_heart_rates(today.isoformat())) + elif i == "0": + # Get training readiness data for 'YYYY-MM-DD' + display_json(f"api.get_training_readiness('{today.isoformat()}')", api.get_training_readiness(today.isoformat())) + elif i == "/": + # Get daily body battery data for 'YYYY-MM-DD' to 'YYYY-MM-DD' + display_json(f"api.get_body_battery('{startdate.isoformat()}, {today.isoformat()}')", api.get_body_battery(startdate.isoformat(), today.isoformat())) + elif i == "?": + # Get daily blood pressure data for 'YYYY-MM-DD' to 'YYYY-MM-DD' + display_json(f"api.get_blood_pressure('{startdate.isoformat()}, {today.isoformat()}')", api.get_blood_pressure(startdate.isoformat(), today.isoformat())) + elif i == "-": + # Get daily step data for 'YYYY-MM-DD' + display_json(f"api.get_daily_steps('{startdate.isoformat()}, {today.isoformat()}')", api.get_daily_steps(startdate.isoformat(), today.isoformat())) + elif i == "!": + # Get daily floors data for 'YYYY-MM-DD' + display_json(f"api.get_floors('{today.isoformat()}')", api.get_floors(today.isoformat())) + elif i == ".": + # Get training status data for 'YYYY-MM-DD' + display_json(f"api.get_training_status('{today.isoformat()}')", api.get_training_status(today.isoformat())) + elif i == "a": + # Get resting heart rate data for 'YYYY-MM-DD' + display_json(f"api.get_rhr_day('{today.isoformat()}')", api.get_rhr_day(today.isoformat())) + elif i == "b": + # Get hydration data 'YYYY-MM-DD' + display_json(f"api.get_hydration_data('{today.isoformat()}')", api.get_hydration_data(today.isoformat())) + elif i == "c": + # Get sleep data for 'YYYY-MM-DD' + display_json(f"api.get_sleep_data('{today.isoformat()}')", api.get_sleep_data(today.isoformat())) + elif i == "d": + # Get stress data for 'YYYY-MM-DD' + display_json(f"api.get_stress_data('{today.isoformat()}')", api.get_stress_data(today.isoformat())) + elif i == "e": + # Get respiration data for 'YYYY-MM-DD' + display_json(f"api.get_respiration_data('{today.isoformat()}')", api.get_respiration_data(today.isoformat())) + elif i == "f": + # Get SpO2 data for 'YYYY-MM-DD' + display_json(f"api.get_spo2_data('{today.isoformat()}')", api.get_spo2_data(today.isoformat())) + elif i == "g": + # Get max metric data (like vo2MaxValue and fitnessAge) for 'YYYY-MM-DD' + display_json(f"api.get_max_metrics('{today.isoformat()}')", api.get_max_metrics(today.isoformat())) + elif i == "h": + # Get personal record for user + display_json("api.get_personal_record()", api.get_personal_record()) + elif i == "i": + # Get earned badges for user + display_json("api.get_earned_badges()", api.get_earned_badges()) + elif i == "j": + # Get adhoc challenges data from start and limit + display_json( + f"api.get_adhoc_challenges({start},{limit})", api.get_adhoc_challenges(start, limit) + ) # 1=start, 100=limit + elif i == "k": + # Get available badge challenges data from start and limit + display_json( + f"api.get_available_badge_challenges({start_badge}, {limit})", api.get_available_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + elif i == "l": + # Get badge challenges data from start and limit + display_json( + f"api.get_badge_challenges({start_badge}, {limit})", api.get_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + elif i == "m": + # Get non completed badge challenges data from start and limit + display_json( + f"api.get_non_completed_badge_challenges({start_badge}, {limit})", api.get_non_completed_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + + # ACTIVITIES + elif i == "n": + # Get activities data from start and limit + display_json(f"api.get_activities({start}, {limit})", api.get_activities(start, limit)) # 0=start, 1=limit + elif i == "o": + # Get last activity + display_json("api.get_last_activity()", api.get_last_activity()) + elif i == "p": + # Get activities data from startdate 'YYYY-MM-DD' to enddate 'YYYY-MM-DD', with (optional) activitytype + # Possible values are: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other + activities = api.get_activities_by_date( + startdate.isoformat(), today.isoformat(), activitytype + ) + + # Download activities + for activity in activities: + + activity_id = activity["activityId"] + display_text(activity) + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.GPX)") + gpx_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.GPX + ) + output_file = f"./{str(activity_id)}.gpx" + with open(output_file, "wb") as fb: + fb.write(gpx_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.TCX)") + tcx_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.TCX + ) + output_file = f"./{str(activity_id)}.tcx" + with open(output_file, "wb") as fb: + fb.write(tcx_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.ORIGINAL)") + zip_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.ORIGINAL + ) + output_file = f"./{str(activity_id)}.zip" + with open(output_file, "wb") as fb: + fb.write(zip_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.CSV)") + csv_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.CSV + ) + output_file = f"./{str(activity_id)}.csv" + with open(output_file, "wb") as fb: + fb.write(csv_data) + print(f"Activity data downloaded to file {output_file}") + + elif i == "r": + # Get activities data from start and limit + activities = api.get_activities(start, limit) # 0=start, 1=limit + + # Get activity splits + first_activity_id = activities[0].get("activityId") + + display_json(f"api.get_activity_splits({first_activity_id})", api.get_activity_splits(first_activity_id)) + + # Get activity split summaries for activity id + display_json(f"api.get_activity_split_summaries({first_activity_id})", api.get_activity_split_summaries(first_activity_id)) + + # Get activity weather data for activity + display_json(f"api.get_activity_weather({first_activity_id})", api.get_activity_weather(first_activity_id)) + + # Get activity hr timezones id + display_json(f"api.get_activity_hr_in_timezones({first_activity_id})", api.get_activity_hr_in_timezones(first_activity_id)) + + # Get activity details for activity id + display_json(f"api.get_activity_details({first_activity_id})", api.get_activity_details(first_activity_id)) + + # Get gear data for activity id + display_json(f"api.get_activity_gear({first_activity_id})", api.get_activity_gear(first_activity_id)) + + # Activity self evaluation data for activity id + display_json(f"api.get_activity_evaluation({first_activity_id})", api.get_activity_evaluation(first_activity_id)) + + # Get exercise sets in case the activity is a strength_training + if activities[0]["activityType"]["typeKey"] == "strength_training": + display_json(f"api.get_activity_exercise_sets({first_activity_id})", api.get_activity_exercise_sets(first_activity_id)) + + elif i == "s": + # Upload activity from file + display_json(f"api.upload_activity({activityfile})", api.upload_activity(activityfile)) + + # DEVICES + elif i == "t": + # Get Garmin devices + devices = api.get_devices() + display_json("api.get_devices()", devices) + + # Get device last used + device_last_used = api.get_device_last_used() + display_json("api.get_device_last_used()", device_last_used) + + # Get settings per device + for device in devices: + device_id = device["deviceId"] + display_json(f"api.get_device_settings({device_id})", api.get_device_settings(device_id)) + + # GOALS + elif i == "u": + # Get active goals + goals = api.get_goals("active") + display_json("api.get_goals(\"active\")", goals) + + elif i == "v": + # Get future goals + goals = api.get_goals("future") + display_json("api.get_goals(\"future\")", goals) + + elif i == "w": + # Get past goals + goals = api.get_goals("past") + display_json("api.get_goals(\"past\")", goals) + + # ALARMS + elif i == "y": + # Get Garmin device alarms + alarms = api.get_device_alarms() + for alarm in alarms: + alarm_id = alarm["alarmId"] + display_json(f"api.get_device_alarms({alarm_id})", alarm) + + elif i == "x": + # Get Heart Rate Variability (hrv) data + display_json(f"api.get_hrv_data({today.isoformat()})", api.get_hrv_data(today.isoformat())) + + elif i == "z": + # Get progress summary + for metric in ["elevationGain", "duration", "distance", "movingDuration"]: + display_json( + f"api.get_progress_summary_between_dates({today.isoformat()})", api.get_progress_summary_between_dates( + startdate.isoformat(), today.isoformat(), metric + )) + + # Gear + elif i == "A": + last_used_device = api.get_device_last_used() + display_json(f"api.get_device_last_used()", last_used_device) + userProfileNumber = last_used_device["userProfileNumber"] + gear = api.get_gear(userProfileNumber) + display_json(f"api.get_gear()", gear) + display_json(f"api.get_gear_defaults()", api.get_gear_defaults(userProfileNumber)) + display_json(f"api.get()", api.get_activity_types()) + for gear in gear: + uuid=gear["uuid"] + name=gear["displayName"] + display_json(f"api.get_gear_stats({uuid}) / {name}", api.get_gear_stats(uuid)) + + elif i == "Z": + # Logout Garmin Connect portal + display_json("api.logout()", api.logout()) + api = None + + except ( + GarminConnectConnectionError, + GarminConnectAuthenticationError, + GarminConnectTooManyRequestsError, + requests.exceptions.HTTPError, + ) as err: + logger.error("Error occurred: %s", err) + except KeyError: + # Invalid menu option chosen + pass + else: + print("Could not login to Garmin Connect, try again later.") + +# Main program loop +while True: + # Display header and login + print("\n*** Garmin Connect API Demo by cyberjunky ***\n") + + # Init API + if not api: + api = init_api(email, password) + + # Display menu + print_menu() + option = readchar.readkey() + switch(api, option) + +``` + +## Donations +[](https://www.paypal.me/cyberjunkynl/) + +%package -n python3-garminconnect +Summary: Python 3 API wrapper for Garmin Connect +Provides: python-garminconnect +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-garminconnect +[](https://www.paypal.me/cyberjunkynl/) + +# Python: Garmin Connect + +Python 3 API wrapper for Garmin Connect to get your statistics. + +## About + +This package allows you to request garmin device, activity and health data from your Garmin Connect account. +See <https://connect.garmin.com/> + +## Installation + +```bash +pip3 install garminconnect +``` + +## API Demo Program + +I wrote this for testing and playing with all available/known API calls. +If you run it from the python-garmin connect directory it will use the library code beneath it, so you can develop without reinstalling the package. + +The code also demonstrates how to implement session saving and re-using of the cookies. + +You can set environment variables with your credentials like so, this is optional: + +```bash +export EMAIL=<your garmin email> +export PASSWORD=<your garmin password> +``` + +Install the pre-requisites for the example program (not all are needed for using the library package): + +```bash +pip3 install cloudscraper readchar requests pwinput +``` + +Or you can just run the program and enter your credentials when asked, it will create and save a session file and use that until it's outdated/invalid. + +``` +python3 ./example.py +*** Garmin Connect API Demo by cyberjunky *** + +1 -- Get full name +2 -- Get unit system +3 -- Get activity data for '2023-03-10' +4 -- Get activity data for '2023-03-10' (compatible with garminconnect-ha) +5 -- Get body composition data for '2023-03-10' (compatible with garminconnect-ha) +6 -- Get body composition data for from '2023-03-03' to '2023-03-10' (to be compatible with garminconnect-ha) +7 -- Get stats and body composition data for '2023-03-10' +8 -- Get steps data for '2023-03-10' +9 -- Get heart rate data for '2023-03-10' +0 -- Get training readiness data for '2023-03-10' +- -- Get daily step data for '2023-03-03' to '2023-03-10' +/ -- Get body battery data for '2023-03-03' to '2023-03-10' +! -- Get floors data for '2023-03-03' +? -- Get blood pressure data for '2023-03-03' to '2023-03-10' +. -- Get training status data for '2023-03-10' +a -- Get resting heart rate data for 2023-03-10' +b -- Get hydration data for '2023-03-10' +c -- Get sleep data for '2023-03-10' +d -- Get stress data for '2023-03-10' +e -- Get respiration data for '2023-03-10' +f -- Get SpO2 data for '2023-03-10' +g -- Get max metric data (like vo2MaxValue and fitnessAge) for '2023-03-10' +h -- Get personal record for user +i -- Get earned badges for user +j -- Get adhoc challenges data from start '0' and limit '100' +k -- Get available badge challenges data from '1' and limit '100' +l -- Get badge challenges data from '1' and limit '100' +m -- Get non completed badge challenges data from '1' and limit '100' +n -- Get activities data from start '0' and limit '100' +o -- Get last activity +p -- Download activities data by date from '2023-03-03' to '2023-03-10' +r -- Get all kinds of activities data from '0' +s -- Upload activity data from file 'MY_ACTIVITY.fit' +t -- Get all kinds of Garmin device info +u -- Get active goals +v -- Get future goals +w -- Get past goals +y -- Get all Garmin device alarms +x -- Get Heart Rate Variability data (HRV) for '2023-03-10' +z -- Get progress summary from '2023-03-03' to '2023-03-10' for all metrics +A -- Get gear, the defaults, activity types and statistics +Z -- Logout Garmin Connect portal +q -- Exit + +Make your selection: + +``` + +This is some example code, and probably older than the latest code which can be found in 'example.py'. + +```python +#!/usr/bin/env python3 +""" +pip3 install cloudscraper requests readchar pwinput + +export EMAIL=<your garmin email> +export PASSWORD=<your garmin password> + +""" +import datetime +import json +import logging +import os +import sys + +import requests +import pwinput +import readchar + +from garminconnect import ( + Garmin, + GarminConnectAuthenticationError, + GarminConnectConnectionError, + GarminConnectTooManyRequestsError, +) + +# Configure debug logging +# logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Load environment variables if defined +email = os.getenv("EMAIL") +password = os.getenv("PASSWORD") +api = None + +# Example selections and settings +today = datetime.date.today() +startdate = today - datetime.timedelta(days=7) # Select past week +start = 0 +limit = 100 +start_badge = 1 # Badge related calls calls start counting at 1 +activitytype = "" # Possible values are: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other +activityfile = "MY_ACTIVITY.fit" # Supported file types are: .fit .gpx .tcx + +menu_options = { + "1": "Get full name", + "2": "Get unit system", + "3": f"Get activity data for '{today.isoformat()}'", + "4": f"Get activity data for '{today.isoformat()}' (compatible with garminconnect-ha)", + "5": f"Get body composition data for '{today.isoformat()}' (compatible with garminconnect-ha)", + "6": f"Get body composition data for from '{startdate.isoformat()}' to '{today.isoformat()}' (to be compatible with garminconnect-ha)", + "7": f"Get stats and body composition data for '{today.isoformat()}'", + "8": f"Get steps data for '{today.isoformat()}'", + "9": f"Get heart rate data for '{today.isoformat()}'", + "0": f"Get training readiness data for '{today.isoformat()}'", + "-": f"Get daily step data for '{startdate.isoformat()}' to '{today.isoformat()}'", + "/": f"Get body battery data for '{startdate.isoformat()}' to '{today.isoformat()}'", + "!": f"Get floors data for '{startdate.isoformat()}'", + "?": f"Get blood pressure data for '{startdate.isoformat()}' to '{today.isoformat()}'", + ".": f"Get training status data for '{today.isoformat()}'", + "a": f"Get resting heart rate data for {today.isoformat()}'", + "b": f"Get hydration data for '{today.isoformat()}'", + "c": f"Get sleep data for '{today.isoformat()}'", + "d": f"Get stress data for '{today.isoformat()}'", + "e": f"Get respiration data for '{today.isoformat()}'", + "f": f"Get SpO2 data for '{today.isoformat()}'", + "g": f"Get max metric data (like vo2MaxValue and fitnessAge) for '{today.isoformat()}'", + "h": "Get personal record for user", + "i": "Get earned badges for user", + "j": f"Get adhoc challenges data from start '{start}' and limit '{limit}'", + "k": f"Get available badge challenges data from '{start_badge}' and limit '{limit}'", + "l": f"Get badge challenges data from '{start_badge}' and limit '{limit}'", + "m": f"Get non completed badge challenges data from '{start_badge}' and limit '{limit}'", + "n": f"Get activities data from start '{start}' and limit '{limit}'", + "o": "Get last activity", + "p": f"Download activities data by date from '{startdate.isoformat()}' to '{today.isoformat()}'", + "r": f"Get all kinds of activities data from '{start}'", + "s": f"Upload activity data from file '{activityfile}'", + "t": "Get all kinds of Garmin device info", + "u": "Get active goals", + "v": "Get future goals", + "w": "Get past goals", + "y": "Get all Garmin device alarms", + "x": f"Get Heart Rate Variability data (HRV) for '{today.isoformat()}'", + "z": f"Get progress summary from '{startdate.isoformat()}' to '{today.isoformat()}' for all metrics", + "A": "Get gear, the defaults, activity types and statistics", + "Z": "Logout Garmin Connect portal", + "q": "Exit", +} + +def display_json(api_call, output): + """Format API output for better readability.""" + + dashed = "-"*20 + header = f"{dashed} {api_call} {dashed}" + footer = "-"*len(header) + + print(header) + print(json.dumps(output, indent=4)) + print(footer) + +def display_text(output): + """Format API output for better readability.""" + + dashed = "-"*60 + header = f"{dashed}" + footer = "-"*len(header) + + print(header) + print(json.dumps(output, indent=4)) + print(footer) + +def get_credentials(): + """Get user credentials.""" + email = input("Login e-mail: ") + password = pwinput.pwinput(prompt='Password: ') + + return email, password + + +def init_api(email, password): + """Initialize Garmin API with your credentials.""" + + try: + ## Try to load the previous session + with open("session.json") as f: + saved_session = json.load(f) + + print( + "Login to Garmin Connect using session loaded from 'session.json'...\n" + ) + + # Use the loaded session for initializing the API (without need for credentials) + api = Garmin(session_data=saved_session) + + # Login using the + api.login() + + except (FileNotFoundError, GarminConnectAuthenticationError): + # Login to Garmin Connect portal with credentials since session is invalid or not present. + print( + "Session file not present or turned invalid, login with your Garmin Connect credentials.\n" + "NOTE: Credentials will not be stored, the session cookies will be stored in 'session.json' for future use.\n" + ) + try: + # Ask for credentials if not set as environment variables + if not email or not password: + email, password = get_credentials() + + api = Garmin(email, password) + api.login() + + # Save session dictionary to json file for future use + with open("session.json", "w", encoding="utf-8") as f: + json.dump(api.session_data, f, ensure_ascii=False, indent=4) + except ( + GarminConnectConnectionError, + GarminConnectAuthenticationError, + GarminConnectTooManyRequestsError, + requests.exceptions.HTTPError, + ) as err: + logger.error("Error occurred during Garmin Connect communication: %s", err) + return None + + return api + + +def print_menu(): + """Print examples menu.""" + for key in menu_options.keys(): + print(f"{key} -- {menu_options[key]}") + print("Make your selection: ", end="", flush=True) + + +def switch(api, i): + """Run selected API call.""" + + # Exit example program + if i == "q": + print("Bye!") + sys.exit() + + # Skip requests if login failed + if api: + try: + print(f"\n\nExecuting: {menu_options[i]}\n") + + # USER BASICS + if i == "1": + # Get full name from profile + display_json("api.get_full_name()", api.get_full_name()) + elif i == "2": + # Get unit system from profile + display_json("api.get_unit_system()", api.get_unit_system()) + + # USER STATISTIC SUMMARIES + elif i == "3": + # Get activity data for 'YYYY-MM-DD' + display_json(f"api.get_stats('{today.isoformat()}')", api.get_stats(today.isoformat())) + elif i == "4": + # Get activity data (to be compatible with garminconnect-ha) + display_json(f"api.get_user_summary('{today.isoformat()}')", api.get_user_summary(today.isoformat())) + elif i == "5": + # Get body composition data for 'YYYY-MM-DD' (to be compatible with garminconnect-ha) + display_json(f"api.get_body_composition('{today.isoformat()}')", api.get_body_composition(today.isoformat())) + elif i == "6": + # Get body composition data for multiple days 'YYYY-MM-DD' (to be compatible with garminconnect-ha) + display_json(f"api.get_body_composition('{startdate.isoformat()}', '{today.isoformat()}')", + api.get_body_composition(startdate.isoformat(), today.isoformat()) + ) + elif i == "7": + # Get stats and body composition data for 'YYYY-MM-DD' + display_json(f"api.get_stats_and_body('{today.isoformat()}')", api.get_stats_and_body(today.isoformat())) + + # USER STATISTICS LOGGED + elif i == "8": + # Get steps data for 'YYYY-MM-DD' + display_json(f"api.get_steps_data('{today.isoformat()}')", api.get_steps_data(today.isoformat())) + elif i == "9": + # Get heart rate data for 'YYYY-MM-DD' + display_json(f"api.get_heart_rates('{today.isoformat()}')", api.get_heart_rates(today.isoformat())) + elif i == "0": + # Get training readiness data for 'YYYY-MM-DD' + display_json(f"api.get_training_readiness('{today.isoformat()}')", api.get_training_readiness(today.isoformat())) + elif i == "/": + # Get daily body battery data for 'YYYY-MM-DD' to 'YYYY-MM-DD' + display_json(f"api.get_body_battery('{startdate.isoformat()}, {today.isoformat()}')", api.get_body_battery(startdate.isoformat(), today.isoformat())) + elif i == "?": + # Get daily blood pressure data for 'YYYY-MM-DD' to 'YYYY-MM-DD' + display_json(f"api.get_blood_pressure('{startdate.isoformat()}, {today.isoformat()}')", api.get_blood_pressure(startdate.isoformat(), today.isoformat())) + elif i == "-": + # Get daily step data for 'YYYY-MM-DD' + display_json(f"api.get_daily_steps('{startdate.isoformat()}, {today.isoformat()}')", api.get_daily_steps(startdate.isoformat(), today.isoformat())) + elif i == "!": + # Get daily floors data for 'YYYY-MM-DD' + display_json(f"api.get_floors('{today.isoformat()}')", api.get_floors(today.isoformat())) + elif i == ".": + # Get training status data for 'YYYY-MM-DD' + display_json(f"api.get_training_status('{today.isoformat()}')", api.get_training_status(today.isoformat())) + elif i == "a": + # Get resting heart rate data for 'YYYY-MM-DD' + display_json(f"api.get_rhr_day('{today.isoformat()}')", api.get_rhr_day(today.isoformat())) + elif i == "b": + # Get hydration data 'YYYY-MM-DD' + display_json(f"api.get_hydration_data('{today.isoformat()}')", api.get_hydration_data(today.isoformat())) + elif i == "c": + # Get sleep data for 'YYYY-MM-DD' + display_json(f"api.get_sleep_data('{today.isoformat()}')", api.get_sleep_data(today.isoformat())) + elif i == "d": + # Get stress data for 'YYYY-MM-DD' + display_json(f"api.get_stress_data('{today.isoformat()}')", api.get_stress_data(today.isoformat())) + elif i == "e": + # Get respiration data for 'YYYY-MM-DD' + display_json(f"api.get_respiration_data('{today.isoformat()}')", api.get_respiration_data(today.isoformat())) + elif i == "f": + # Get SpO2 data for 'YYYY-MM-DD' + display_json(f"api.get_spo2_data('{today.isoformat()}')", api.get_spo2_data(today.isoformat())) + elif i == "g": + # Get max metric data (like vo2MaxValue and fitnessAge) for 'YYYY-MM-DD' + display_json(f"api.get_max_metrics('{today.isoformat()}')", api.get_max_metrics(today.isoformat())) + elif i == "h": + # Get personal record for user + display_json("api.get_personal_record()", api.get_personal_record()) + elif i == "i": + # Get earned badges for user + display_json("api.get_earned_badges()", api.get_earned_badges()) + elif i == "j": + # Get adhoc challenges data from start and limit + display_json( + f"api.get_adhoc_challenges({start},{limit})", api.get_adhoc_challenges(start, limit) + ) # 1=start, 100=limit + elif i == "k": + # Get available badge challenges data from start and limit + display_json( + f"api.get_available_badge_challenges({start_badge}, {limit})", api.get_available_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + elif i == "l": + # Get badge challenges data from start and limit + display_json( + f"api.get_badge_challenges({start_badge}, {limit})", api.get_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + elif i == "m": + # Get non completed badge challenges data from start and limit + display_json( + f"api.get_non_completed_badge_challenges({start_badge}, {limit})", api.get_non_completed_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + + # ACTIVITIES + elif i == "n": + # Get activities data from start and limit + display_json(f"api.get_activities({start}, {limit})", api.get_activities(start, limit)) # 0=start, 1=limit + elif i == "o": + # Get last activity + display_json("api.get_last_activity()", api.get_last_activity()) + elif i == "p": + # Get activities data from startdate 'YYYY-MM-DD' to enddate 'YYYY-MM-DD', with (optional) activitytype + # Possible values are: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other + activities = api.get_activities_by_date( + startdate.isoformat(), today.isoformat(), activitytype + ) + + # Download activities + for activity in activities: + + activity_id = activity["activityId"] + display_text(activity) + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.GPX)") + gpx_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.GPX + ) + output_file = f"./{str(activity_id)}.gpx" + with open(output_file, "wb") as fb: + fb.write(gpx_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.TCX)") + tcx_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.TCX + ) + output_file = f"./{str(activity_id)}.tcx" + with open(output_file, "wb") as fb: + fb.write(tcx_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.ORIGINAL)") + zip_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.ORIGINAL + ) + output_file = f"./{str(activity_id)}.zip" + with open(output_file, "wb") as fb: + fb.write(zip_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.CSV)") + csv_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.CSV + ) + output_file = f"./{str(activity_id)}.csv" + with open(output_file, "wb") as fb: + fb.write(csv_data) + print(f"Activity data downloaded to file {output_file}") + + elif i == "r": + # Get activities data from start and limit + activities = api.get_activities(start, limit) # 0=start, 1=limit + + # Get activity splits + first_activity_id = activities[0].get("activityId") + + display_json(f"api.get_activity_splits({first_activity_id})", api.get_activity_splits(first_activity_id)) + + # Get activity split summaries for activity id + display_json(f"api.get_activity_split_summaries({first_activity_id})", api.get_activity_split_summaries(first_activity_id)) + + # Get activity weather data for activity + display_json(f"api.get_activity_weather({first_activity_id})", api.get_activity_weather(first_activity_id)) + + # Get activity hr timezones id + display_json(f"api.get_activity_hr_in_timezones({first_activity_id})", api.get_activity_hr_in_timezones(first_activity_id)) + + # Get activity details for activity id + display_json(f"api.get_activity_details({first_activity_id})", api.get_activity_details(first_activity_id)) + + # Get gear data for activity id + display_json(f"api.get_activity_gear({first_activity_id})", api.get_activity_gear(first_activity_id)) + + # Activity self evaluation data for activity id + display_json(f"api.get_activity_evaluation({first_activity_id})", api.get_activity_evaluation(first_activity_id)) + + # Get exercise sets in case the activity is a strength_training + if activities[0]["activityType"]["typeKey"] == "strength_training": + display_json(f"api.get_activity_exercise_sets({first_activity_id})", api.get_activity_exercise_sets(first_activity_id)) + + elif i == "s": + # Upload activity from file + display_json(f"api.upload_activity({activityfile})", api.upload_activity(activityfile)) + + # DEVICES + elif i == "t": + # Get Garmin devices + devices = api.get_devices() + display_json("api.get_devices()", devices) + + # Get device last used + device_last_used = api.get_device_last_used() + display_json("api.get_device_last_used()", device_last_used) + + # Get settings per device + for device in devices: + device_id = device["deviceId"] + display_json(f"api.get_device_settings({device_id})", api.get_device_settings(device_id)) + + # GOALS + elif i == "u": + # Get active goals + goals = api.get_goals("active") + display_json("api.get_goals(\"active\")", goals) + + elif i == "v": + # Get future goals + goals = api.get_goals("future") + display_json("api.get_goals(\"future\")", goals) + + elif i == "w": + # Get past goals + goals = api.get_goals("past") + display_json("api.get_goals(\"past\")", goals) + + # ALARMS + elif i == "y": + # Get Garmin device alarms + alarms = api.get_device_alarms() + for alarm in alarms: + alarm_id = alarm["alarmId"] + display_json(f"api.get_device_alarms({alarm_id})", alarm) + + elif i == "x": + # Get Heart Rate Variability (hrv) data + display_json(f"api.get_hrv_data({today.isoformat()})", api.get_hrv_data(today.isoformat())) + + elif i == "z": + # Get progress summary + for metric in ["elevationGain", "duration", "distance", "movingDuration"]: + display_json( + f"api.get_progress_summary_between_dates({today.isoformat()})", api.get_progress_summary_between_dates( + startdate.isoformat(), today.isoformat(), metric + )) + + # Gear + elif i == "A": + last_used_device = api.get_device_last_used() + display_json(f"api.get_device_last_used()", last_used_device) + userProfileNumber = last_used_device["userProfileNumber"] + gear = api.get_gear(userProfileNumber) + display_json(f"api.get_gear()", gear) + display_json(f"api.get_gear_defaults()", api.get_gear_defaults(userProfileNumber)) + display_json(f"api.get()", api.get_activity_types()) + for gear in gear: + uuid=gear["uuid"] + name=gear["displayName"] + display_json(f"api.get_gear_stats({uuid}) / {name}", api.get_gear_stats(uuid)) + + elif i == "Z": + # Logout Garmin Connect portal + display_json("api.logout()", api.logout()) + api = None + + except ( + GarminConnectConnectionError, + GarminConnectAuthenticationError, + GarminConnectTooManyRequestsError, + requests.exceptions.HTTPError, + ) as err: + logger.error("Error occurred: %s", err) + except KeyError: + # Invalid menu option chosen + pass + else: + print("Could not login to Garmin Connect, try again later.") + +# Main program loop +while True: + # Display header and login + print("\n*** Garmin Connect API Demo by cyberjunky ***\n") + + # Init API + if not api: + api = init_api(email, password) + + # Display menu + print_menu() + option = readchar.readkey() + switch(api, option) + +``` + +## Donations +[](https://www.paypal.me/cyberjunkynl/) + +%package help +Summary: Development documents and examples for garminconnect +Provides: python3-garminconnect-doc +%description help +[](https://www.paypal.me/cyberjunkynl/) + +# Python: Garmin Connect + +Python 3 API wrapper for Garmin Connect to get your statistics. + +## About + +This package allows you to request garmin device, activity and health data from your Garmin Connect account. +See <https://connect.garmin.com/> + +## Installation + +```bash +pip3 install garminconnect +``` + +## API Demo Program + +I wrote this for testing and playing with all available/known API calls. +If you run it from the python-garmin connect directory it will use the library code beneath it, so you can develop without reinstalling the package. + +The code also demonstrates how to implement session saving and re-using of the cookies. + +You can set environment variables with your credentials like so, this is optional: + +```bash +export EMAIL=<your garmin email> +export PASSWORD=<your garmin password> +``` + +Install the pre-requisites for the example program (not all are needed for using the library package): + +```bash +pip3 install cloudscraper readchar requests pwinput +``` + +Or you can just run the program and enter your credentials when asked, it will create and save a session file and use that until it's outdated/invalid. + +``` +python3 ./example.py +*** Garmin Connect API Demo by cyberjunky *** + +1 -- Get full name +2 -- Get unit system +3 -- Get activity data for '2023-03-10' +4 -- Get activity data for '2023-03-10' (compatible with garminconnect-ha) +5 -- Get body composition data for '2023-03-10' (compatible with garminconnect-ha) +6 -- Get body composition data for from '2023-03-03' to '2023-03-10' (to be compatible with garminconnect-ha) +7 -- Get stats and body composition data for '2023-03-10' +8 -- Get steps data for '2023-03-10' +9 -- Get heart rate data for '2023-03-10' +0 -- Get training readiness data for '2023-03-10' +- -- Get daily step data for '2023-03-03' to '2023-03-10' +/ -- Get body battery data for '2023-03-03' to '2023-03-10' +! -- Get floors data for '2023-03-03' +? -- Get blood pressure data for '2023-03-03' to '2023-03-10' +. -- Get training status data for '2023-03-10' +a -- Get resting heart rate data for 2023-03-10' +b -- Get hydration data for '2023-03-10' +c -- Get sleep data for '2023-03-10' +d -- Get stress data for '2023-03-10' +e -- Get respiration data for '2023-03-10' +f -- Get SpO2 data for '2023-03-10' +g -- Get max metric data (like vo2MaxValue and fitnessAge) for '2023-03-10' +h -- Get personal record for user +i -- Get earned badges for user +j -- Get adhoc challenges data from start '0' and limit '100' +k -- Get available badge challenges data from '1' and limit '100' +l -- Get badge challenges data from '1' and limit '100' +m -- Get non completed badge challenges data from '1' and limit '100' +n -- Get activities data from start '0' and limit '100' +o -- Get last activity +p -- Download activities data by date from '2023-03-03' to '2023-03-10' +r -- Get all kinds of activities data from '0' +s -- Upload activity data from file 'MY_ACTIVITY.fit' +t -- Get all kinds of Garmin device info +u -- Get active goals +v -- Get future goals +w -- Get past goals +y -- Get all Garmin device alarms +x -- Get Heart Rate Variability data (HRV) for '2023-03-10' +z -- Get progress summary from '2023-03-03' to '2023-03-10' for all metrics +A -- Get gear, the defaults, activity types and statistics +Z -- Logout Garmin Connect portal +q -- Exit + +Make your selection: + +``` + +This is some example code, and probably older than the latest code which can be found in 'example.py'. + +```python +#!/usr/bin/env python3 +""" +pip3 install cloudscraper requests readchar pwinput + +export EMAIL=<your garmin email> +export PASSWORD=<your garmin password> + +""" +import datetime +import json +import logging +import os +import sys + +import requests +import pwinput +import readchar + +from garminconnect import ( + Garmin, + GarminConnectAuthenticationError, + GarminConnectConnectionError, + GarminConnectTooManyRequestsError, +) + +# Configure debug logging +# logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Load environment variables if defined +email = os.getenv("EMAIL") +password = os.getenv("PASSWORD") +api = None + +# Example selections and settings +today = datetime.date.today() +startdate = today - datetime.timedelta(days=7) # Select past week +start = 0 +limit = 100 +start_badge = 1 # Badge related calls calls start counting at 1 +activitytype = "" # Possible values are: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other +activityfile = "MY_ACTIVITY.fit" # Supported file types are: .fit .gpx .tcx + +menu_options = { + "1": "Get full name", + "2": "Get unit system", + "3": f"Get activity data for '{today.isoformat()}'", + "4": f"Get activity data for '{today.isoformat()}' (compatible with garminconnect-ha)", + "5": f"Get body composition data for '{today.isoformat()}' (compatible with garminconnect-ha)", + "6": f"Get body composition data for from '{startdate.isoformat()}' to '{today.isoformat()}' (to be compatible with garminconnect-ha)", + "7": f"Get stats and body composition data for '{today.isoformat()}'", + "8": f"Get steps data for '{today.isoformat()}'", + "9": f"Get heart rate data for '{today.isoformat()}'", + "0": f"Get training readiness data for '{today.isoformat()}'", + "-": f"Get daily step data for '{startdate.isoformat()}' to '{today.isoformat()}'", + "/": f"Get body battery data for '{startdate.isoformat()}' to '{today.isoformat()}'", + "!": f"Get floors data for '{startdate.isoformat()}'", + "?": f"Get blood pressure data for '{startdate.isoformat()}' to '{today.isoformat()}'", + ".": f"Get training status data for '{today.isoformat()}'", + "a": f"Get resting heart rate data for {today.isoformat()}'", + "b": f"Get hydration data for '{today.isoformat()}'", + "c": f"Get sleep data for '{today.isoformat()}'", + "d": f"Get stress data for '{today.isoformat()}'", + "e": f"Get respiration data for '{today.isoformat()}'", + "f": f"Get SpO2 data for '{today.isoformat()}'", + "g": f"Get max metric data (like vo2MaxValue and fitnessAge) for '{today.isoformat()}'", + "h": "Get personal record for user", + "i": "Get earned badges for user", + "j": f"Get adhoc challenges data from start '{start}' and limit '{limit}'", + "k": f"Get available badge challenges data from '{start_badge}' and limit '{limit}'", + "l": f"Get badge challenges data from '{start_badge}' and limit '{limit}'", + "m": f"Get non completed badge challenges data from '{start_badge}' and limit '{limit}'", + "n": f"Get activities data from start '{start}' and limit '{limit}'", + "o": "Get last activity", + "p": f"Download activities data by date from '{startdate.isoformat()}' to '{today.isoformat()}'", + "r": f"Get all kinds of activities data from '{start}'", + "s": f"Upload activity data from file '{activityfile}'", + "t": "Get all kinds of Garmin device info", + "u": "Get active goals", + "v": "Get future goals", + "w": "Get past goals", + "y": "Get all Garmin device alarms", + "x": f"Get Heart Rate Variability data (HRV) for '{today.isoformat()}'", + "z": f"Get progress summary from '{startdate.isoformat()}' to '{today.isoformat()}' for all metrics", + "A": "Get gear, the defaults, activity types and statistics", + "Z": "Logout Garmin Connect portal", + "q": "Exit", +} + +def display_json(api_call, output): + """Format API output for better readability.""" + + dashed = "-"*20 + header = f"{dashed} {api_call} {dashed}" + footer = "-"*len(header) + + print(header) + print(json.dumps(output, indent=4)) + print(footer) + +def display_text(output): + """Format API output for better readability.""" + + dashed = "-"*60 + header = f"{dashed}" + footer = "-"*len(header) + + print(header) + print(json.dumps(output, indent=4)) + print(footer) + +def get_credentials(): + """Get user credentials.""" + email = input("Login e-mail: ") + password = pwinput.pwinput(prompt='Password: ') + + return email, password + + +def init_api(email, password): + """Initialize Garmin API with your credentials.""" + + try: + ## Try to load the previous session + with open("session.json") as f: + saved_session = json.load(f) + + print( + "Login to Garmin Connect using session loaded from 'session.json'...\n" + ) + + # Use the loaded session for initializing the API (without need for credentials) + api = Garmin(session_data=saved_session) + + # Login using the + api.login() + + except (FileNotFoundError, GarminConnectAuthenticationError): + # Login to Garmin Connect portal with credentials since session is invalid or not present. + print( + "Session file not present or turned invalid, login with your Garmin Connect credentials.\n" + "NOTE: Credentials will not be stored, the session cookies will be stored in 'session.json' for future use.\n" + ) + try: + # Ask for credentials if not set as environment variables + if not email or not password: + email, password = get_credentials() + + api = Garmin(email, password) + api.login() + + # Save session dictionary to json file for future use + with open("session.json", "w", encoding="utf-8") as f: + json.dump(api.session_data, f, ensure_ascii=False, indent=4) + except ( + GarminConnectConnectionError, + GarminConnectAuthenticationError, + GarminConnectTooManyRequestsError, + requests.exceptions.HTTPError, + ) as err: + logger.error("Error occurred during Garmin Connect communication: %s", err) + return None + + return api + + +def print_menu(): + """Print examples menu.""" + for key in menu_options.keys(): + print(f"{key} -- {menu_options[key]}") + print("Make your selection: ", end="", flush=True) + + +def switch(api, i): + """Run selected API call.""" + + # Exit example program + if i == "q": + print("Bye!") + sys.exit() + + # Skip requests if login failed + if api: + try: + print(f"\n\nExecuting: {menu_options[i]}\n") + + # USER BASICS + if i == "1": + # Get full name from profile + display_json("api.get_full_name()", api.get_full_name()) + elif i == "2": + # Get unit system from profile + display_json("api.get_unit_system()", api.get_unit_system()) + + # USER STATISTIC SUMMARIES + elif i == "3": + # Get activity data for 'YYYY-MM-DD' + display_json(f"api.get_stats('{today.isoformat()}')", api.get_stats(today.isoformat())) + elif i == "4": + # Get activity data (to be compatible with garminconnect-ha) + display_json(f"api.get_user_summary('{today.isoformat()}')", api.get_user_summary(today.isoformat())) + elif i == "5": + # Get body composition data for 'YYYY-MM-DD' (to be compatible with garminconnect-ha) + display_json(f"api.get_body_composition('{today.isoformat()}')", api.get_body_composition(today.isoformat())) + elif i == "6": + # Get body composition data for multiple days 'YYYY-MM-DD' (to be compatible with garminconnect-ha) + display_json(f"api.get_body_composition('{startdate.isoformat()}', '{today.isoformat()}')", + api.get_body_composition(startdate.isoformat(), today.isoformat()) + ) + elif i == "7": + # Get stats and body composition data for 'YYYY-MM-DD' + display_json(f"api.get_stats_and_body('{today.isoformat()}')", api.get_stats_and_body(today.isoformat())) + + # USER STATISTICS LOGGED + elif i == "8": + # Get steps data for 'YYYY-MM-DD' + display_json(f"api.get_steps_data('{today.isoformat()}')", api.get_steps_data(today.isoformat())) + elif i == "9": + # Get heart rate data for 'YYYY-MM-DD' + display_json(f"api.get_heart_rates('{today.isoformat()}')", api.get_heart_rates(today.isoformat())) + elif i == "0": + # Get training readiness data for 'YYYY-MM-DD' + display_json(f"api.get_training_readiness('{today.isoformat()}')", api.get_training_readiness(today.isoformat())) + elif i == "/": + # Get daily body battery data for 'YYYY-MM-DD' to 'YYYY-MM-DD' + display_json(f"api.get_body_battery('{startdate.isoformat()}, {today.isoformat()}')", api.get_body_battery(startdate.isoformat(), today.isoformat())) + elif i == "?": + # Get daily blood pressure data for 'YYYY-MM-DD' to 'YYYY-MM-DD' + display_json(f"api.get_blood_pressure('{startdate.isoformat()}, {today.isoformat()}')", api.get_blood_pressure(startdate.isoformat(), today.isoformat())) + elif i == "-": + # Get daily step data for 'YYYY-MM-DD' + display_json(f"api.get_daily_steps('{startdate.isoformat()}, {today.isoformat()}')", api.get_daily_steps(startdate.isoformat(), today.isoformat())) + elif i == "!": + # Get daily floors data for 'YYYY-MM-DD' + display_json(f"api.get_floors('{today.isoformat()}')", api.get_floors(today.isoformat())) + elif i == ".": + # Get training status data for 'YYYY-MM-DD' + display_json(f"api.get_training_status('{today.isoformat()}')", api.get_training_status(today.isoformat())) + elif i == "a": + # Get resting heart rate data for 'YYYY-MM-DD' + display_json(f"api.get_rhr_day('{today.isoformat()}')", api.get_rhr_day(today.isoformat())) + elif i == "b": + # Get hydration data 'YYYY-MM-DD' + display_json(f"api.get_hydration_data('{today.isoformat()}')", api.get_hydration_data(today.isoformat())) + elif i == "c": + # Get sleep data for 'YYYY-MM-DD' + display_json(f"api.get_sleep_data('{today.isoformat()}')", api.get_sleep_data(today.isoformat())) + elif i == "d": + # Get stress data for 'YYYY-MM-DD' + display_json(f"api.get_stress_data('{today.isoformat()}')", api.get_stress_data(today.isoformat())) + elif i == "e": + # Get respiration data for 'YYYY-MM-DD' + display_json(f"api.get_respiration_data('{today.isoformat()}')", api.get_respiration_data(today.isoformat())) + elif i == "f": + # Get SpO2 data for 'YYYY-MM-DD' + display_json(f"api.get_spo2_data('{today.isoformat()}')", api.get_spo2_data(today.isoformat())) + elif i == "g": + # Get max metric data (like vo2MaxValue and fitnessAge) for 'YYYY-MM-DD' + display_json(f"api.get_max_metrics('{today.isoformat()}')", api.get_max_metrics(today.isoformat())) + elif i == "h": + # Get personal record for user + display_json("api.get_personal_record()", api.get_personal_record()) + elif i == "i": + # Get earned badges for user + display_json("api.get_earned_badges()", api.get_earned_badges()) + elif i == "j": + # Get adhoc challenges data from start and limit + display_json( + f"api.get_adhoc_challenges({start},{limit})", api.get_adhoc_challenges(start, limit) + ) # 1=start, 100=limit + elif i == "k": + # Get available badge challenges data from start and limit + display_json( + f"api.get_available_badge_challenges({start_badge}, {limit})", api.get_available_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + elif i == "l": + # Get badge challenges data from start and limit + display_json( + f"api.get_badge_challenges({start_badge}, {limit})", api.get_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + elif i == "m": + # Get non completed badge challenges data from start and limit + display_json( + f"api.get_non_completed_badge_challenges({start_badge}, {limit})", api.get_non_completed_badge_challenges(start_badge, limit) + ) # 1=start, 100=limit + + # ACTIVITIES + elif i == "n": + # Get activities data from start and limit + display_json(f"api.get_activities({start}, {limit})", api.get_activities(start, limit)) # 0=start, 1=limit + elif i == "o": + # Get last activity + display_json("api.get_last_activity()", api.get_last_activity()) + elif i == "p": + # Get activities data from startdate 'YYYY-MM-DD' to enddate 'YYYY-MM-DD', with (optional) activitytype + # Possible values are: cycling, running, swimming, multi_sport, fitness_equipment, hiking, walking, other + activities = api.get_activities_by_date( + startdate.isoformat(), today.isoformat(), activitytype + ) + + # Download activities + for activity in activities: + + activity_id = activity["activityId"] + display_text(activity) + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.GPX)") + gpx_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.GPX + ) + output_file = f"./{str(activity_id)}.gpx" + with open(output_file, "wb") as fb: + fb.write(gpx_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.TCX)") + tcx_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.TCX + ) + output_file = f"./{str(activity_id)}.tcx" + with open(output_file, "wb") as fb: + fb.write(tcx_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.ORIGINAL)") + zip_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.ORIGINAL + ) + output_file = f"./{str(activity_id)}.zip" + with open(output_file, "wb") as fb: + fb.write(zip_data) + print(f"Activity data downloaded to file {output_file}") + + print(f"api.download_activity({activity_id}, dl_fmt=api.ActivityDownloadFormat.CSV)") + csv_data = api.download_activity( + activity_id, dl_fmt=api.ActivityDownloadFormat.CSV + ) + output_file = f"./{str(activity_id)}.csv" + with open(output_file, "wb") as fb: + fb.write(csv_data) + print(f"Activity data downloaded to file {output_file}") + + elif i == "r": + # Get activities data from start and limit + activities = api.get_activities(start, limit) # 0=start, 1=limit + + # Get activity splits + first_activity_id = activities[0].get("activityId") + + display_json(f"api.get_activity_splits({first_activity_id})", api.get_activity_splits(first_activity_id)) + + # Get activity split summaries for activity id + display_json(f"api.get_activity_split_summaries({first_activity_id})", api.get_activity_split_summaries(first_activity_id)) + + # Get activity weather data for activity + display_json(f"api.get_activity_weather({first_activity_id})", api.get_activity_weather(first_activity_id)) + + # Get activity hr timezones id + display_json(f"api.get_activity_hr_in_timezones({first_activity_id})", api.get_activity_hr_in_timezones(first_activity_id)) + + # Get activity details for activity id + display_json(f"api.get_activity_details({first_activity_id})", api.get_activity_details(first_activity_id)) + + # Get gear data for activity id + display_json(f"api.get_activity_gear({first_activity_id})", api.get_activity_gear(first_activity_id)) + + # Activity self evaluation data for activity id + display_json(f"api.get_activity_evaluation({first_activity_id})", api.get_activity_evaluation(first_activity_id)) + + # Get exercise sets in case the activity is a strength_training + if activities[0]["activityType"]["typeKey"] == "strength_training": + display_json(f"api.get_activity_exercise_sets({first_activity_id})", api.get_activity_exercise_sets(first_activity_id)) + + elif i == "s": + # Upload activity from file + display_json(f"api.upload_activity({activityfile})", api.upload_activity(activityfile)) + + # DEVICES + elif i == "t": + # Get Garmin devices + devices = api.get_devices() + display_json("api.get_devices()", devices) + + # Get device last used + device_last_used = api.get_device_last_used() + display_json("api.get_device_last_used()", device_last_used) + + # Get settings per device + for device in devices: + device_id = device["deviceId"] + display_json(f"api.get_device_settings({device_id})", api.get_device_settings(device_id)) + + # GOALS + elif i == "u": + # Get active goals + goals = api.get_goals("active") + display_json("api.get_goals(\"active\")", goals) + + elif i == "v": + # Get future goals + goals = api.get_goals("future") + display_json("api.get_goals(\"future\")", goals) + + elif i == "w": + # Get past goals + goals = api.get_goals("past") + display_json("api.get_goals(\"past\")", goals) + + # ALARMS + elif i == "y": + # Get Garmin device alarms + alarms = api.get_device_alarms() + for alarm in alarms: + alarm_id = alarm["alarmId"] + display_json(f"api.get_device_alarms({alarm_id})", alarm) + + elif i == "x": + # Get Heart Rate Variability (hrv) data + display_json(f"api.get_hrv_data({today.isoformat()})", api.get_hrv_data(today.isoformat())) + + elif i == "z": + # Get progress summary + for metric in ["elevationGain", "duration", "distance", "movingDuration"]: + display_json( + f"api.get_progress_summary_between_dates({today.isoformat()})", api.get_progress_summary_between_dates( + startdate.isoformat(), today.isoformat(), metric + )) + + # Gear + elif i == "A": + last_used_device = api.get_device_last_used() + display_json(f"api.get_device_last_used()", last_used_device) + userProfileNumber = last_used_device["userProfileNumber"] + gear = api.get_gear(userProfileNumber) + display_json(f"api.get_gear()", gear) + display_json(f"api.get_gear_defaults()", api.get_gear_defaults(userProfileNumber)) + display_json(f"api.get()", api.get_activity_types()) + for gear in gear: + uuid=gear["uuid"] + name=gear["displayName"] + display_json(f"api.get_gear_stats({uuid}) / {name}", api.get_gear_stats(uuid)) + + elif i == "Z": + # Logout Garmin Connect portal + display_json("api.logout()", api.logout()) + api = None + + except ( + GarminConnectConnectionError, + GarminConnectAuthenticationError, + GarminConnectTooManyRequestsError, + requests.exceptions.HTTPError, + ) as err: + logger.error("Error occurred: %s", err) + except KeyError: + # Invalid menu option chosen + pass + else: + print("Could not login to Garmin Connect, try again later.") + +# Main program loop +while True: + # Display header and login + print("\n*** Garmin Connect API Demo by cyberjunky ***\n") + + # Init API + if not api: + api = init_api(email, password) + + # Display menu + print_menu() + option = readchar.readkey() + switch(api, option) + +``` + +## Donations +[](https://www.paypal.me/cyberjunkynl/) + +%prep +%autosetup -n garminconnect-0.1.55 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "\"/%h/%f\"\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "\"/%h/%f.gz\"\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-garminconnect -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Tue Jun 20 2023 Python_Bot <Python_Bot@openeuler.org> - 0.1.55-1 +- Package Spec generated @@ -0,0 +1 @@ +975f73227bdcb17d1613743448c7eada garminconnect-0.1.55.tar.gz |
