Skip to content

Commit

Permalink
[#436] support syncing strength exercises to garmin when available (#444
Browse files Browse the repository at this point in the history
)

* [#436] support syncing strength exercises to garmin when available

* working :)

* uncomment amrap

* rename setting
  • Loading branch information
philosowaffle committed Feb 25, 2023
1 parent d097b45 commit be57bf6
Show file tree
Hide file tree
Showing 19 changed files with 1,697 additions and 33 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Sync workouts from Peloton to Garmin.
* Earn Badges and credit for Garmin Challenges
* Counts towards VO2 Max [1]({{ site.baseurl }}{% link faq.md %}) and Training Stress Scores
* Supports Garmin accounts protected by Two Step Verification
* Supports mapping Exercises from Strength workouts

Head on over to the [Wiki](https://philosowaffle.github.io/peloton-to-garmin) to get started!

Expand Down
5 changes: 5 additions & 0 deletions docs/configuration/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ This section provides settings related to conversions and what formats should be
},
"Rowing": {
"PreferredLapType": "Class_Segments"
},
"Strength": {
"DefaultSecondsPerRep": 3
}
}
```
Expand All @@ -137,6 +140,8 @@ This section provides settings related to conversions and what formats should be
| Running.PreferredLapType | no | `Default` | `Conversion Tab` | The preferred [lap type to use](#lap-types). |
| Rowing | no | `null` | none | Configuration specific to Rowing workouts. |
| Rowing.PreferredLapType | no | `Default` | `Conversion Tab` | The preferred [lap type to use](#lap-types). |
| Strength | no | `null` | `Conversion Tab` | Configuration specific to Strength workouts. |
| Strength.DefaultSecondsPerRep | no | `3` | `Conversion Tab` | For exercises that are done for time instead of reps, P2G can estimate how many reps you completed using this value. Ex. If `DefaultSecondsPerRep=3` and you do Curls for 15s, P2G will estimate you completed 5 reps. |

### Understanding Custom Zones

Expand Down
1 change: 1 addition & 0 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Convert, Backup, and Sync.
1. Syncs laps and target cadence
1. Synced workouts count towards Garmin Badges and Challenges
1. Synced workouts count towards VO2 Max [1]({{ site.baseurl }}{% link faq.md %}) and Training Stress Scores
1. Syncs Exercise information (including reps and weight) for Strength and Core workouts (when available)
1. Syncs on demand or on a schedule
1. Highly Configurable
1. Docker-ized
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Sync workouts from Peloton to Garmin.
* Earn Badges and credit for Garmin Challenges
* Counts towards VO2 Max [1]({{ site.baseurl }}{% link faq.md %}) and Training Stress Scores
* Supports Garmin accounts protected by Two Step Verification
* Supports mapping Exercises from Strength workouts

Head on over to the [Install]({{ site.baseurl }}{% link install/index.md %}) page to get started!

Expand Down
13 changes: 13 additions & 0 deletions src/Common/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public Format()
Cycling = new Cycling();
Running = new Running();
Rowing = new Rowing();
Strength= new Strength();
}

[DisplayName("FIT")]
Expand All @@ -135,6 +136,7 @@ public Format()
public Cycling Cycling { get; set; }
public Running Running { get; set; }
public Rowing Rowing { get; init; }
public Strength Strength { get; init; }
}

public record Cycling
Expand All @@ -152,6 +154,17 @@ public record Rowing
public PreferredLapType PreferredLapType { get; set; }
}

public record Strength
{
/// <summary>
/// When no Rep information is provided by Peloton, P2G will calculate number
/// of reps based on this default value. Example, if your DefaultNumSecondsPerRep is 3,
/// and the Exercise duration was 15 seconds, then P2G would credit you with 5 reps for that
/// exercise.
/// </summary>
public int DefaultSecondsPerRep { get; set; } = 3;
}

public enum PreferredLapType
{
Default = 0,
Expand Down
64 changes: 64 additions & 0 deletions src/Common/Dto/Peloton/CompletedMovementsSummaryData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Common.Dto.Peloton;

public record MovementTrackerData
{
public CompletedMovementsSummaryData Completed_Movements_Summary_Data { get; init; }
}

public record CompletedMovementsSummaryData
{
public ICollection<RepetitionSummaryData> Repetition_Summary_Data { get; init; }
}

public record RepetitionSummaryData
{
public string Movement_Id { get; init; }
public string Movement_Name { get; init; }

/// <summary>
/// True when doing the exercise for Time instead of for Reps
/// </summary>
public bool Is_Hold { get; init; }
public int Target_Number { get; init; }
public int Completed_Number { get; init; }
/// <summary>
/// Seconds offset from start.... I think?
/// </summary>
public int Offset { get; init; }
/// <summary>
/// Length in seconds to complete exercise
/// </summary>
public int Length { get; init; }
/// <summary>
/// Total load of the weight lifted and number of reps
/// </summary>
public int? Total_Volume { get; init; }
public ICollection<Weight> Weight { get; init; }
}

public record Weight
{
[JsonConverter(typeof(JsonStringEnumConverter))]
public WeightCategory Weight_Category { get; init; }
public WeightData Weight_Data { get; init; }
}

public record WeightData
{
public double Weight_Value { get; init; }

/// <summary>
/// lb
/// </summary>
public string Weight_Unit { get; init; }
}

public enum WeightCategory : byte
{
Light = 1,
Medium = 2,
Heavy = 3
}
4 changes: 3 additions & 1 deletion src/Common/Dto/Peloton/Workout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ public record Workout
//public string Device_Type_Display_Name { get; init; }
//public bool Is_Skip_Intro_Available { get; init; }
// total hr zones durations
// average effort score
// average effort score

public MovementTrackerData Movement_Tracker_Data { get; init; }

}

Expand Down
19 changes: 19 additions & 0 deletions src/Common/Dto/Unit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ public enum SpeedUnit : byte
MinutesPer500Meters = 3,
}

public enum WeightUnit : byte
{
Unknown = 0,
Pounds = 1,
Kilograms = 2
}

public static class UnitHelpers
{
public static DistanceUnit GetDistanceUnit(string unit)
Expand Down Expand Up @@ -59,4 +66,16 @@ public static SpeedUnit GetSpeedUnit(string unit)
return SpeedUnit.Unknown;
}
}

public static WeightUnit GetWeightUnit(string unit)
{
switch(unit?.ToLower())
{
case "lb": return WeightUnit.Pounds;
case "kg": return WeightUnit.Kilograms;
default:
Log.Error("Found unknown distance unit {@Unit}", unit);
return WeightUnit.Unknown;
}
}
}
94 changes: 94 additions & 0 deletions src/Conversion/ExerciseMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using Dynastream.Fit;
using System.Collections.Generic;

namespace Conversion;

public static class ExerciseMapping
{
public static readonly Dictionary<string, GarminExercise> StrengthExerciseMappings = new()
{
// A
/* AMRAP */ { "98cde50f696746ff98727d5362229cfb", new (ExerciseCategory.Invalid, 0) },

// B
/* Bent Over Row */ { "d60a1dd8824a49a4926f826b24f3b061", new (ExerciseCategory.Row, RowExerciseName.OneArmBentOverRow) },
/* Bicep Curl */ { "43d404595338443baab306a6589ae7fc", new (ExerciseCategory.Curl, CurlExerciseName.StandingDumbbellBicepsCurl) },
/* Bird Dog */ { "df8e18c5082f408b8490c4adcb0678b5", new (ExerciseCategory.Plank, PlankExerciseName.PlankWithKneeToElbow) },

// C
/* Chest Fly */ { "45207949aa384783b5d71451f7fe1c3d", new (ExerciseCategory.Flye, FlyeExerciseName.DumbbellFlye) },
/* Crunch */ { "61ac0d64602c48fba25af7e5e5dc1f97", new (ExerciseCategory.Crunch, CrunchExerciseName.Crunch) },
/* Concentrated Curl */ { "3695ef0ec2ce484faedc8ce2bfa2819d", new (ExerciseCategory.Curl, CurlExerciseName.SeatedDumbbellBicepsCurl) },

// D
/* Deadlift */ { "cd6046306b2c4c4a8f40e169ec924eb9", new (ExerciseCategory.Deadlift, DeadliftExerciseName.DumbbellDeadlift) },
/* Dumbbell Squat */ { "7d82b59462a54e61926077ded0becae5", new (ExerciseCategory.Squat, SquatExerciseName.DumbbellSquat) },
/* Dumbbell Thruster */ { "5ab0baeebee94d3995cb7f2b0332f430", new (ExerciseCategory.Squat, SquatExerciseName.Thrusters) },

// F
/* Flutter Kick */ { "6091566fa0674afd96a22fcec3ab18ce", new (ExerciseCategory.Crunch, CrunchExerciseName.FlutterKicks) },
/* Forearm Side Plank */ { "1c0403c4d7264d83b1c75d18c8cdac4f", new (ExerciseCategory.Plank, PlankExerciseName.SidePlank) },
/* ForeArm Plank */ { "feb44f24e2b8487b870a35f4501069be", new (ExerciseCategory.Plank, PlankExerciseName.Plank) },
/* Front to Back Lunge */ { "ed18d837c14746c5af38d4fa03b56918", new (ExerciseCategory.Lunge, LungeExerciseName.DumbbellLunge) },

// G
/* Goblet Squat */ { "588e35f7067842979485ff1e4f80df26", new (ExerciseCategory.Squat,SquatExerciseName.GobletSquat) },

// H
/* Hammer Curl */ { "114ce849b47a4fabbaad961188bf4f7d", new (ExerciseCategory.Curl, CurlExerciseName.DumbbellHammerCurl) },
/* High Plank */ { "194cc4f6a88c4abd80afe9bbddb25915", new (ExerciseCategory.Plank, PlankExerciseName.StraightArmPlank) },
/* Hip Bridge */ { "06a504988ace45faabd927af1479f454", new (ExerciseCategory.HipRaise, HipRaiseExerciseName.BridgeWithLegExtension) },
/* Hollow Hold */ { "060174b84e3744e6a19fe4ce80411113", new (ExerciseCategory.Crunch, CrunchExerciseName.HollowRock) },

// L
/* Lateral Lunge */ { "fb63e1ea19264145ae6856eefacbcb22", new (ExerciseCategory.Lunge, LungeExerciseName.SlidingLateralLunge)},

// N
/* Neutral Grip Chest Press */ { "802f10996b5048d08f320d8661f13ee1", new (ExerciseCategory.BenchPress, BenchPressExerciseName.NeutralGripDumbbellBenchPress) },

// O
/* Overhead Carry */ { "12057d5f9e144913a824bcae5706966c", new (ExerciseCategory.Carry, CarryExerciseName.OverheadCarry) },
/* Overhead Extension */ { "f260623343e74d37b165071ee5903199", new (ExerciseCategory.TricepsExtension, TricepsExtensionExerciseName.OverheadDumbbellTricepsExtension) },
/* Overhead Press */ { "ef0279948228409298cd6bf62c5b122c", new (ExerciseCategory.ShoulderPress, ShoulderPressExerciseName.OverheadDumbbellPress) },

// P
/* Push Up */ { "1c4d81ad487849a6995f93e1a6a4b1e4", new (ExerciseCategory.PushUp, PushUpExerciseName.PushUp) },
/* Punches */ { "d56b610f9958400eb4c40d2385f32aaf", new (ExerciseCategory.Invalid, 0) },

// R
/* Russian Twist */ { "5c7b2bc65abc4c44849e2119f1338120", new (ExerciseCategory.Core, CoreExerciseName.RussianTwist) },
/* Reverse Lunge */ { "c430accc3802486a86ad2de9cb8f01cc", new (ExerciseCategory.Lunge, LungeExerciseName.ReverseSlidingLunge) },

// S
///
/* Scissor Kick */ { "f6a10df381004afba2a2b63447d9968f", new (ExerciseCategory.Crunch, CrunchExerciseName.LegLevers) },
/* Shoulder Tap */ { "5b33283433e7479390c0d5fc11722f80", new (ExerciseCategory.Plank, PlankExerciseName.StraightArmPlankWithShoulderTouch) },
/* Skull Crusher */ { "3c72e60de73d43f4b5a774c90dea90cd", new (ExerciseCategory.TricepsExtension, TricepsExtensionExerciseName.DumbbellLyingTricepsExtension) },
/* Snatch */ { "0ddf8f94acfe4c2289aef5a9bf59e8d9", new (ExerciseCategory.OlympicLift, OlympicLiftExerciseName.SingleArmDumbbellSnatch) },
/* Split Squat */ { "28833fd99466476ea273d6b94747e3db", new (ExerciseCategory.Squat, SquatExerciseName.DumbbellSplitSquat) },
///* Squat Jump */ { "", new (ExerciseCategory.Plyo, PlyoExerciseName.DumbbellJumpSquat) },

// T
/* Tricep Kickback */ { "da89d743904640d58e8b3f667f08783c", new (ExerciseCategory.TricepsExtension, TricepsExtensionExerciseName.DumbbellKickback) },
/* Tuck up */ { "3069e7ba28b84005b71c16a3781dda8d", new (ExerciseCategory.SitUp, SitUpExerciseName.BentKneeVUp) },
/* Twisting Mountain Climber */ { "cc70d143627c45e5b64e2cb116619899", new (ExerciseCategory.Plank, PlankExerciseName.CrossBodyMountainClimber) },

// V
/* V-Up */ { "715caba11593427299342c378b444e05", new(ExerciseCategory.SitUp, SitUpExerciseName.VUp) },

// W
/* Wide Grip Bent Over Row */ { "d861cb497fcc4e1cba994b7a949a3bac", new (ExerciseCategory.Row, RowExerciseName.WideGripSeatedCableRow) },
};
}

public record GarminExercise
{
public ushort ExerciseCategory { get; init; }
public ushort ExerciseName { get; init; }

public GarminExercise(ushort exerciseCategory, ushort exerciseName)
{
ExerciseCategory = exerciseCategory;
ExerciseName = exerciseName;
}
}
Loading

0 comments on commit be57bf6

Please sign in to comment.