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.

85 lines
2.9 KiB

11 months ago
11 months ago
8 months ago
10 months ago
11 months ago
8 months ago
11 months ago
11 months ago
8 months ago
11 months ago
10 months ago
8 months ago
11 months ago
  1. import * as fs from "fs/promises";
  2. import { parseString } from "xml2js";
  3. export function generateTrimlogInput(filename, name = "Walk", type = "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. pulse: 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 list = Object.keys(map).sort().map(k => map[k]);
  37. const firstLon = list.find(e => !!e.longitude)?.longitude;
  38. const firstLat = list.find(e => !!e.latitude)?.latitude;
  39. const firstAlt = list.find(e => !!e.altitude)?.altitude;
  40. const out = {
  41. date: dateStr,
  42. contents: [{rawActivity: {
  43. kind: type === "cycling" ? "Biking" : "Walking",
  44. sets: [],
  45. effortScale: 1,
  46. weight: 0,
  47. measurements: list.map(p => ({
  48. seconds: Math.round((new Date(p.time) - date) / 1000),
  49. meters: p.distance,
  50. pulse: p.pulse,
  51. longitude: p.longitude || firstLon,
  52. latitude: p.latitude || firstLat,
  53. altitude: p.altitude || firstAlt,
  54. }))
  55. }}],
  56. description: name,
  57. tags: [
  58. { key: "tcx:Time", value: res.TrainingCenterDatabase.Activities[0].Activity[0].Id[0] },
  59. { key: "tcx:TotalCalories", value: totalCalories.toFixed(1) },
  60. { key: "tcx:MaximumAltitude", value: maxAltitude.toFixed(1) },
  61. { key: "tcx:MinimumAltitude", value: minAltitude.toFixed(1) },
  62. ]
  63. }
  64. resolve(out);
  65. });
  66. })
  67. }
  68. export function today() {
  69. const date = new Date();
  70. return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
  71. }
  72. function pad(n) {return n > 9 ? n.toString() : `0${n}`}