@hangtime/grip-connect 0.5.8 → 0.5.9

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.
@@ -160,6 +160,11 @@ export class Device extends BaseModel {
160
160
  * @param {number} [options.duration=1000] - The duration (in milliseconds) to monitor the input for activity.
161
161
  * @returns {void}
162
162
  * @public
163
+ *
164
+ * @example
165
+ * device.active((isActive) => {
166
+ * console.log(`Device is ${isActive ? 'active' : 'inactive'}`);
167
+ * }, { threshold: 3.0, duration: 1500 });
163
168
  */
164
169
  active = (callback, options) => {
165
170
  this.activeCallback = callback;
@@ -178,21 +183,34 @@ export class Device extends BaseModel {
178
183
  *
179
184
  * @param {number} input - The dynamic value to check for activity status.
180
185
  * @returns {Promise<void>} A promise that resolves once the activity check is complete.
186
+ *
187
+ * @example
188
+ * await device.activityCheck(5.0);
181
189
  */
182
190
  activityCheck = (input) => {
183
191
  return new Promise((resolve) => {
184
- // Check the activity status after the specified duration
185
- setTimeout(() => {
186
- // Determine the activity status based on the stored threshold in the config
187
- const activeNow = input > this.activeConfig.threshold;
188
- if (this.isActive !== activeNow) {
189
- this.isActive = activeNow;
190
- if (this.activeCallback) {
191
- this.activeCallback(activeNow);
192
+ const startTime = Date.now();
193
+ const checkActivity = () => {
194
+ const currentTime = Date.now();
195
+ const elapsedTime = currentTime - startTime;
196
+ if (elapsedTime >= this.activeConfig.duration) {
197
+ // Determine the activity status based on the most recent input
198
+ const activeNow = input > this.activeConfig.threshold;
199
+ if (this.isActive !== activeNow) {
200
+ this.isActive = activeNow;
201
+ if (this.activeCallback) {
202
+ this.activeCallback(activeNow);
203
+ }
192
204
  }
205
+ resolve();
206
+ }
207
+ else {
208
+ // Continue checking until the duration is met
209
+ requestAnimationFrame(checkActivity);
193
210
  }
194
- resolve();
195
- }, this.activeConfig.duration);
211
+ };
212
+ // Start the activity check
213
+ checkActivity();
196
214
  });
197
215
  };
198
216
  /**
@@ -200,6 +218,12 @@ export class Device extends BaseModel {
200
218
  * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
201
219
  * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
202
220
  * @public
221
+ *
222
+ * @example
223
+ * device.connect(
224
+ * () => console.log("Connected successfully"),
225
+ * (error) => console.error("Connection failed:", error)
226
+ * );
203
227
  */
204
228
  connect = async (onSuccess = () => console.log("Connected successfully"), onError = (error) => console.error(error)) => {
205
229
  try {
@@ -226,24 +250,57 @@ export class Device extends BaseModel {
226
250
  };
227
251
  /**
228
252
  * Disconnects the device if it is currently connected.
229
- * - Checks if the device is connected via it's GATT server.
230
- * - If the device is connected, it attempts to gracefully disconnect.
253
+ * - Removes all notification listeners from the device's characteristics.
254
+ * - Removes the 'gattserverdisconnected' event listener.
255
+ * - Attempts to gracefully disconnect the device's GATT server.
256
+ * - Resets relevant properties to their initial states.
257
+ * @returns {void}
231
258
  * @public
259
+ *
260
+ * @example
261
+ * device.disconnect();
232
262
  */
233
263
  disconnect = () => {
234
- // Verify that the device is connected using the provided helper function
235
264
  if (this.isConnected()) {
265
+ // Remove all notification listeners
266
+ this.services.forEach((service) => {
267
+ service.characteristics.forEach((char) => {
268
+ if (char.characteristic && char.id === "rx") {
269
+ char.characteristic.stopNotifications();
270
+ char.characteristic.removeEventListener("characteristicvaluechanged", (event) => {
271
+ const target = event.target;
272
+ if (target && target.value) {
273
+ this.handleNotifications(target);
274
+ }
275
+ });
276
+ }
277
+ });
278
+ });
279
+ // Remove disconnect listener
280
+ this.bluetooth?.removeEventListener("gattserverdisconnected", this.onDisconnected);
236
281
  // Safely attempt to disconnect the device's GATT server, if available
237
282
  this.bluetooth?.gatt?.disconnect();
283
+ // Reset properties
284
+ this.server = undefined;
285
+ this.writeLast = null;
286
+ this.isActive = false;
238
287
  }
239
288
  };
240
289
  /**
241
290
  * Converts the `downloadPackets` array into a CSV formatted string.
242
291
  * @returns {string} A CSV string representation of the `downloadPackets` data, with each packet on a new line.
243
292
  * @private
293
+ *
294
+ * @example
295
+ * const csvData = device.downloadToCSV();
296
+ * console.log(csvData);
244
297
  */
245
298
  downloadToCSV = () => {
246
- return this.downloadPackets
299
+ const packets = [...this.downloadPackets];
300
+ if (packets.length === 0) {
301
+ return "";
302
+ }
303
+ return packets
247
304
  .map((packet) => [
248
305
  packet.received.toString(),
249
306
  packet.sampleNum.toString(),
@@ -260,6 +317,10 @@ export class Device extends BaseModel {
260
317
  * Converts an array of DownloadPacket objects to a JSON string.
261
318
  * @returns {string} JSON string representation of the data.
262
319
  * @private
320
+ *
321
+ * @example
322
+ * const jsonData = device.downloadToJSON();
323
+ * console.log(jsonData);
263
324
  */
264
325
  downloadToJSON = () => {
265
326
  // Pretty print JSON with 2-space indentation
@@ -269,6 +330,10 @@ export class Device extends BaseModel {
269
330
  * Converts an array of DownloadPacket objects to an XML string.
270
331
  * @returns {string} XML string representation of the data.
271
332
  * @private
333
+ *
334
+ * @example
335
+ * const xmlData = device.downloadToXML();
336
+ * console.log(xmlData);
272
337
  */
273
338
  downloadToXML = () => {
274
339
  const xmlPackets = this.downloadPackets
@@ -297,6 +362,9 @@ export class Device extends BaseModel {
297
362
  *
298
363
  * @returns {void} Initiates a download of the data in the specified format.
299
364
  * @private
365
+ *
366
+ * @example
367
+ * device.download('json');
300
368
  */
301
369
  download = (format = "csv") => {
302
370
  let content = "";
@@ -341,9 +409,13 @@ export class Device extends BaseModel {
341
409
  * Returns UUIDs of all services associated with the device.
342
410
  * @returns {string[]} Array of service UUIDs.
343
411
  * @protected
412
+ *
413
+ * @example
414
+ * const serviceUUIDs = device.getAllServiceUUIDs();
415
+ * console.log(serviceUUIDs);
344
416
  */
345
417
  getAllServiceUUIDs = () => {
346
- return this.services.map((service) => service.uuid);
418
+ return this.services.filter((service) => service?.uuid).map((service) => service.uuid);
347
419
  };
348
420
  /**
349
421
  * Retrieves the characteristic from the device's service.
@@ -351,6 +423,12 @@ export class Device extends BaseModel {
351
423
  * @param {string} characteristicId - The UUID of the characteristic.
352
424
  * @returns {BluetoothRemoteGATTCharacteristic | undefined} The characteristic, if found.
353
425
  * @protected
426
+ *
427
+ * @example
428
+ * const characteristic = device.getCharacteristic('battery', 'level');
429
+ * if (characteristic) {
430
+ * console.log('Characteristic found');
431
+ * }
354
432
  */
355
433
  getCharacteristic = (serviceId, characteristicId) => {
356
434
  // Find the service with the specified serviceId
@@ -368,25 +446,34 @@ export class Device extends BaseModel {
368
446
  };
369
447
  /**
370
448
  * Handles notifications received from a characteristic.
371
- * @param {Event} event - The notification event.
372
- * @protected
449
+ * @param {BluetoothRemoteGATTCharacteristic} characteristic - The notification event.
450
+ *
451
+ * @example
452
+ * device.handleNotifications(someCharacteristic);
373
453
  */
374
- handleNotifications = (event) => {
375
- const characteristic = event.target;
454
+ handleNotifications = (characteristic) => {
376
455
  const value = characteristic.value;
377
- if (value) {
378
- if (value.buffer) {
379
- console.log(value);
380
- }
381
- else {
382
- console.log(value);
383
- }
456
+ if (!value) {
457
+ return;
458
+ }
459
+ if (value.buffer) {
460
+ console.log(value);
461
+ }
462
+ else {
463
+ console.log(value);
384
464
  }
385
465
  };
386
466
  /**
387
467
  * Checks if a Bluetooth device is connected.
388
468
  * @returns {boolean} A boolean indicating whether the device is connected.
389
469
  * @public
470
+ *
471
+ * @example
472
+ * if (device.isConnected()) {
473
+ * console.log('Device is connected');
474
+ * } else {
475
+ * console.log('Device is not connected');
476
+ * }
390
477
  */
391
478
  isConnected = () => {
392
479
  // Check if the device is defined and available
@@ -401,6 +488,11 @@ export class Device extends BaseModel {
401
488
  * @param {NotifyCallback} callback - The callback function to be set.
402
489
  * @returns {void}
403
490
  * @public
491
+ *
492
+ * @example
493
+ * device.notify((data) => {
494
+ * console.log('Received notification:', data);
495
+ * });
404
496
  */
405
497
  notify = (callback) => {
406
498
  this.notifyCallback = callback;
@@ -409,10 +501,18 @@ export class Device extends BaseModel {
409
501
  * Handles the 'connected' event.
410
502
  * @param {Function} onSuccess - Callback function to execute on successful connection.
411
503
  * @public
504
+ *
505
+ * @example
506
+ * device.onConnected(() => {
507
+ * console.log('Device connected successfully');
508
+ * });
412
509
  */
413
510
  onConnected = async (onSuccess) => {
511
+ if (!this.server) {
512
+ throw new Error("GATT server is not available");
513
+ }
414
514
  // Connect to GATT server and set up characteristics
415
- const services = await this.server?.getPrimaryServices();
515
+ const services = await this.server.getPrimaryServices();
416
516
  if (!services || services.length === 0) {
417
517
  throw new Error("No services found");
418
518
  }
@@ -432,7 +532,10 @@ export class Device extends BaseModel {
432
532
  if (element.id === "rx") {
433
533
  matchingCharacteristic.startNotifications();
434
534
  matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
435
- this.handleNotifications(event);
535
+ const target = event.target;
536
+ if (target && target.value) {
537
+ this.handleNotifications(target);
538
+ }
436
539
  });
437
540
  }
438
541
  }
@@ -450,11 +553,14 @@ export class Device extends BaseModel {
450
553
  * Handles the 'disconnected' event.
451
554
  * @param {Event} event - The 'disconnected' event.
452
555
  * @public
556
+ *
557
+ * @example
558
+ * device.onDisconnected(event);
453
559
  */
454
560
  onDisconnected = (event) => {
455
561
  this.bluetooth = undefined;
456
562
  const device = event.target;
457
- throw new Error(`Device ${device.name} is disconnected.`);
563
+ console.warn(`Device ${device.name} is disconnected.`);
458
564
  };
459
565
  /**
460
566
  * Reads the value of the specified characteristic from the device.
@@ -463,6 +569,10 @@ export class Device extends BaseModel {
463
569
  * @param {number} [duration=0] - The duration to wait before resolving the promise, in milliseconds.
464
570
  * @returns {Promise<string | undefined>} A promise that resolves when the read operation is completed.
465
571
  * @public
572
+ *
573
+ * @example
574
+ * const value = await device.read('battery', 'level', 1000);
575
+ * console.log('Battery level:', value);
466
576
  */
467
577
  read = async (serviceId, characteristicId, duration = 0) => {
468
578
  if (!this.isConnected()) {
@@ -496,20 +606,35 @@ export class Device extends BaseModel {
496
606
  /**
497
607
  * Initiates the tare calibration process.
498
608
  * @param {number} duration - The duration time for tare calibration.
499
- * @returns {void}
609
+ * @returns {boolean} A boolean indicating whether the tare calibration was successful.
500
610
  * @public
611
+ *
612
+ * @example
613
+ * const success = device.tare(5000);
614
+ * if (success) {
615
+ * console.log('Tare calibration started');
616
+ * } else {
617
+ * console.log('Tare calibration failed to start');
618
+ * }
501
619
  */
502
620
  tare(duration = 5000) {
621
+ if (this.tareActive)
622
+ return false;
503
623
  this.tareActive = true;
504
624
  this.tareDuration = duration;
505
625
  this.tareSamples = [];
506
626
  this.tareStartTime = Date.now();
627
+ return true;
507
628
  }
508
629
  /**
509
630
  * Apply tare calibration to the provided sample.
510
631
  * @param {number} sample - The sample to calibrate.
511
632
  * @returns {number} The calibrated tare value.
512
633
  * @protected
634
+ *
635
+ * @example
636
+ * const calibratedSample = device.applyTare(rawSample);
637
+ * console.log('Calibrated sample:', calibratedSample);
513
638
  */
514
639
  applyTare(sample) {
515
640
  if (this.tareActive && this.tareStartTime) {
@@ -549,7 +674,7 @@ export class Device extends BaseModel {
549
674
  write = async (serviceId, characteristicId, message, duration = 0, callback = this.writeCallback) => {
550
675
  // Check if not connected or no message is provided
551
676
  if (!this.isConnected() || message === undefined) {
552
- return undefined;
677
+ return Promise.resolve();
553
678
  }
554
679
  // Get the characteristic from the service
555
680
  const characteristic = this.getCharacteristic(serviceId, characteristicId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.5.8",
3
+ "version": "0.5.9",
4
4
  "description": "Griptonite Motherboard, Tindeq Progressor, PitchSix Force Board, WHC-06, Entralpi, Climbro, mySmartBoard: Web Bluetooth API Force-Sensing strength analysis for climbers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -69,19 +69,54 @@ export interface IDevice extends IBase {
69
69
  */
70
70
  commands: Commands
71
71
 
72
+ /**
73
+ * Sets the callback function to be called when the activity status changes,
74
+ * and optionally sets the configuration for threshold and duration.
75
+ *
76
+ * This function allows you to specify a callback that will be invoked whenever
77
+ * the activity status changes, indicating whether the device is currently active.
78
+ * It also allows optionally configuring the threshold and duration used to determine activity.
79
+ *
80
+ * @param {ActiveCallback} callback - The callback function to be set. This function
81
+ * receives a boolean value indicating the new activity status.
82
+ * @param {object} [options] - Optional configuration object containing the threshold and duration.
83
+ * @param {number} [options.threshold=2.5] - The threshold value for determining activity.
84
+ * @param {number} [options.duration=1000] - The duration (in milliseconds) to monitor the input for activity.
85
+ * @returns {void}
86
+ * @public
87
+ *
88
+ * @example
89
+ * device.active((isActive) => {
90
+ * console.log(`Device is ${isActive ? 'active' : 'inactive'}`);
91
+ * }, { threshold: 3.0, duration: 1500 });
92
+ */
93
+ active(callback?: (data: boolean) => void, options?: { threshold?: number; duration?: number }): void
94
+
72
95
  /**
73
96
  * Connects to a Bluetooth device.
74
97
  * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
75
98
  * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
76
99
  * @public
100
+ *
101
+ * @example
102
+ * device.connect(
103
+ * () => console.log("Connected successfully"),
104
+ * (error) => console.error("Connection failed:", error)
105
+ * );
77
106
  */
78
107
  connect(onSuccess?: () => void, onError?: (error: Error) => void): Promise<void>
79
108
 
80
109
  /**
81
110
  * Disconnects the device if it is currently connected.
82
- * - Checks if the device is connected via it's GATT server.
83
- * - If the device is connected, it attempts to gracefully disconnect.
111
+ * - Removes all notification listeners from the device's characteristics.
112
+ * - Removes the 'gattserverdisconnected' event listener.
113
+ * - Attempts to gracefully disconnect the device's GATT server.
114
+ * - Resets relevant properties to their initial states.
115
+ * @returns {void}
84
116
  * @public
117
+ *
118
+ * @example
119
+ * device.disconnect();
85
120
  */
86
121
  disconnect(): void
87
122
 
@@ -94,6 +129,9 @@ export interface IDevice extends IBase {
94
129
  *
95
130
  * @returns {void} Initiates a download of the data in the specified format.
96
131
  * @private
132
+ *
133
+ * @example
134
+ * device.download('json');
97
135
  */
98
136
  download(format?: "csv" | "json" | "xml"): void
99
137
 
@@ -101,6 +139,13 @@ export interface IDevice extends IBase {
101
139
  * Checks if a Bluetooth device is connected.
102
140
  * @returns {boolean} A boolean indicating whether the device is connected.
103
141
  * @public
142
+ *
143
+ * @example
144
+ * if (device.isConnected()) {
145
+ * console.log('Device is connected');
146
+ * } else {
147
+ * console.log('Device is not connected');
148
+ * }
104
149
  */
105
150
  isConnected(): boolean
106
151
 
@@ -109,6 +154,11 @@ export interface IDevice extends IBase {
109
154
  * @param {NotifyCallback} callback - The callback function to be set.
110
155
  * @returns {void}
111
156
  * @public
157
+ *
158
+ * @example
159
+ * device.notify((data) => {
160
+ * console.log('Received notification:', data);
161
+ * });
112
162
  */
113
163
  notify(callback: (data: massObject) => void): void
114
164
 
@@ -119,15 +169,28 @@ export interface IDevice extends IBase {
119
169
  * @param {number} [duration=0] - The duration to wait before resolving the promise, in milliseconds.
120
170
  * @returns {Promise<string | undefined>} A promise that resolves when the read operation is completed.
121
171
  * @public
172
+ *
173
+ * @example
174
+ * const value = await device.read('battery', 'level', 1000);
175
+ * console.log('Battery level:', value);
122
176
  */
123
177
  read(serviceId: string, characteristicId: string, duration?: number): Promise<string | undefined>
124
178
 
125
179
  /**
126
180
  * Initiates the tare calibration process.
127
181
  * @param {number} duration - The duration time for tare calibration.
128
- * @returns {void}
182
+ * @returns {boolean} A boolean indicating whether the tare calibration was successful.
183
+ * @public
184
+ *
185
+ * @example
186
+ * const success = device.tare(5000);
187
+ * if (success) {
188
+ * console.log('Tare calibration started');
189
+ * } else {
190
+ * console.log('Tare calibration failed to start');
191
+ * }
129
192
  */
130
- tare(duration?: number): void
193
+ tare(duration?: number): boolean
131
194
 
132
195
  /**
133
196
  * Writes a message to the specified characteristic of a Bluetooth device and optionally provides a callback to handle responses.
@@ -153,10 +153,9 @@ export class Entralpi extends Device implements IEntralpi {
153
153
  * and updates mass data including maximum and average values.
154
154
  * It also handles command responses for retrieving device information.
155
155
  *
156
- * @param {Event} event - The notification event.
156
+ * @param {BluetoothRemoteGATTCharacteristic} characteristic - The notification event.
157
157
  */
158
- handleNotifications = (event: Event): void => {
159
- const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
158
+ handleNotifications = (characteristic: BluetoothRemoteGATTCharacteristic): void => {
160
159
  const value: DataView | undefined = characteristic.value
161
160
 
162
161
  if (value) {
@@ -180,10 +180,9 @@ export class ForceBoard extends Device implements IForceBoard {
180
180
  * and updates mass data including maximum and average values.
181
181
  * It also handles command responses for retrieving device information.
182
182
  *
183
- * @param {Event} event - The notification event.
183
+ * @param {BluetoothRemoteGATTCharacteristic} characteristic - The notification event.
184
184
  */
185
- handleNotifications = (event: Event): void => {
186
- const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
185
+ handleNotifications = (characteristic: BluetoothRemoteGATTCharacteristic): void => {
187
186
  const value: DataView | undefined = characteristic.value
188
187
  if (value) {
189
188
  if (value.buffer) {
@@ -201,10 +201,9 @@ export class Motherboard extends Device implements IMotherboard {
201
201
  * to extract samples, calibrate masses, and update running averages of mass data.
202
202
  * If the received data is not a valid hex packet, it returns the unprocessed data.
203
203
  *
204
- * @param {Event} event - The notification event.
204
+ * @param {BluetoothRemoteGATTCharacteristic} characteristic - The notification event.
205
205
  */
206
- handleNotifications = (event: Event): void => {
207
- const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
206
+ handleNotifications = (characteristic: BluetoothRemoteGATTCharacteristic): void => {
208
207
  const value: DataView | undefined = characteristic.value
209
208
 
210
209
  if (value) {
@@ -120,10 +120,9 @@ export class Progressor extends Device implements IProgressor {
120
120
  * and updates mass data including maximum and average values.
121
121
  * It also handles command responses for retrieving device information.
122
122
  *
123
- * @param {Event} event - The notification event.
123
+ * @param {BluetoothRemoteGATTCharacteristic} characteristic - The notification event.
124
124
  */
125
- handleNotifications = (event: Event): void => {
126
- const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
125
+ handleNotifications = (characteristic: BluetoothRemoteGATTCharacteristic): void => {
127
126
  const value: DataView | undefined = characteristic.value
128
127
 
129
128
  if (value) {