@flow-js/garmin-connect 1.6.6 → 1.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,42 +1,61 @@
1
- # garmin-connect
1
+ # Garmin Connect
2
2
 
3
- This is a fork of https://github.com/Pythe1337N/garmin-connect
3
+ This is a fork of https://github.com/Pythe1337N/garmin-connect which was inspired by [https://github.com/matin/garth](https://github.com/matin/garth). Many thanks to contributors.
4
4
 
5
- ## v1.6.0 refactor
5
+ ---
6
6
 
7
- TODO:
7
+ A JavaScript library for accessing and managing your Garmin Connect data. It comes with methods to get and set information in your Garmin account, and also supports [custom requests](#custom-requests) using `GET`, `POST`, and `PUT` so you can cover additional needs.
8
8
 
9
- - [x] New HttpClient class
10
- - [x] Login and get user token
11
- - [x] Garmin URLs works with `garmin.cn` and `garmin.com`
12
- - [x] Auto refresh Ouath2 token
13
- - [x] Oauth1,Oauth2 token import and export.
14
- - [x] Download Activity, countActivities, getActivities, getActivity, getUserProfile, getUserSettings
15
- - [x] Upload Activity, delete Activity
16
- - [ ] Implementation of other methods:
17
- - [ ] Badge
18
- - [x] Gear
19
- - [x] Workout
20
- - [x] Course
21
- - ...etc
22
- - [ ] Handle MFA
23
- - [x] Handle Account locked
24
- - [ ] Unit test
25
- - [ ] Listeners
9
+ This document provides detailed information about the public API methods available in the `@flow-js/garmin-connect` library.
26
10
 
27
- If something is not working, please check [https://connect.garmin.com/status/](https://connect.garmin.com/status/) first.
11
+ ## Table of Contents
28
12
 
29
- Currently, most of previous features are working, but some of Rest API are not added, such as `Gear`,`Workout`,`Badge` etc. So if you need these features, please add a PR.
13
+ - [Authentication](#authentication)
14
+ - [Constructor](#constructor)
15
+ - [Login](#login)
16
+ - [Session Management](#session-management)
17
+ - [User Data](#user-data)
18
+ - [User Profile](#user-profile)
19
+ - [User Settings](#user-settings)
20
+ - [Activities](#activities)
21
+ - [Getting Activities](#getting-activities)
22
+ - [Individual Activity Operations](#individual-activity-operations)
23
+ - [Activity Files](#activity-files)
24
+ - [Workouts](#workouts)
25
+ - [Managing Workouts](#managing-workouts)
26
+ - [Workout Scheduling](#workout-scheduling)
27
+ - [Health Data](#health-data)
28
+ - [Steps](#steps)
29
+ - [Sleep](#sleep)
30
+ - [Weight](#weight)
31
+ - [Hydration](#hydration)
32
+ - [Heart Rate](#heart-rate)
33
+ - [Golf](#golf)
34
+ - [Gear](#gear)
35
+ - [GPX and Courses](#gpx-and-courses)
36
+ - [Calendar](#calendar)
37
+ - [Custom Requests](#custom-requests)
30
38
 
31
- All of above work inspired by [https://github.com/matin/garth](https://github.com/matin/garth). Many thanks.
39
+ ## Authentication
32
40
 
33
- ---
41
+ ### Constructor
42
+
43
+ Create a new instance of the Garmin Connect client.
44
+
45
+ ```js
46
+ const { GarminConnect } = require('@flow-js/garmin-connect');
34
47
 
35
- A powerful JavaScript library for connecting to Garmin Connect for sending and receiving health and workout data. It comes with some predefined methods to get and set different kinds of data for your Garmin account, but also have the possibility to make [custom requests](#custom-requests) `GET`, `POST` and `PUT` are currently supported. This makes it easy to implement whatever may be missing to suite your needs.
48
+ // Create a new Garmin Connect Client with credentials
49
+ const GCClient = new GarminConnect({
50
+ username: 'my.email@example.com',
51
+ password: 'MySecretPassword'
52
+ });
36
53
 
37
- ## Prerequisites
54
+ // Alternatively, create a client using credentials from garmin.config.json
55
+ const GCClient = new GarminConnect();
56
+ ```
38
57
 
39
- This library will require you to add a configuration file to your project root called `garmin.config.json` containing your username and password for the Garmin Connect service.
58
+ You can also provide a configuration file named `garmin.config.json` at your project root:
40
59
 
41
60
  ```json
42
61
  {
@@ -45,164 +64,144 @@ This library will require you to add a configuration file to your project root c
45
64
  }
46
65
  ```
47
66
 
48
- ## How to install
49
-
50
- ```shell
51
- $ npm install garmin-connect
52
- ```
53
-
54
- ## How to use
67
+ ### Login
55
68
 
56
69
  ```js
57
- const { GarminConnect } = require('garmin-connect');
58
- // Create a new Garmin Connect Client
59
- const GCClient = new GarminConnect({
60
- username: 'my.email@example.com',
61
- password: 'MySecretPassword'
62
- });
63
- // Uses credentials from garmin.config.json or uses supplied params
64
- await GCClient.login();
65
- const userProfile = await GCClient.getUserProfile();
70
+ /**
71
+ * Login to Garmin Connect with provided credentials or those set during construction
72
+ * @param username - Optional username to override the one in credentials
73
+ * @param password - Optional password to override the one in credentials
74
+ * @returns The GarminConnect instance for chaining
75
+ */
76
+ async login(username?: string, password?: string): Promise<GarminConnect>
66
77
  ```
67
78
 
68
- Now you can check `userProfile.userName` to verify that your login was successful.
69
-
70
- ## Reusing your session(since v1.6.0)
71
-
72
- ### Save token to file and reuse it.
79
+ Example:
73
80
 
74
81
  ```js
75
- GCClient.saveTokenToFile('/path/to/save/tokens');
82
+ await GCClient.login();
83
+ // Or with specific credentials
84
+ await GCClient.login('my.email@example.com', 'MySecretPassword');
76
85
  ```
77
86
 
78
- Result:
79
-
80
- ```bash
81
- $ ls /path/to/save/tokens
82
- oauth1_token.json oauth2_token.json
83
- ```
87
+ ### Session Management
84
88
 
85
- Reuse token:
89
+ #### Export Tokens
86
90
 
87
91
  ```js
88
- GCClient.loadTokenByFile('/path/to/save/tokens');
92
+ /**
93
+ * Exports OAuth tokens to files in the specified directory
94
+ * @param dirPath - Directory path where token files will be saved
95
+ */
96
+ exportTokenToFile(dirPath: string): void
89
97
  ```
90
98
 
91
- ### Or just save your token to db or other storage.
99
+ Example:
92
100
 
93
101
  ```js
94
- const oauth1 = GCClient.client.oauth1Token;
95
- const oauth2 = GCClient.client.oauth2Token;
96
- // save to db or other storage
97
- ...
102
+ GCClient.exportTokenToFile('/path/to/save/tokens');
103
+ // Creates oauth1_token.json and oauth2_token.json in the specified directory
98
104
  ```
99
105
 
100
- Reuse token:
106
+ #### Load Tokens from Files
101
107
 
102
108
  ```js
103
- GCClient.loadToken(oauth1, oauth2);
109
+ /**
110
+ * Loads OAuth tokens from files in the specified directory
111
+ * @param dirPath - Directory path where token files are stored
112
+ * @throws Error if directory not found
113
+ */
114
+ loadTokenByFile(dirPath: string): void
104
115
  ```
105
116
 
106
- ## Reusing your session(deprecated)
107
-
108
- This is an experimental feature and might not yet provide full stability.
109
-
110
- After a successful login the `sessionJson` getter and setter can be used to export and restore your session.
117
+ Example:
111
118
 
112
119
  ```js
113
- // Exporting the session
114
- const session = GCClient.sessionJson;
115
-
116
- // Use this instead of GCClient.login() to restore the session
117
- // This will throw an error if the stored session cannot be reused
118
- GCClient.restore(session);
120
+ GCClient.loadTokenByFile('/path/to/save/tokens');
119
121
  ```
120
122
 
121
- The exported session should be serializable and can be stored as a JSON string.
122
-
123
- A stored session can only be reused once and will need to be stored after each request. This can be done by attaching some storage to the `sessionChange` event.
123
+ #### Export Tokens as Object
124
124
 
125
125
  ```js
126
- GCClient.onSessionChange((session) => {
127
- /*
128
- Your choice of storage here
129
- node-persist will probably work in most cases
130
- */
131
- });
126
+ /**
127
+ * Exports OAuth tokens as an object
128
+ * @returns Object containing OAuth1 and OAuth2 tokens
129
+ * @throws Error if tokens are not found
130
+ */
131
+ exportToken(): IGarminTokens
132
132
  ```
133
133
 
134
- ### Login fallback
135
-
136
- To make sure to use a stored session if possible, but fallback to regular login, one can use the `restoreOrLogin` method.
137
- The arguments `username` and `password` are both optional and the regular `.login()` will be
138
- called if session restore fails.
134
+ #### Load Tokens from Objects
139
135
 
140
136
  ```js
141
- await GCClient.restoreOrLogin(session, username, password);
137
+ /**
138
+ * Loads OAuth tokens from provided token objects (e.g., from DB or localStorage)
139
+ * @param oauth1 - OAuth1 token object
140
+ * @param oauth2 - OAuth2 token object
141
+ */
142
+ loadToken(oauth1: IOauth1Token, oauth2: IOauth2Token): void
142
143
  ```
143
144
 
144
- ## Events
145
-
146
- - `sessionChange` will trigger on a change in the current `sessionJson`
147
-
148
- To attach a listener to an event, use the `.on()` method.
145
+ Example:
149
146
 
150
147
  ```js
151
- GCClient.on('sessionChange', (session) => console.log(session));
148
+ const oauth1 = GCClient.client.oauth1Token;
149
+ const oauth2 = GCClient.client.oauth2Token;
150
+ // Later, use these to restore the session
151
+ GCClient.loadToken(oauth1, oauth2);
152
152
  ```
153
153
 
154
- There's currently no way of removing listeners.
155
-
156
- ## Reading data
154
+ ## User Data
157
155
 
158
- ### User info is not implemented yet. // TODO: Implement this function
159
-
160
- Receive basic user information
156
+ ### User Profile
161
157
 
162
158
  ```js
163
- GCClient.getUserInfo();
159
+ /**
160
+ * Retrieves the user's social profile from Garmin Connect
161
+ * @returns User's social profile data
162
+ */
163
+ async getUserProfile(): Promise<ISocialProfile>
164
164
  ```
165
165
 
166
- ### Social Profile is not implemented yet. // TODO: Implement this function
167
-
168
- Receive social user information
166
+ Example:
169
167
 
170
168
  ```js
171
- GCClient.getSocialProfile();
169
+ const userProfile = await GCClient.getUserProfile();
170
+ console.log(userProfile.userName); // Verify login was successful
172
171
  ```
173
172
 
174
- ### Social Connections is not implemented yet. // TODO: Implement this function
175
-
176
- Get a list of all social connections
173
+ ### User Settings
177
174
 
178
175
  ```js
179
- GCClient.getSocialConnections();
176
+ /**
177
+ * Retrieves the user's settings from Garmin Connect
178
+ * @returns User settings data
179
+ */
180
+ async getUserSettings(): Promise<IUserSettings>
180
181
  ```
181
182
 
182
- ### Device info is not implemented yet. // TODO: Implement this function
183
+ ## Activities
183
184
 
184
- Get a list of all registered devices including model numbers and firmware versions.
185
+ ### Getting Activities
185
186
 
186
187
  ```js
187
- GCClient.getDeviceInfo();
188
+ /**
189
+ * Retrieves a list of activities matching the specified criteria
190
+ * @param start - Optional starting index for pagination
191
+ * @param limit - Optional limit for pagination
192
+ * @param activityType - Optional activity type filter
193
+ * @param subActivityType - Optional activity subtype filter
194
+ * @returns Array of activities matching the criteria
195
+ */
196
+ async getActivities(
197
+ start?: number,
198
+ limit?: number,
199
+ activityType?: ActivityType,
200
+ subActivityType?: ActivitySubType
201
+ ): Promise<IActivity[]>
188
202
  ```
189
203
 
190
- ### `getActivities(start: number, limit: number, activityType?: ActivityType, subActivityType?: ActivitySubType): Promise<IActivity[]>`
191
-
192
- Retrieves a list of activities based on specified parameters.
193
-
194
- #### Parameters:
195
-
196
- - `start` (number, optonal): Index to start fetching activities.
197
- - `limit` (number, optonal): Number of activities to retrieve.
198
- - `activityType` (ActivityType, optional): Type of activity (if specified, start must be null).
199
- - `subActivityType` (ActivitySubType, optional): Subtype of activity (if specified, start must be null).
200
-
201
- #### Returns:
202
-
203
- - `Promise<IActivity[]>`: A Promise that resolves to an array of activities.
204
-
205
- #### Example:
204
+ Example:
206
205
 
207
206
  ```js
208
207
  const activities = await GCClient.getActivities(
@@ -213,415 +212,598 @@ const activities = await GCClient.getActivities(
213
212
  );
214
213
  ```
215
214
 
216
- ### `getActivity(activity: { activityId: GCActivityId }): Promise<IActivity>`
217
-
218
- Retrieves details for a specific activity based on the provided `activityId`.
219
-
220
- #### Parameters:
221
-
222
- - `activity` (object): An object containing the `activityId` property.
223
-
224
- - `activityId` (GCActivityId): Identifier for the desired activity.
225
-
226
- #### Returns:
227
-
228
- - `Promise<IActivity>`: A Promise that resolves to the details of the specified activity.
229
-
230
- #### Example:
231
-
232
215
  ```js
233
- const activityDetails = await GCClient.getActivity({
234
- activityId: 'exampleActivityId'
235
- });
236
- ```
237
-
238
- ### News Feed is not implemented yet. // TODO: Implement this function
239
-
240
- To get a list of activities in your news feed, use the `getNewsFeed` method. This function takes two arguments, _start_ and _limit_, which is used for pagination. Both are optional and will default to whatever Garmin Connect is using. To be sure to get all activities, use this correctly.
241
-
242
- ```js
243
- // Get the news feed with a default length with most recent activities
244
- GCClient.getNewsFeed();
245
- // Get activities in feed, 10 through 15. (start 10, limit 5)
246
- GCClient.getNewsFeed(10, 5);
216
+ /**
217
+ * Counts lifetime activities
218
+ * @returns Activity statistics including counts by type
219
+ */
220
+ async countActivities(): Promise<ICountActivities>
247
221
  ```
248
222
 
249
- ### Download original activity data
250
-
251
- Use the activityId to download the original activity data. Usually this is supplied as a .zip file.
223
+ ### Individual Activity Operations
252
224
 
253
225
  ```js
254
- const [activity] = await GCClient.getActivities(0, 1);
255
- // Directory path is optional and defaults to the current working directory.
256
- // Downloads filename will be supplied by Garmin.
257
- GCClient.downloadOriginalActivityData(activity, './some/path/that/exists');
226
+ /**
227
+ * Retrieves a specific activity by its ID
228
+ * @param activity - Object containing activityId
229
+ * @returns Details of the specified activity
230
+ * @throws Error if activityId is missing
231
+ */
232
+ async getActivity(activity: {
233
+ activityId: GCActivityId;
234
+ }): Promise<IActivity>
258
235
  ```
259
236
 
260
- ### Upload activity file
261
-
262
- Uploads an activity file as a new Activity. The file can be a `gpx`, `tcx`, or `fit` file. If the activity already exists, the result will have a status code of 409.
263
- Upload fixed in 1.4.4, Garmin changed the upload api, the response `detailedImportResult` doesn't contain the new activityId.
237
+ Example:
264
238
 
265
239
  ```js
266
- const upload = await GCClient.uploadActivity('./some/path/to/file.fit');
267
- // not working
268
- const activityId = upload.detailedImportResult.successes[0].internalId;
269
- const uploadId = upload.detailedImportResult.uploadId;
240
+ const activityDetails = await GCClient.getActivity({
241
+ activityId: 'exampleActivityId'
242
+ });
270
243
  ```
271
244
 
272
- ### Upload activity image
273
-
274
- Uploads an image to activity
275
-
276
245
  ```js
277
- const [latestActivty] = await GCClient.getActivities(0, 1);
278
-
279
- const upload = await GCClient.uploadImage(
280
- latestActivty,
281
- './some/path/to/file.jpg'
282
- );
283
- ```
284
-
285
- ### Delete activity image
286
-
287
- Delete an image from activity
246
+ /**
247
+ * Deletes an activity by activityId
248
+ * @param activity - with activityId
249
+ * @returns void
250
+ *
251
+ */
252
+ async deleteActivity(activity: {
253
+ activityId: GCActivityId;
254
+ }): Promise<void>
255
+ ```
256
+
257
+ ```js
258
+ /**
259
+ * Renames an activity with the given activityId to the newName.
260
+ * @param activityId
261
+ * @param newName
262
+ */
263
+ async renameActivity(
264
+ activityId: GCActivityId,
265
+ newName: string
266
+ ): Promise<void>
267
+ ```
268
+
269
+ ### Activity Files
270
+
271
+ `````ts
272
+ /**
273
+ * Download activity original data file
274
+ *
275
+ * Use the activityId to download the original activity data. Usually this is supplied as a .zip file.
276
+ *
277
+ * @example
278
+ * ```js
279
+ * const [activity] = await GCClient.getActivities(0, 1);
280
+ * // Directory path is optional and defaults to the current working directory.
281
+ * // Downloads filename will be supplied by Garmin.
282
+ * GCClient.downloadOriginalActivityData(activity, './some/path/that/exists');
283
+ * ```
284
+ *
285
+ * @param activity - with activityId
286
+ * @param dir - directory to save the file
287
+ * @param type - 'zip' | 'gpx' | 'tcx' | 'kml' (default: 'zip')
288
+ */
289
+ async downloadOriginalActivityData(
290
+ activity: { activityId: GCActivityId },
291
+ dir: string,
292
+ type: ExportFileTypeValue = 'zip'
293
+ ): Promise<void>
294
+ ``` `
295
+
296
+ ```js
297
+ /**
298
+ * Uploads an activity file
299
+ *
300
+ * Uploads an activity file as a new Activity. The file can be a 'gpx', 'tcx', or 'fit' file.
301
+ * If the activity already exists, the result will have a status code of 409.
302
+ * Note: Garmin changed the upload API in v1.4.4, the response `detailedImportResult` no longer contains the new activityId.
303
+ *
304
+ * @param file - Path to the activity file
305
+ * @param format - 'fit' | 'gpx' | 'tcx'
306
+ * @returns Response from the upload operation
307
+ */
308
+ async uploadActivity(
309
+ file: string,
310
+ format: UploadFileTypeTypeValue = 'fit'
311
+ )
312
+ ```
313
+
314
+ Example:
288
315
 
289
316
  ```js
290
- const [activity] = await GCClient.getActivities(0, 1);
291
- const activityDetails = await GCClient.getActivityDetails(activity.activityId);
292
-
293
- await GCClient.deleteImage(
294
- activity,
295
- activityDetails.metadataDTO.activityImages[0].imageId
296
- );
297
- ```
298
-
299
- ### `getSteps(date?: Date): Promise<number>`
300
-
301
- Retrieves the total steps for a given date.
302
-
303
- #### Parameters:
304
-
305
- - `date` (Date, optional): Date of the steps information requested; defaults to today if no date is supplied.
306
-
307
- #### Returns:
308
-
309
- - `Promise<number>`: A Promise that resolves to the total steps for the specified date.
310
-
311
- #### Example:
317
+ const upload = await GCClient.uploadActivity('./some/path/to/file.fit');
318
+ // Note: Garmin changed the upload API in v1.4.4
319
+ // const activityId = upload.detailedImportResult.successes[0].internalId; // Not working
320
+ // const uploadId = upload.detailedImportResult.uploadId;
321
+ ```
322
+
323
+ ## Workouts
324
+
325
+ ### Managing Workouts
326
+
327
+ ```js
328
+ /**
329
+ * Gets the list of workouts
330
+ * @param start
331
+ * @param limit
332
+ */
333
+ async getWorkouts(start: number, limit: number): Promise<IWorkout[]>
334
+ ```
335
+
336
+ ```js
337
+ /**
338
+ * Gets the workout detail by workoutId
339
+ * @param workout
340
+ * @returns workout detail - IWorkoutDetail
341
+ */
342
+ async getWorkoutDetail(workout: {
343
+ workoutId: string;
344
+ }): Promise<IWorkoutDetail>
345
+ ```
346
+
347
+ ````js
348
+ /**
349
+ * Creates a new workout
350
+ *
351
+ * Use workoutBuilder to create the workout object. See the example in the examples/example-workout.js for more complex workouts.
352
+ *
353
+ * @param workout - workout detail
354
+ * @returns Response from the workout creation operation
355
+ *
356
+ * @example
357
+ * ```js
358
+ * const wb = new WorkoutBuilder(
359
+ * WorkoutType.Running,
360
+ * 'Workout running ' + new Date().toISOString()
361
+ * );
362
+ *
363
+ * wb.addStep(
364
+ * new Step(
365
+ * StepType.Run,
366
+ * TimeDuration.fromSeconds(45),
367
+ * new NoTarget(),
368
+ * 'Comment for the step: Run for 45 seconds'
369
+ * )
370
+ * );
371
+ *
372
+ * GCClient.createWorkout(wb.build());
373
+ * ```
374
+ */
375
+ async createWorkout(workout: IWorkoutDetail)
376
+ `````
377
+
378
+ ````js
379
+ /**
380
+ * Deletes a workout by workoutId
381
+ * @param workout - with workoutId
382
+ *
383
+ * @example
384
+ * ```js
385
+ * const workouts = await GCClient.getWorkouts();
386
+ * const id = workouts[0].workoutId;
387
+ * GCClient.deleteWorkout({ workoutId: id });
388
+ * ```
389
+ */
390
+ async deleteWorkout(workout: { workoutId: string })
391
+ ````
392
+
393
+ ```js
394
+ /**
395
+ * Retrieves all workouts
396
+ * @returns List of workouts
397
+ */
398
+ async workouts(): Promise<Workout[]>
399
+ ```
400
+
401
+ ### Workout Scheduling
402
+
403
+ ````js
404
+ /**
405
+ * Schedule a workout by workoutId to a specific date
406
+ *
407
+ * To add a workout to your calendar, provide the workout id and the date to schedule it on.
408
+ *
409
+ * @param workout - with workoutId
410
+ * @param scheduleDate - 'YYYY-MM-DD' format date string
411
+ *
412
+ * @example
413
+ * ```js
414
+ * const workouts = await GCClient.getWorkouts();
415
+ * const id = workouts[0].workoutId;
416
+ * GCClient.scheduleWorkout({ workoutId: id }, new Date('2025-12-01'));
417
+ * ```
418
+ */
419
+ async scheduleWorkout(
420
+ workout: { workoutId: string },
421
+ scheduleDate: string
422
+ )
423
+ ````
424
+
425
+ ## Health Data
426
+
427
+ ### Steps
428
+
429
+ ```js
430
+ /**
431
+ * Retrieves step count for a specific date
432
+ * @param date - The date to get step count for, defaults to current date
433
+ * @returns Total step count for the specified date
434
+ * @throws Error if steps data not found for the date
435
+ */
436
+ async getSteps(date = new Date()): Promise<number>
437
+ ```
438
+
439
+ Example:
312
440
 
313
441
  ```js
314
442
  const totalSteps = await GCClient.getSteps(new Date('2020-03-24'));
315
443
  ```
316
444
 
317
- ### `getSleepData(date: string): Promise<SleepData>`
318
-
319
- Retrieves all sleep data for a given date
320
-
321
- #### Parameters:
322
-
323
- - `date` (Date, optional): Date of information requested, this will default to today if no date is supplied
324
-
325
- #### Returns:
326
-
327
- - `Promise<SleepData>`: A Promise that resolves to an object containing detailed sleep information.
328
-
329
- - `dailySleepDTO` (object): Information about the user's daily sleep.
330
- - `id` (number): The unique identifier of the sleep record.
331
- - `userProfilePK` (number): The user's profile identifier.
332
- - `calendarDate` (string): The date of the sleep record.
333
- - ...
334
- - `sleepMovement` (array): An array of sleep movement data.
335
- - `remSleepData` (boolean): Indicates whether REM sleep data is available.
336
- - `sleepLevels` (array): An array of sleep levels data.
337
- - `restlessMomentsCount` (number): Count of restless moments during sleep.
338
- - ...
339
-
340
- #### Example:
341
-
342
- ```js
343
- const detailedSleep = await GCClient.getSleepDuration(new Date('2020-03-24'));
344
- ```
345
-
346
- ### `getSleepDuration(date: string): Promise<{hours: number, minutes: number}`
347
-
348
- Retrieves hours and minutes slept for a given date
349
-
350
- #### Parameters:
351
-
352
- - `date` (Date, optional): Date of information requested, this will default to today if no date is supplied
353
-
354
- #### Returns:
355
-
356
- - `Promise<{hours: string, minutes: string }>`: A Promise that resolves to an object containing information about the sleep duration
357
-
358
- - `hours` (string): Number of hours
359
- - `minutes` (string): Number of minutes
360
-
361
- #### Example:
362
-
363
- ```js
364
- const detailedSleep = await GCClient.getSleepDuration(new Date('2020-03-24'));
365
- ```
366
-
367
- ### `getDailyWeightData(date?: Date): Promise<number>`
368
-
369
- Retrieves the daily weight and converts it from grams to pounds.
370
-
371
- #### Parameters:
372
-
373
- - `date` (Date, optional): Date of information requested. Defaults to the current date.
374
-
375
- #### Returns:
376
-
377
- - `Promise<number>`: A Promise that resolves to the daily weight converted from grams to pounds.
378
-
379
- #### Throws:
380
-
381
- - `Error`: If valid daily weight data cannot be found for the specified date.
382
-
383
- #### Example:
384
-
385
- ```js
386
- const weightData = await GCClient.getDailyWeightData(new Date('2023-12-25'));
387
- ```
388
-
389
- ### `getDailyWeightInPounds(date?: Date): Promise<number>`
390
-
391
- Retrieves the daily weight in pounds for a given date.
392
-
393
- #### Parameters:
394
-
395
- - `date` (Date, optional): Date of information requested; defaults to today if no date is supplied.
396
-
397
- #### Returns:
398
-
399
- - `Promise<number>`: A Promise that resolves to the daily weight in pounds.
400
-
401
- #### Example:
402
-
403
- ```js
404
- const weightInPounds = await GCClient.getDailyWeightInPounds(
405
- new Date('2020-03-24')
406
- );
445
+ ### Sleep
446
+
447
+ ```js
448
+ /**
449
+ * Retrieves sleep data for a specific date
450
+ * @param date - The date to get sleep data for, defaults to current date
451
+ * @returns Sleep data for the specified date
452
+ * @throws Error if sleep data is invalid or empty
453
+ */
454
+ async getSleepData(date = new Date()): Promise<SleepData>
455
+ ```
456
+
457
+ ````js
458
+ /**
459
+ * Calculates sleep duration for a specific date
460
+ *
461
+ * Retrieves hours and minutes slept for a given date.
462
+ *
463
+ * @param date - The date to get sleep duration for, defaults to current date
464
+ * @returns Object with hours and minutes of sleep
465
+ * @throws Error if sleep data is missing or invalid
466
+ *
467
+ * @example
468
+ * ```js
469
+ * const detailedSleep = await GCClient.getSleepDuration(new Date('2020-03-24'));
470
+ * console.log(`Hours: ${detailedSleep.hours}, Minutes: ${detailedSleep.minutes}`);
471
+ * ```
472
+ */
473
+ async getSleepDuration(
474
+ date = new Date()
475
+ ): Promise<{ hours: number; minutes: number }>
476
+ ````
477
+
478
+ ### Weight
479
+
480
+ ```js
481
+ /**
482
+ * Retrieves weight data for a specific date
483
+ * @param date - The date to get weight data for, defaults to current date
484
+ * @returns Weight data for the specified date
485
+ * @throws Error if weight data is invalid or empty
486
+ */
487
+ async getDailyWeightData(date = new Date()): Promise<WeightData>
488
+ ```
489
+
490
+ ```js
491
+ /**
492
+ * Retrieves weight data in pounds for a specific date
493
+ * @param date - The date to get weight data for, defaults to current date
494
+ * @returns Weight in pounds for the specified date
495
+ * @throws Error if valid weight data not found for the date
496
+ */
497
+ async getDailyWeightInPounds(date = new Date()): Promise<number>
498
+ ```
499
+
500
+ ````js
501
+ /**
502
+ * Updates weight data for a specific date
503
+ *
504
+ * Updates weight information for the specified date.
505
+ *
506
+ * @param date - The date for the weight data, defaults to current date
507
+ * @param lbs - Weight value in pounds
508
+ * @param timezone - Timezone string for correct timestamp conversion
509
+ * @returns Response from the weight update operation
510
+ * @throws Error if update fails
511
+ *
512
+ * @example
513
+ * ```js
514
+ * await GCClient.updateWeight(undefined, 202.9, 'America/Los_Angeles');
515
+ * ```
516
+ */
517
+ async updateWeight(
518
+ date = new Date(),
519
+ lbs: number,
520
+ timezone: string
521
+ ): Promise<UpdateWeight>
522
+ ````
523
+
524
+ ### Hydration
525
+
526
+ ```js
527
+ /**
528
+ * Retrieves hydration data in fluid ounces for a specific date
529
+ * @param date - The date to get hydration data for, defaults to current date
530
+ * @returns Hydration value in fluid ounces for the specified date
531
+ * @throws Error if hydration data is invalid or empty
532
+ */
533
+ async getDailyHydration(date = new Date()): Promise<number>
534
+ ```
535
+
536
+ ````js
537
+ /**
538
+ * Updates hydration log with fluid ounces for a specific date
539
+ *
540
+ * Adds a hydration log entry in ounces for a given date.
541
+ *
542
+ * @param date - The date for the hydration data, defaults to current date
543
+ * @param valueInOz - Hydration value in fluid ounces. Accepts negative number.
544
+ * @returns Response from the hydration update operation
545
+ * @throws Error if update fails
546
+ *
547
+ * @example
548
+ * ```js
549
+ * const hydrationLogEntry = await GCClient.updateHydrationLogOunces(
550
+ * new Date('2020-03-24'),
551
+ * 16
552
+ * );
553
+ * ```
554
+ */
555
+ async updateHydrationLogOunces(
556
+ date = new Date(),
557
+ valueInOz: number
558
+ ): Promise<WaterIntake>
559
+ ````
560
+
561
+ ### Heart Rate
562
+
563
+ ````js
564
+ /**
565
+ * Retrieves heart rate data for a specific date
566
+ *
567
+ * Retrieves daily heart rate data for a given date.
568
+ *
569
+ * @param date - The date to get heart rate data for, defaults to current date
570
+ * @returns Heart rate data for the specified date
571
+ * @throws Error if the operation fails
572
+ *
573
+ * @example
574
+ * ```js
575
+ * const heartRateData = await GCClient.getHeartRate(new Date('2020-03-24'));
576
+ * ```
577
+ */
578
+ async getHeartRate(date = new Date()): Promise<HeartRate>
579
+ ````
580
+
581
+ ## Golf
582
+
583
+ ```js
584
+ /**
585
+ * Retrieves golf summary data
586
+ * @returns Summary of golf activities
587
+ * @throws Error if golf summary data is invalid or empty
588
+ */
589
+ async getGolfSummary(): Promise<GolfSummary>
590
+ ```
591
+
592
+ ```js
593
+ /**
594
+ * Retrieves golf scorecard for a specific round
595
+ * @param scorecardId - ID of the scorecard to retrieve
596
+ * @returns Golf scorecard data
597
+ * @throws Error if golf scorecard data is invalid or empty
598
+ */
599
+ async getGolfScorecard(scorecardId: number): Promise<GolfScorecard>
600
+ ```
601
+
602
+ ## Gear
603
+
604
+ ```js
605
+ /**
606
+ * Returns the gear data for the user.
607
+ * @param availableGearDate - Optional date to filter the gear available at the date (format: 'YYYY-MM-DD').
608
+ */
609
+ async getGear(availableGearDate?: string): Promise<GearData[]>
610
+ ```
611
+
612
+ ```js
613
+ /**
614
+ * Returns the gear data assigned with a specific activity.
615
+ * @param activityId
616
+ */
617
+ async getGearsForActivity(activityId: GCActivityId): Promise<GearData[]>
618
+ ```
619
+
620
+ ```js
621
+ /**
622
+ * Links a gear item to an activity.
623
+ * @param activityId
624
+ * @param gearId - uuid field from GearData
625
+ * @return GearData - the linked gear item data
626
+ */
627
+ async linkGearToActivity(
628
+ activityId: GCActivityId,
629
+ gearId: GCGearId
630
+ ): Promise<GearData>
631
+ ```
632
+
633
+ ```js
634
+ /**
635
+ * Unlinks a gear item from an activity.
636
+ * @param activityId
637
+ * @param gearId - uuid field from GearData
638
+ * @return GearData - the unlinked gear item data
639
+ */
640
+ async unlinkGearFromActivity(
641
+ activityId: GCActivityId,
642
+ gearId: GCGearId
643
+ ): Promise<GearData>
644
+ ```
645
+
646
+ ## GPX and Courses
647
+
648
+ ````js
649
+ /**
650
+ * Imports GPX file content
651
+ *
652
+ * @example ./examples/example-gpx-file.js
653
+ * @param fileName - Name of the GPX file
654
+ * @param fileContent - Content of the GPX file as string
655
+ * @returns Response from the GPX import operation containing courseName, geoPoints, and coursePoints
656
+ *
657
+ * @example
658
+ * ```js
659
+ * const fileContent = await fs.readFile('paris-marathon.gpx', 'utf8');
660
+ * const response = await GCClient.importGpx('paris-marathon.gpx', fileContent);
661
+ * // The response contains courseName, geoPoints, and coursePoints that can be used with createCourse
662
+ * ```
663
+ */
664
+ async importGpx(
665
+ fileName: string,
666
+ fileContent: string
667
+ ): Promise<ImportedGpxResponse>
668
+ ````
669
+
670
+ ````js
671
+ /**
672
+ * Creates a course from GPX data
673
+ * You can get geoPoints and coursePoints from the imported GPX file response.
674
+ *
675
+ * @example ./examples/example-gpx-file.js
676
+ * @param activityType - Type of activity for the course
677
+ * @param courseName - Name of the course
678
+ * @param geoPoints - Array of geographical points making up the course
679
+ * @param coursePoints - Optional array of course points (waypoints)
680
+ * @returns Response from the course creation operation containing the courseId
681
+ *
682
+ * @example
683
+ * ```js
684
+ * // First import GPX to get geoPoints and coursePoints
685
+ * const response = await GCClient.importGpx('course.gpx', gpxFileContent);
686
+ *
687
+ * // Then create the course
688
+ * const createCourseResponse = await GCClient.createCourse(
689
+ * 1, // activityType (1 = running)
690
+ * response.courseName,
691
+ * response.geoPoints,
692
+ * response.coursePoints
693
+ * );
694
+ *
695
+ * console.log('Course created with id:', createCourseResponse.courseId);
696
+ * ```
697
+ */
698
+ async createCourse(
699
+ activityType: GpxActivityType,
700
+ courseName: string,
701
+ geoPoints: GeoPoint[],
702
+ coursePoints: CoursePoint[] = []
703
+ )
704
+ ````
705
+
706
+ ````js
707
+ /**
708
+ * Lists all courses
709
+ * @returns List of courses in ListCoursesResponse format
710
+ *
711
+ * @example
712
+ * ```js
713
+ * const listCourses = await GCClient.listCourses();
714
+ * console.log(
715
+ * 'Last course:',
716
+ * listCourses.coursesForUser[0].courseId,
717
+ * listCourses.coursesForUser[0].courseName
718
+ * );
719
+ * ```
720
+ */
721
+ async listCourses(): Promise<ListCoursesResponse>
722
+ ````
723
+
724
+ ````js
725
+ /**
726
+ * Exports a course as GPX file content
727
+ * @param courseId - ID of the course to export
728
+ * @returns GPX file content as string
729
+ *
730
+ * @example
731
+ * ```js
732
+ * const downloadGpx = await GCClient.exportCourseAsGpx(courseId);
733
+ * console.log('Downloaded GPX size:', downloadGpx.length);
734
+ * ```
735
+ */
736
+ async exportCourseAsGpx(courseId: number): Promise<string>
737
+ ````
738
+
739
+ ## Calendar
740
+
741
+ ```js
742
+ /**
743
+ * Retrieves calendar events for a specific year.
744
+ * @param year {number} - The year for which to retrieve calendar events.
745
+ */
746
+ async getYearCalendarEvents(year: number): Promise<YearCalendar>
747
+ ```
748
+
749
+ ```js
750
+ /**
751
+ * Retrieves calendar events for a specific month and year.
752
+ * @param year {number} - The year for which to retrieve calendar events.
753
+ * @param month {number} - The month (0-11) for which to retrieve calendar events.
754
+ */
755
+ async getMonthCalendarEvents(
756
+ year: number,
757
+ month: number
758
+ ): Promise<MonthCalendar>
759
+ ```
760
+
761
+ ```js
762
+ /**
763
+ * Retrieves calendar events for a specific week containing the given date.
764
+ * @param year {number} - The year of the date.
765
+ * @param month {number} - The month (0-11) of the date.
766
+ * @param day {number} - The day of the first day of the week.
767
+ * @param firstDayOfWeek {number} - Optional first day of the week, default is 1
768
+ */
769
+ async getWeekCalendarEvents(
770
+ year: number,
771
+ month: number,
772
+ day: number,
773
+ firstDayOfWeek?: number
774
+ ): Promise<any>
775
+ ```
776
+
777
+ ## Custom Requests
778
+
779
+ The library provides methods for making custom requests to the Garmin Connect API:
780
+
781
+ ```js
782
+ /**
783
+ * Performs a GET request to the specified URL
784
+ * @param url - URL to send the request to
785
+ * @param data - Optional query parameters or request configuration
786
+ * @returns Response data of type T
787
+ */
788
+ async get<T>(url: string, data?: any)
789
+ ```
790
+
791
+ ```js
792
+ /**
793
+ * Performs a POST request to the specified URL
794
+ * @param url - URL to send the request to
795
+ * @param data - Data to send in the request body
796
+ * @returns Response data of type T
797
+ */
798
+ async post<T>(url: string, data: any)
799
+ ```
800
+
801
+ ```js
802
+ /**
803
+ * Performs a PUT request to the specified URL
804
+ * @param url - URL to send the request to
805
+ * @param data - Data to send in the request body
806
+ * @returns Response data of type T
807
+ */
808
+ async put<T>(url: string, data: any)
407
809
  ```
408
-
409
- ## `getDailyHydration(date?: Date): Promise<number>`
410
-
411
- Retrieves the daily hydration data and converts it from milliliters to ounces.
412
-
413
- ### Parameters:
414
-
415
- - `date` (Date, optional): Date of the requested information. Defaults to the current date.
416
-
417
- ### Returns:
418
-
419
- - `Promise<number>`: A Promise that resolves to the daily hydration data converted from milliliters to ounces.
420
-
421
- ### Throws:
422
-
423
- - `Error`: If valid daily hydration data cannot be found for the specified date or if the response is invalid.
424
-
425
- ### Example:
426
-
427
- ```js
428
- const hydrationInOunces = await GCClient.getDailyHydration(
429
- new Date('2023-12-25')
430
- );
431
- ```
432
-
433
- ### `getGolfSummary(): Promise<GolfSummary>`
434
-
435
- Retrieves a summary of golf scorecard data.
436
-
437
- #### Returns:
438
-
439
- - `Promise<GolfSummary>`: A Promise that resolves to the golf scorecard summary.
440
-
441
- #### Example:
442
-
443
- ```js
444
- const golfSummary = await GCClient.getGolfSummary();
445
- ```
446
-
447
- ### `getGolfScorecard(scorecardId: number): Promise<GolfScorecard>`
448
-
449
- Retrieves golf scorecard data for a specific scorecard.
450
-
451
- #### Parameters:
452
-
453
- - `scorecardId` (number): Identifier for the desired golf scorecard.
454
-
455
- #### Returns:
456
-
457
- - `Promise<GolfScorecard>`: A Promise that resolves to the golf scorecard data.
458
-
459
- #### Example:
460
-
461
- ```js
462
- const scorecardId = 123; // Replace with the desired scorecard ID
463
- const golfScorecard = await GCClient.getGolfScorecard(scorecardId);
464
- ```
465
-
466
- ### `getHeartRate(date?: Date): Promise<HeartRate>`
467
-
468
- Retrieves daily heart rate data for a given date.
469
-
470
- #### Parameters:
471
-
472
- - `date` (Date, optional): Date of the heart rate data requested; defaults to today if no date is supplied.
473
-
474
- #### Returns:
475
-
476
- - `Promise<HeartRate>`: A Promise that resolves to the daily heart rate data.
477
-
478
- #### Example:
479
-
480
- ```js
481
- const heartRateData = await GCClient.getHeartRate(new Date('2020-03-24'));
482
- ```
483
-
484
- ## Modifying data
485
-
486
- ### Update activity is not implemented yet. // TODO: Implement this function
487
-
488
- ```js
489
- const activities = await GCClient.getActivities(0, 1);
490
- const activity = activities[0];
491
- activity['activityName'] = 'The Updated Name';
492
- await GCClient.updateActivity(activity);
493
- ```
494
-
495
- ### Delete an activity
496
-
497
- Deletes an activty.
498
-
499
- ```js
500
- const activities = await GCClient.getActivities(0, 1);
501
- const activity = activities[0];
502
- await GCClient.deleteActivity(activity);
503
- ```
504
-
505
- ### `updateHydrationLogOunces(date?: Date, valueInOz: number): Promise<WaterIntake>`
506
-
507
- Adds a hydration log entry in ounces for a given date.
508
-
509
- #### Parameters:
510
-
511
- - `date` (Date, optional): Date of the log entry; defaults to today if no date is supplied.
512
- - `valueInOz` (number): Amount of water intake in ounces. Accepts negative number.
513
-
514
- #### Returns:
515
-
516
- - `Promise<WaterIntake>`: A Promise that resolves to the hydration log entry.
517
-
518
- #### Example:
519
-
520
- ```js
521
- const hydrationLogEntry = await GCClient.addHydrationLogOunces(
522
- new Date('2020-03-24'),
523
- 16
524
- );
525
- ```
526
-
527
- ### `updateWeight(date = new Date(), lbs: number, timezone: string): Promise<UpdateWeight>`
528
-
529
- Updates weight information
530
-
531
- #### Parameters:
532
-
533
- - `date` (optional): Date object representing the weight entry date. Defaults to the current date if not provided.
534
- - `lbs` (number): Weight value in pounds.
535
- - `timezone` (string): String representing the timezone for the weight entry.
536
-
537
- #### Returns:
538
-
539
- - `Promise<UpdateWeight>`: A Promise that resolves to the result of the weight update.
540
-
541
- #### Example:
542
-
543
- ```js
544
- await GCClient.updateWeight(undefined, 202.9, 'America/Los_Angeles');
545
- ```
546
-
547
- ### Add workout
548
-
549
- To add a custom workout, use the `addWorkout` or more specifically `addRunningWorkout`.
550
-
551
- ```js
552
- GCClient.addRunningWorkout('My 5k run', 5000, 'Some description');
553
- ```
554
-
555
- Will add a running workout of 5km called 'My 5k run' and return a JSON object representing the saved workout.
556
-
557
- ### Schedule workout
558
-
559
- To add a workout to your calendar, first find your workout and then add it to a specific date.
560
-
561
- ```js
562
- const workouts = await GCClient.getWorkouts();
563
- const id = workouts[0].workoutId;
564
- GCClient.scheduleWorkout({ workoutId: id }, new Date('2020-03-24'));
565
- ```
566
-
567
- This will add the workout to a specific date in your calendar and make it show up automatically if you're using any of the Garmin watches.
568
-
569
- ### Delete workout
570
-
571
- Deleting a workout is very similar to [scheduling](#schedule-workout) one.
572
-
573
- ```js
574
- const workouts = await GCClient.getWorkouts();
575
- const id = workouts[0].workoutId;
576
- GCClient.deleteWorkout({ workoutId: id });
577
- ```
578
-
579
- ## Custom requests
580
-
581
- This library will handle custom requests to your active Garmin Connect session. There are a lot of different url's that is used, which means that this library probably wont cover them all. By using the network analyze tool you can find url's that are used by Garmin Connect to fetch data.
582
-
583
- Let's assume I found a `GET` requests to the following url:
584
-
585
- ```
586
- https://connect.garmin.com/modern/proxy/wellness-service/wellness/dailyHeartRate/22f5f84c-de9d-4ad6-97f2-201097b3b983?date=2020-03-24
587
- ```
588
-
589
- The request can be sent using `GCClient` by running
590
-
591
- ```js
592
- // You can get your displayName by using the getUserInfo method;
593
- const displayName = '22f5f84c-de9d-4ad6-97f2-201097b3b983';
594
- const url =
595
- 'https://connect.garmin.com/modern/proxy/wellness-service/wellness/dailyHeartRate/';
596
- const dateString = '2020-03-24';
597
- GCClient.get(url + displayName, { date: dateString });
598
- ```
599
-
600
- and will net you the same result as using the provided way
601
-
602
- ```js
603
- GCClient.getHeartRate();
604
- ```
605
-
606
- Notice how the client will keep track of the url's, your user information as well as keeping the session alive.
607
-
608
- ## Limitations
609
-
610
- Many responses from Garmin Connect are missing type definitions and defaults to `unknown`. Feel free to add types by opening a pull request.
611
-
612
- For now, this library only supports the following:
613
-
614
- - Get user info
615
- - Get social user info
616
- - Get heart rate
617
- - Set body weight
618
- - Get list of workouts
619
- - Add new workouts
620
- - Add workouts to you calendar
621
- - Remove previously added workouts
622
- - Get list of activities
623
- - Get details about one specific activity
624
- - Get the step count
625
- - Get earned badges
626
- - Get available badges
627
- - Get details about one specific badge