@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.
@@ -187,6 +187,11 @@ export abstract class Device extends BaseModel implements IDevice {
187
187
  * @param {number} [options.duration=1000] - The duration (in milliseconds) to monitor the input for activity.
188
188
  * @returns {void}
189
189
  * @public
190
+ *
191
+ * @example
192
+ * device.active((isActive) => {
193
+ * console.log(`Device is ${isActive ? 'active' : 'inactive'}`);
194
+ * }, { threshold: 3.0, duration: 1500 });
190
195
  */
191
196
  active = (callback: ActiveCallback, options?: { threshold?: number; duration?: number }): void => {
192
197
  this.activeCallback = callback
@@ -207,21 +212,35 @@ export abstract class Device extends BaseModel implements IDevice {
207
212
  *
208
213
  * @param {number} input - The dynamic value to check for activity status.
209
214
  * @returns {Promise<void>} A promise that resolves once the activity check is complete.
215
+ *
216
+ * @example
217
+ * await device.activityCheck(5.0);
210
218
  */
211
219
  protected activityCheck = (input: number): Promise<void> => {
212
220
  return new Promise((resolve) => {
213
- // Check the activity status after the specified duration
214
- setTimeout(() => {
215
- // Determine the activity status based on the stored threshold in the config
216
- const activeNow = input > this.activeConfig.threshold
217
- if (this.isActive !== activeNow) {
218
- this.isActive = activeNow
219
- if (this.activeCallback) {
220
- this.activeCallback(activeNow)
221
+ const startTime = Date.now()
222
+ const checkActivity = () => {
223
+ const currentTime = Date.now()
224
+ const elapsedTime = currentTime - startTime
225
+
226
+ if (elapsedTime >= this.activeConfig.duration) {
227
+ // Determine the activity status based on the most recent input
228
+ const activeNow = input > this.activeConfig.threshold
229
+ if (this.isActive !== activeNow) {
230
+ this.isActive = activeNow
231
+ if (this.activeCallback) {
232
+ this.activeCallback(activeNow)
233
+ }
221
234
  }
235
+ resolve()
236
+ } else {
237
+ // Continue checking until the duration is met
238
+ requestAnimationFrame(checkActivity)
222
239
  }
223
- resolve()
224
- }, this.activeConfig.duration)
240
+ }
241
+
242
+ // Start the activity check
243
+ checkActivity()
225
244
  })
226
245
  }
227
246
 
@@ -230,6 +249,12 @@ export abstract class Device extends BaseModel implements IDevice {
230
249
  * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
231
250
  * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
232
251
  * @public
252
+ *
253
+ * @example
254
+ * device.connect(
255
+ * () => console.log("Connected successfully"),
256
+ * (error) => console.error("Connection failed:", error)
257
+ * );
233
258
  */
234
259
  connect = async (
235
260
  onSuccess: () => void = () => console.log("Connected successfully"),
@@ -264,15 +289,40 @@ export abstract class Device extends BaseModel implements IDevice {
264
289
 
265
290
  /**
266
291
  * Disconnects the device if it is currently connected.
267
- * - Checks if the device is connected via it's GATT server.
268
- * - If the device is connected, it attempts to gracefully disconnect.
292
+ * - Removes all notification listeners from the device's characteristics.
293
+ * - Removes the 'gattserverdisconnected' event listener.
294
+ * - Attempts to gracefully disconnect the device's GATT server.
295
+ * - Resets relevant properties to their initial states.
296
+ * @returns {void}
269
297
  * @public
298
+ *
299
+ * @example
300
+ * device.disconnect();
270
301
  */
271
302
  disconnect = (): void => {
272
- // Verify that the device is connected using the provided helper function
273
303
  if (this.isConnected()) {
304
+ // Remove all notification listeners
305
+ this.services.forEach((service) => {
306
+ service.characteristics.forEach((char) => {
307
+ if (char.characteristic && char.id === "rx") {
308
+ char.characteristic.stopNotifications()
309
+ char.characteristic.removeEventListener("characteristicvaluechanged", (event: Event) => {
310
+ const target = event.target as BluetoothRemoteGATTCharacteristic
311
+ if (target && target.value) {
312
+ this.handleNotifications(target)
313
+ }
314
+ })
315
+ }
316
+ })
317
+ })
318
+ // Remove disconnect listener
319
+ this.bluetooth?.removeEventListener("gattserverdisconnected", this.onDisconnected)
274
320
  // Safely attempt to disconnect the device's GATT server, if available
275
321
  this.bluetooth?.gatt?.disconnect()
322
+ // Reset properties
323
+ this.server = undefined
324
+ this.writeLast = null
325
+ this.isActive = false
276
326
  }
277
327
  }
278
328
 
@@ -280,9 +330,17 @@ export abstract class Device extends BaseModel implements IDevice {
280
330
  * Converts the `downloadPackets` array into a CSV formatted string.
281
331
  * @returns {string} A CSV string representation of the `downloadPackets` data, with each packet on a new line.
282
332
  * @private
333
+ *
334
+ * @example
335
+ * const csvData = device.downloadToCSV();
336
+ * console.log(csvData);
283
337
  */
284
338
  private downloadToCSV = (): string => {
285
- return this.downloadPackets
339
+ const packets = [...this.downloadPackets]
340
+ if (packets.length === 0) {
341
+ return ""
342
+ }
343
+ return packets
286
344
  .map((packet) =>
287
345
  [
288
346
  packet.received.toString(),
@@ -302,6 +360,10 @@ export abstract class Device extends BaseModel implements IDevice {
302
360
  * Converts an array of DownloadPacket objects to a JSON string.
303
361
  * @returns {string} JSON string representation of the data.
304
362
  * @private
363
+ *
364
+ * @example
365
+ * const jsonData = device.downloadToJSON();
366
+ * console.log(jsonData);
305
367
  */
306
368
  private downloadToJSON = (): string => {
307
369
  // Pretty print JSON with 2-space indentation
@@ -312,6 +374,10 @@ export abstract class Device extends BaseModel implements IDevice {
312
374
  * Converts an array of DownloadPacket objects to an XML string.
313
375
  * @returns {string} XML string representation of the data.
314
376
  * @private
377
+ *
378
+ * @example
379
+ * const xmlData = device.downloadToXML();
380
+ * console.log(xmlData);
315
381
  */
316
382
  private downloadToXML = (): string => {
317
383
  const xmlPackets = this.downloadPackets
@@ -341,6 +407,9 @@ export abstract class Device extends BaseModel implements IDevice {
341
407
  *
342
408
  * @returns {void} Initiates a download of the data in the specified format.
343
409
  * @private
410
+ *
411
+ * @example
412
+ * device.download('json');
344
413
  */
345
414
  download = (format: "csv" | "json" | "xml" = "csv"): void => {
346
415
  let content = ""
@@ -394,9 +463,13 @@ export abstract class Device extends BaseModel implements IDevice {
394
463
  * Returns UUIDs of all services associated with the device.
395
464
  * @returns {string[]} Array of service UUIDs.
396
465
  * @protected
466
+ *
467
+ * @example
468
+ * const serviceUUIDs = device.getAllServiceUUIDs();
469
+ * console.log(serviceUUIDs);
397
470
  */
398
471
  protected getAllServiceUUIDs = (): string[] => {
399
- return this.services.map((service) => service.uuid)
472
+ return this.services.filter((service) => service?.uuid).map((service) => service.uuid)
400
473
  }
401
474
 
402
475
  /**
@@ -405,6 +478,12 @@ export abstract class Device extends BaseModel implements IDevice {
405
478
  * @param {string} characteristicId - The UUID of the characteristic.
406
479
  * @returns {BluetoothRemoteGATTCharacteristic | undefined} The characteristic, if found.
407
480
  * @protected
481
+ *
482
+ * @example
483
+ * const characteristic = device.getCharacteristic('battery', 'level');
484
+ * if (characteristic) {
485
+ * console.log('Characteristic found');
486
+ * }
408
487
  */
409
488
  protected getCharacteristic = (
410
489
  serviceId: string,
@@ -428,19 +507,22 @@ export abstract class Device extends BaseModel implements IDevice {
428
507
 
429
508
  /**
430
509
  * Handles notifications received from a characteristic.
431
- * @param {Event} event - The notification event.
432
- * @protected
510
+ * @param {BluetoothRemoteGATTCharacteristic} characteristic - The notification event.
511
+ *
512
+ * @example
513
+ * device.handleNotifications(someCharacteristic);
433
514
  */
434
- protected handleNotifications = (event: Event): void => {
435
- const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
436
- const value: DataView | undefined = characteristic.value
515
+ protected handleNotifications = (characteristic: BluetoothRemoteGATTCharacteristic): void => {
516
+ const value = characteristic.value
437
517
 
438
- if (value) {
439
- if (value.buffer) {
440
- console.log(value)
441
- } else {
442
- console.log(value)
443
- }
518
+ if (!value) {
519
+ return
520
+ }
521
+
522
+ if (value.buffer) {
523
+ console.log(value)
524
+ } else {
525
+ console.log(value)
444
526
  }
445
527
  }
446
528
 
@@ -448,6 +530,13 @@ export abstract class Device extends BaseModel implements IDevice {
448
530
  * Checks if a Bluetooth device is connected.
449
531
  * @returns {boolean} A boolean indicating whether the device is connected.
450
532
  * @public
533
+ *
534
+ * @example
535
+ * if (device.isConnected()) {
536
+ * console.log('Device is connected');
537
+ * } else {
538
+ * console.log('Device is not connected');
539
+ * }
451
540
  */
452
541
  isConnected = (): boolean => {
453
542
  // Check if the device is defined and available
@@ -463,6 +552,11 @@ export abstract class Device extends BaseModel implements IDevice {
463
552
  * @param {NotifyCallback} callback - The callback function to be set.
464
553
  * @returns {void}
465
554
  * @public
555
+ *
556
+ * @example
557
+ * device.notify((data) => {
558
+ * console.log('Received notification:', data);
559
+ * });
466
560
  */
467
561
  notify = (callback: NotifyCallback): void => {
468
562
  this.notifyCallback = callback
@@ -472,10 +566,18 @@ export abstract class Device extends BaseModel implements IDevice {
472
566
  * Handles the 'connected' event.
473
567
  * @param {Function} onSuccess - Callback function to execute on successful connection.
474
568
  * @public
569
+ *
570
+ * @example
571
+ * device.onConnected(() => {
572
+ * console.log('Device connected successfully');
573
+ * });
475
574
  */
476
575
  protected onConnected = async (onSuccess: () => void): Promise<void> => {
576
+ if (!this.server) {
577
+ throw new Error("GATT server is not available")
578
+ }
477
579
  // Connect to GATT server and set up characteristics
478
- const services: BluetoothRemoteGATTService[] | undefined = await this.server?.getPrimaryServices()
580
+ const services: BluetoothRemoteGATTService[] | undefined = await this.server.getPrimaryServices()
479
581
 
480
582
  if (!services || services.length === 0) {
481
583
  throw new Error("No services found")
@@ -502,7 +604,10 @@ export abstract class Device extends BaseModel implements IDevice {
502
604
  if (element.id === "rx") {
503
605
  matchingCharacteristic.startNotifications()
504
606
  matchingCharacteristic.addEventListener("characteristicvaluechanged", (event: Event) => {
505
- this.handleNotifications(event)
607
+ const target = event.target as BluetoothRemoteGATTCharacteristic
608
+ if (target && target.value) {
609
+ this.handleNotifications(target)
610
+ }
506
611
  })
507
612
  }
508
613
  }
@@ -521,11 +626,14 @@ export abstract class Device extends BaseModel implements IDevice {
521
626
  * Handles the 'disconnected' event.
522
627
  * @param {Event} event - The 'disconnected' event.
523
628
  * @public
629
+ *
630
+ * @example
631
+ * device.onDisconnected(event);
524
632
  */
525
633
  protected onDisconnected = (event: Event): void => {
526
634
  this.bluetooth = undefined
527
635
  const device = event.target as BluetoothDevice
528
- throw new Error(`Device ${device.name} is disconnected.`)
636
+ console.warn(`Device ${device.name} is disconnected.`)
529
637
  }
530
638
 
531
639
  /**
@@ -535,6 +643,10 @@ export abstract class Device extends BaseModel implements IDevice {
535
643
  * @param {number} [duration=0] - The duration to wait before resolving the promise, in milliseconds.
536
644
  * @returns {Promise<string | undefined>} A promise that resolves when the read operation is completed.
537
645
  * @public
646
+ *
647
+ * @example
648
+ * const value = await device.read('battery', 'level', 1000);
649
+ * console.log('Battery level:', value);
538
650
  */
539
651
  read = async (serviceId: string, characteristicId: string, duration = 0): Promise<string | undefined> => {
540
652
  if (!this.isConnected()) {
@@ -572,14 +684,24 @@ export abstract class Device extends BaseModel implements IDevice {
572
684
  /**
573
685
  * Initiates the tare calibration process.
574
686
  * @param {number} duration - The duration time for tare calibration.
575
- * @returns {void}
687
+ * @returns {boolean} A boolean indicating whether the tare calibration was successful.
576
688
  * @public
577
- */
578
- tare(duration = 5000): void {
689
+ *
690
+ * @example
691
+ * const success = device.tare(5000);
692
+ * if (success) {
693
+ * console.log('Tare calibration started');
694
+ * } else {
695
+ * console.log('Tare calibration failed to start');
696
+ * }
697
+ */
698
+ tare(duration = 5000): boolean {
699
+ if (this.tareActive) return false
579
700
  this.tareActive = true
580
701
  this.tareDuration = duration
581
702
  this.tareSamples = []
582
703
  this.tareStartTime = Date.now()
704
+ return true
583
705
  }
584
706
 
585
707
  /**
@@ -587,6 +709,10 @@ export abstract class Device extends BaseModel implements IDevice {
587
709
  * @param {number} sample - The sample to calibrate.
588
710
  * @returns {number} The calibrated tare value.
589
711
  * @protected
712
+ *
713
+ * @example
714
+ * const calibratedSample = device.applyTare(rawSample);
715
+ * console.log('Calibrated sample:', calibratedSample);
590
716
  */
591
717
  protected applyTare(sample: number): number {
592
718
  if (this.tareActive && this.tareStartTime) {
@@ -635,7 +761,7 @@ export abstract class Device extends BaseModel implements IDevice {
635
761
  ): Promise<void> => {
636
762
  // Check if not connected or no message is provided
637
763
  if (!this.isConnected() || message === undefined) {
638
- return undefined
764
+ return Promise.resolve()
639
765
  }
640
766
  // Get the characteristic from the service
641
767
  const characteristic = this.getCharacteristic(serviceId, characteristicId)