From dd3873eeacb9b4e1fb452debf749b8cf7ae09784 Mon Sep 17 00:00:00 2001 From: Lubo Molent <23703392+lmolent@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:16:47 +0100 Subject: [PATCH 1/4] Improve authentication retries and backoff logic --- scripts/sync_garmin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/sync_garmin.py b/scripts/sync_garmin.py index 61bb209..9b55494 100644 --- a/scripts/sync_garmin.py +++ b/scripts/sync_garmin.py @@ -84,7 +84,7 @@ def authenticate() -> Garmin: tokenstore = str(TOKEN_DIR) last_exc: Exception | None = None - for attempt in range(3): + for attempt in range(5): try: client.login(tokenstore) return client @@ -98,8 +98,8 @@ def authenticate() -> Garmin: sys.exit(1) except Exception as e: last_exc = e - if attempt < 2 and "no profile" in str(e).lower(): - time.sleep(2**attempt) + if attempt < 4 and "no profile" in str(e).lower(): + time.sleep(2 * (attempt + 1)) continue break From fc4b0f34cca490c5b321be79e277dd0ac80d9eb6 Mon Sep 17 00:00:00 2001 From: Lubo Molent <23703392+lmolent@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:16:58 +0100 Subject: [PATCH 2/4] Enhance verbose mode with raw sleep data logging --- scripts/sync_garmin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/sync_garmin.py b/scripts/sync_garmin.py index 9b55494..60acedb 100644 --- a/scripts/sync_garmin.py +++ b/scripts/sync_garmin.py @@ -151,6 +151,10 @@ def fetch_sleep(client: Garmin, day: str) -> str | None: print(f" [verbose] Sleep fetch failed: {e}", file=sys.stderr) return None + if VERBOSE: + import json + print(f" [verbose] Raw Sleep Data for {day}: {json.dumps(data, indent=2)}", file=sys.stderr) + daily = data.get("dailySleepDTO", {}) if not daily or not daily.get("sleepTimeSeconds"): return None From 9e1a945d89280d57105aca5c6f04dcc41bf88587 Mon Sep 17 00:00:00 2001 From: Lubo Molent <23703392+lmolent@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:18:18 +0100 Subject: [PATCH 3/4] Add activity start times and formatting improvements --- scripts/sync_garmin.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/scripts/sync_garmin.py b/scripts/sync_garmin.py index 60acedb..14e79ce 100644 --- a/scripts/sync_garmin.py +++ b/scripts/sync_garmin.py @@ -479,7 +479,40 @@ def fetch_activities(client: Garmin, day: str) -> str | None: for act in activities: name = act.get("activityName", "Activity") duration = fmt_duration_mmss(act.get("duration")) - header_parts = [f"**{name}** — {duration}"] + + # Activity start time (local) if available + start_local = act.get("startTimeLocal") or act.get("startTimeGMT") + start_hm = None + if isinstance(start_local, str): + # e.g. 2026-02-19T18:12:34.0 or 2026-02-19 18:12:34 + if "T" in start_local: + try: + start_hm = start_local.split("T", 1)[1][:5] + except Exception: + start_hm = None + elif " " in start_local: + try: + start_hm = start_local.split(" ", 1)[1][:5] + except Exception: + start_hm = None + + # Fallback: beginTimestamp (ms since epoch) + if start_hm is None: + ts = act.get("beginTimestamp") + if isinstance(ts, (int, float)) and ts > 0: + try: + from datetime import datetime + + start_hm = datetime.fromtimestamp(ts / 1000).strftime("%H:%M") + except Exception: + start_hm = None + + header = f"**{name}**" + if start_hm: + header += f" ({start_hm})" + header += f" — {duration}" + + header_parts = [header] distance = act.get("distance") if distance and distance > 0: From 81f1e1797895dd1931ffaffe685c571374330940 Mon Sep 17 00:00:00 2001 From: Lubo Molent <23703392+lmolent@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:25:30 +0100 Subject: [PATCH 4/4] Update README with verbose mode and activity formatting documentation --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 908c9b3..a163379 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ SpO2: 94.0% Moderate: 69 | Vigorous: 158 | Goal: 150 ## Activities -- **5K Run** — 28:15, 5.0 km, 320 cal +- **5K Run** (18:12) — 28:15, 5.0 km, 320 cal Avg HR 155 / Max 172 | Elevation: +45m | Pace: 5:39/km | Cadence: 168 spm | Training Effect: 3.2 aerobic | VO2 Max: 50 ``` @@ -74,6 +74,9 @@ After setup succeeds, the password is no longer needed. All subsequent syncs use # Sync today (no credentials needed — uses cached tokens) uv run scripts/sync_garmin.py +# Sync today with verbose logging (shows raw data and fetch errors) +uv run scripts/sync_garmin.py --verbose + # Sync a specific date uv run scripts/sync_garmin.py --date 2025-01-26