@@ -358,15 +358,24 @@ def login(self, /, tokenstore: str | None = None) -> tuple[str | None, str | Non
358358 )
359359 # Continue to load profile/settings below
360360
361- # Validate profile data exists
362- if not hasattr (self .garth , "profile" ) or not self .garth .profile :
363- raise GarminConnectAuthenticationError ("Failed to retrieve profile" )
364-
365- self .display_name = self .garth .profile .get ("displayName" )
366- self .full_name = self .garth .profile .get ("fullName" )
367-
368- if not self .display_name :
369- raise GarminConnectAuthenticationError ("Invalid profile data found" )
361+ # Ensure profile is loaded (tokenstore path may not populate it)
362+ if not getattr (self .garth , "profile" , None ):
363+ try :
364+ prof = self .garth .connectapi (
365+ "/userprofile-service/userprofile/profile"
366+ )
367+ except Exception as e :
368+ raise GarminConnectAuthenticationError (
369+ "Failed to retrieve profile"
370+ ) from e
371+ if not prof or "displayName" not in prof :
372+ raise GarminConnectAuthenticationError ("Invalid profile data found" )
373+ # Use profile data directly since garth.profile is read-only
374+ self .display_name = prof .get ("displayName" )
375+ self .full_name = prof .get ("fullName" )
376+ else :
377+ self .display_name = self .garth .profile .get ("displayName" )
378+ self .full_name = self .garth .profile .get ("fullName" )
370379
371380 settings = self .garth .connectapi (self .garmin_connect_user_settings_url )
372381
@@ -891,11 +900,10 @@ def get_lactate_threshold(
891900 speed_and_heart_rate_dict ["sequence" ] = entry ["sequence" ]
892901 speed_and_heart_rate_dict ["speed" ] = speed
893902
894- # This is not a typo. The Garmin dictionary has a typo as of 2025-07-08, referring to it as "hearRate"
895- hr = entry .get ("hearRate" )
903+ # Prefer correct key; fall back to Garmin's historical typo ( "hearRate")
904+ hr = entry .get ("heartRate" ) or entry . get ( " hearRate" )
896905 if hr is not None :
897906 speed_and_heart_rate_dict ["heartRate" ] = hr
898- # Fix Garmin's typo
899907
900908 # Doesn't exist for me but adding it just in case. We'll check for each entry
901909 hrc = entry .get ("heartRateCycling" )
@@ -1780,7 +1788,7 @@ def get_gear_defaults(self, userProfileNumber: str) -> dict[str, Any]:
17801788 f"{ self .garmin_connect_gear_baseurl } user/"
17811789 f"{ userProfileNumber } /activityTypes"
17821790 )
1783- logger .debug ("Requesting gear for user %s" , userProfileNumber )
1791+ logger .debug ("Requesting gear defaults for user %s" , userProfileNumber )
17841792 return self .connectapi (url )
17851793
17861794 def set_gear_default (
@@ -1830,7 +1838,7 @@ def download_activity(
18301838 raise ValueError (f"unexpected value { dl_fmt } for dl_fmt" )
18311839 url = urls [dl_fmt ]
18321840
1833- logger .debug ("Downloading activities from %s" , url )
1841+ logger .debug ("Downloading activity from %s" , url )
18341842
18351843 return self .download (url )
18361844
@@ -1970,7 +1978,7 @@ def request_reload(self, cdate: str) -> dict[str, Any]:
19701978 return self .garth .post ("connectapi" , url , api = True ).json ()
19711979
19721980 def get_workouts (self , start : int = 0 , limit : int = 100 ) -> dict [str , Any ]:
1973- """Return workouts from start till end ."""
1981+ """Return workouts starting at offset ` start` with at most `limit` results ."""
19741982
19751983 url = f"{ self .garmin_workouts } /workouts"
19761984 start = _validate_non_negative_integer (start , "start" )
@@ -2012,7 +2020,7 @@ def upload_workout(
20122020 raise ValueError (f"invalid workout_json string: { e } " ) from e
20132021 else :
20142022 payload = workout_json
2015- if not isinstance (payload , dict | list ):
2023+ if not isinstance (payload , ( dict | list ) ):
20162024 raise ValueError ("workout_json must be a JSON object or array" )
20172025 return self .garth .post ("connectapi" , url , json = payload , api = True ).json ()
20182026
0 commit comments