Added
- Timezone setting so Strava shows the correct local time (#348). Hevy hands out UTC-only timestamps, so uploads carried no local offset and Garmin could forward them to Strava as raw UTC (a 6am workout showing up as a 3am "Night" workout). Set your timezone in Settings (Profile, an IANA name like
Europe/Berlin) and the uploaded FIT now carries a DST-correctlocal_timestampso the local time travels with the activity. Leave it blank to keep the previous behaviour. - Mappings for 21 previously-unmapped exercises in Hevy's global catalog, plus Walking Lunge (Sandbag), Burpee Broad Jumps and Sled Pull (#292, #294, thanks @OliverKrr).
- Normalized-title fallback so an exercise whose name only differs by formatting still maps instead of falling back to a generic type (#303, thanks @OliverKrr).
- Reverse-proxy sub-path support, so the dashboard can be served under a path prefix (#305, thanks @OliverKrr).
- Every single-workout sync is now recorded in the sync log (#306, thanks @OliverKrr).
- Optional Hevy-webhook sync, staged so a watch merge still wins over a plain upload (#307, thanks @OliverKrr).
- When Replace swaps a watch activity, the replaced copy is now also removed from intervals.icu (#308, thanks @OliverKrr).
- Timestamps on log lines, so container logs show when each sync ran and how long it took (#341, thanks @diogormendes).
Fixed
- Exercise names newer than the bundled FIT enums now resolve through the bundled catalog instead of rendering as a generic type (#329, thanks @alasano).
- Merge mode no longer shows pushed exercises as "Unknown" on Garmin Connect. The set payload now sends confidence 100 for named exercises, which is what Connect needs to render the name while keeping the watch's native metrics (#330, thanks @alasano).
generate_descriptionno longer raises aTypeErroron sets with null reps or weight (bodyweight and cardio) (#309, thanks @gianluca-mazza).- The dashboard heart-rate endpoint no longer crashes when Garmin returns a null daily-HR series for the current day (#327).
- Several exercise-mapping corrections: the generated template map no longer overrides the hand-maintained table, generic entries use the correct sentinel, and Pallof Press, machine chest press, hip abduction and Overhead Dumbbell Lunge now map to their exact FIT matches (#272, #274, #276, #278, #290, thanks @OliverKrr).
- An excluded activity id is never adopted from the Garmin import result (#280, thanks @OliverKrr).
Changed
- The default watch strategy is now
merge, which keeps your recorded activity and its native metrics while pushing in the Hevy exercise names (#332).replaceanddescriberemain available in Settings. fit-toolis no longer capped below 0.9.16. The heart-rate parsing that appeared to break on 0.9.16 was a signature mismatch in our own lenient-string patch, now fixed to work on both the old and new APIs (#337, thanks @diogormendes).
Internal
- Auto-sync now runs as an asyncio task with a clean lifespan shutdown instead of a background
threading.Timer, and the shared sync state and loop were extracted into their own modules (#338, #342, thanks @diogormendes). - HTTP-level test coverage now reaches every route (42/42), with the guard rails on the state-mutating ones mutation-tested (#339, #340, thanks @diogormendes), plus a regression corpus built from a real Hevy history (#334, thanks @OliverKrr).
- Docker image split into builder and runtime stages, self-hosting documented as a first-class path, and an exercise-mapping audit against the FIT SDK (#282, #296, #284, thanks @OliverKrr).
psycopg2-binarymoved to the optionalcloudextra (#335, thanks @diogormendes).