garmsync garmsync
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.5 KiB

11 months ago
  1. import * as fs from "fs/promises";
  2. import { parseString } from "xml2js";
  3. export function generateTrimlogInput(filename, name = "Walk") {
  4. return new Promise(async(resolve, reject) => {
  5. parseString(await fs.readFile(filename, "utf-8"), (err, res) => {
  6. if (err != null) {
  7. reject(err);
  8. }
  9. const data = res.TrainingCenterDatabase.Activities[0].Activity[0];
  10. const map = {};
  11. const date = new Date(res.TrainingCenterDatabase.Activities[0].Activity[0].Id[0]);
  12. const dateStr = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
  13. let totalCalories = 0;
  14. let minAltitude = null;
  15. let maxAltitude = null;
  16. for (const lap of data.Lap) {
  17. totalCalories += parseFloat(lap.Calories[0]) || 0;
  18. for (const tp of lap.Track[0].Trackpoint) {
  19. const point = {
  20. time: tp.Time[0],
  21. longitude: parseFloat(tp.Position?.[0]?.LongitudeDegrees[0]),
  22. latitude: parseFloat(tp.Position?.[0]?.LatitudeDegrees[0]),
  23. altitude: parseFloat(tp.AltitudeMeters[0]),
  24. distance: parseFloat(tp.DistanceMeters[0]),
  25. heartrate: parseFloat(tp.HeartRateBpm?.[0].Value?.[0]),
  26. };
  27. if (minAltitude == null || point.altitude < minAltitude) {
  28. minAltitude = point.altitude;
  29. }
  30. if (maxAltitude == null || point.altitude > maxAltitude) {
  31. maxAltitude = point.altitude;
  32. }
  33. map[tp.Time[0]] = point;
  34. }
  35. }
  36. const out = {
  37. date: dateStr,
  38. contents: [{rawActivity: {
  39. kind: "Walking",
  40. sets: [],
  41. effortScale: 1,
  42. weight: 0,
  43. measurements: Object.keys(map).sort().map(k => map[k]).map(p => ({
  44. seconds: Math.round((new Date(p.time) - date) / 1000),
  45. meters: p.distance,
  46. pulse: p.heartrate,
  47. }))
  48. }}],
  49. description: name,
  50. tags: [
  51. { key: "tcx:Time", value: res.TrainingCenterDatabase.Activities[0].Activity[0].Id[0] },
  52. { key: "tcx:TotalCalories", value: totalCalories.toFixed(1) },
  53. { key: "tcx:MaximumAltitude", value: maxAltitude.toFixed(1) },
  54. { key: "tcx:MinimumAltitude", value: minAltitude.toFixed(1) },
  55. ]
  56. }
  57. resolve(out);
  58. });
  59. })
  60. }
  61. export function today() {
  62. const date = new Date();
  63. return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
  64. }
  65. function pad(n) {return n > 9 ? n.toString() : `0${n}`}
  66. /*
  67. {
  68. "date": "2023-12-31",
  69. "contents": [
  70. {
  71. "rawActivity": {
  72. "kind": "Walking",
  73. "sets": [],
  74. "measurements": [
  75. {
  76. "seconds": 1234,
  77. "calories": 4321,
  78. "meters": 432553
  79. },
  80. {
  81. "seconds": 2468,
  82. "calories": 42342,
  83. "meters": 3424
  84. },
  85. {
  86. "seconds": 3702,
  87. "calories": 23423,
  88. "meters": 2342
  89. },
  90. {
  91. "seconds": 4936,
  92. "calories": 23423,
  93. "meters": 2342
  94. }
  95. ],
  96. "effortScale": 1,
  97. "weight": 0
  98. }
  99. }
  100. ],
  101. "description": "asdfasdfasdf",
  102. "tags": []
  103. }
  104. */