import os import sqlite3 from datetime import datetime from garminconnect import Garmin from garmin_fit_sdk import Decoder def get_db_path(): """Return path to SQLite database.""" return os.path.join('garmin_data', 'garmin.db') def init_database(db_path): """Initialize SQLite database with required tables.""" os.makedirs(os.path.dirname(db_path), exist_ok=True) with sqlite3.connect(db_path) as conn: cursor = conn.cursor() # Create activities table cursor.execute(''' CREATE TABLE IF NOT EXISTS activities ( id INTEGER PRIMARY KEY, start_time TEXT, end_time TEXT, distance REAL, duration INTEGER, activity_type TEXT, avg_heart_rate INTEGER, max_heart_rate INTEGER, avg_speed REAL, max_speed REAL, calories INTEGER, climb INTEGER, UNIQUE(id) ) ''') # Create records table cursor.execute(''' CREATE TABLE IF NOT EXISTS records ( id INTEGER PRIMARY KEY, activity_id INTEGER, timestamp TEXT, heart_rate INTEGER, cadence INTEGER, speed REAL, altitude REAL, latitude REAL, longitude REAL, power INTEGER, distance REAL, FOREIGN KEY (activity_id) REFERENCES activities (id) ) ''') # Create indexes for better query performance cursor.execute('CREATE INDEX IF NOT EXISTS idx_records_activity ON records(activity_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_records_time ON records(timestamp)') conn.commit() def get_garmin_client(email, password): """Authenticate with Garmin Connect.""" try: client = Garmin(email, password) client.login() return client except Exception as e: print(f"Error authenticating: {e}") return None def download_activities(client): """Download activity list.""" try: return client.get_activities(0, 1) # Get most recent activity except Exception as e: print(f"Error downloading activity list: {e}") return None def download_fit_file(client, activity_id, output_dir): """Download FIT file for activity.""" try: fit_data = client.download_activity(activity_id, dl_fmt=client.ActivityDownloadFormat.ORIGINAL) fit_path = os.path.join(output_dir, f"{activity_id}.fit") os.makedirs(output_dir, exist_ok=True) with open(fit_path, 'wb') as f: f.write(fit_data) return fit_path except Exception as e: print(f"Error downloading FIT file: {e}") return None def extract_fit_data(fit_file_path): """Extract data from FIT file.""" try: decoder = Decoder() messages, errors = decoder.read_fit_file(fit_file_path) return messages, errors except Exception as e: print(f"Error decoding FIT file: {e}") return None, None def save_to_database(db_path, messages): """Save extracted data to SQLite database.""" with sqlite3.connect(db_path) as conn: cursor = conn.cursor() # Extract activity metadata activity_message = next((m for m in messages if m['type'] == 'session'), None) if not activity_message: print("No session message found") return # Insert activity cursor.execute(''' INSERT OR IGNORE INTO activities (id, start_time, end_time, distance, duration, activity_type, avg_heart_rate, max_heart_rate, avg_speed, max_speed, calories, climb) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( activity_message['message']['start_time'], activity_message['message']['end_time'], activity_message['message']['total_distance'], activity_message['message']['total_elapsed_time'], activity_message['message']['sport'], activity_message['message']['avg_heart_rate'], activity_message['message']['max_heart_rate'], activity_message['message']['avg_speed'], activity_message['message']['max_speed'], activity_message['message']['total_calories'], activity_message['message']['total_ascent'] )) # Insert records record_messages = [m for m in messages if m['type'] == 'record'] for message in record_messages: record = message['message'] cursor.execute(''' INSERT INTO records (activity_id, timestamp, heart_rate, cadence, speed, altitude, latitude, longitude, power, distance) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ( activity_message['message']['start_time'], record.get('timestamp'), record.get('heart_rate'), record.get('cadence'), record.get('speed'), record.get('altitude'), record.get('position_lat'), record.get('position_long'), record.get('power'), record.get('distance') )) conn.commit() print(f"Saved {len(record_messages)} records to database") def main(): # Authentication credentials email = os.getenv('GARMIN_EMAIL') password = os.getenv('GARMIN_PASSWORD') if not email or not password: print("Please set GARMIN_EMAIL and GARMIN_PASSWORD environment variables") return # Initialize database db_path = get_db_path() init_database(db_path) # Initialize client client = get_garmin_client(email, password) if not client: return # Get activities activities = download_activities(client) if not activities: return activity_id = activities[0]['activityId'] # Download FIT file fit_path = download_fit_file(client, activity_id, 'garmin_data') if not fit_path: return # Extract data messages, errors = extract_fit_data(fit_path) if errors: print(f"FIT file errors: {errors}") # Save to database save_to_database(db_path, messages) if __name__ == "__main__": main()