#!/usr/bin/env python3 """ Created on Sun Feb 11 13:55:24 2024 """ import datetime import json import logging import os import sys from getpass import getpass import glob import zipfile import io import readchar import requests from garth.exc import GarthHTTPError 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") tokenstore = os.getenv("GARMINTOKENS") or "~/.garminconnect" tokenstore_base64 = os.getenv("GARMINTOKENS_BASE64") or "~/.garminconnect_base64" api = None files=glob.glob('fit/20*fit') files.sort() startdate=datetime.datetime.strptime(os.path.split(files[-5])[-1].split('T')[0],'%Y-%m-%d') startdate=datetime.date(startdate.year,startdate.month,startdate.day)- datetime.timedelta(days=30) # Example selections and settings today = datetime.date.today() def get_credentials(): """Get user credentials.""" email = input("Login e-mail: ") password = getpass("Enter password: ") return email, password def init_api(email, password): """Initialize Garmin API with your credentials.""" try: # Using Oauth1 and OAuth2 token files from directory print( f"Trying to login to Garmin Connect using token data from directory '{tokenstore}'...\n" ) # Using Oauth1 and Oauth2 tokens from base64 encoded string # print( # f"Trying to login to Garmin Connect using token data from file '{tokenstore_base64}'...\n" # ) # dir_path = os.path.expanduser(tokenstore_base64) # with open(dir_path, "r") as token_file: # tokenstore = token_file.read() garmin = Garmin() garmin.login(tokenstore) except (FileNotFoundError, GarthHTTPError, GarminConnectAuthenticationError): # Session is expired. You'll need to log in again print( "Login tokens not present, login with your Garmin Connect credentials to generate them.\n" f"They will be stored in '{tokenstore}' for future use.\n" ) try: # Ask for credentials if not set as environment variables if not email or not password: email, password = get_credentials() garmin = Garmin(email, password) garmin.login() # Save Oauth1 and Oauth2 token files to directory for next login garmin.garth.dump(tokenstore) print( f"Oauth tokens stored in '{tokenstore}' directory for future use. (first method)\n" ) # Encode Oauth1 and Oauth2 tokens to base64 string and safe to file for next login (alternative way) token_base64 = garmin.garth.dumps() dir_path = os.path.expanduser(tokenstore_base64) with open(dir_path, "w") as token_file: token_file.write(token_base64) print( f"Oauth tokens encoded as base64 string and saved to '{dir_path}' file for future use. (second method)\n" ) except (FileNotFoundError, GarthHTTPError, GarminConnectAuthenticationError, requests.exceptions.HTTPError) as err: logger.error(err) return None return garmin def download(api): """Run selected API call.""" #%% # Skip requests if login failed if api: try: # USER BASICS # 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(), "" ) # Download activities for activity in activities: activity_id = activity["activityId"] # activity_name = activity["activityName"] # display_text(activity) # activity['beginTimestamp'] # activity['startTimeLocal'] tc=activity['startTimeGMT'].replace(' ','T') zip_data = api.download_activity( activity_id, dl_fmt=api.ActivityDownloadFormat.ORIGINAL ) z = zipfile.ZipFile(io.BytesIO(zip_data)) fn=z.infolist()[0].filename.replace('_ACTIVITY','') fit_data = z.read(z.infolist()[0]) nfn=os.path.join('fit',f'{tc}+00:00_{fn}') if os.path.isfile(nfn): print(f'File {nfn} exists already.') else: print(f'File {nfn} does not exist.') with open(nfn, "wb") as fb: fb.write(fit_data) print(f"Activity data downloaded to file {nfn}") except ( GarminConnectConnectionError, GarminConnectAuthenticationError, GarminConnectTooManyRequestsError, requests.exceptions.HTTPError, GarthHTTPError ) as err: logger.error(err) except KeyError: # Invalid menu option chosen pass else: print("Could not login to Garmin Connect, try again later.") #%% # Main program loop if __name__=="__main__": # Display header and login print("\n*** Garmin Connect activity download ***\n") # Init API if not api: api = init_api(email, password) if api: # Display menu # print_menu() # option = readchar.readkey() download(api) else: api = init_api(email, password)