@hangtime/grip-connect 0.4.2 → 0.5.0

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.
Files changed (167) hide show
  1. package/README.md +49 -40
  2. package/dist/characteristic.d.ts +1 -1
  3. package/dist/commands/forceboard.d.ts +6 -0
  4. package/dist/commands/forceboard.js +5 -0
  5. package/dist/commands/index.d.ts +3 -2
  6. package/dist/commands/index.js +3 -2
  7. package/dist/index.d.ts +1 -13
  8. package/dist/index.js +3 -23
  9. package/dist/interfaces/base.interface.d.ts +5 -0
  10. package/dist/interfaces/device/climbro.interface.d.ts +3 -0
  11. package/dist/interfaces/device/climbro.interface.js +1 -0
  12. package/dist/interfaces/device/entralpi.interface.d.ts +3 -0
  13. package/dist/interfaces/device/entralpi.interface.js +1 -0
  14. package/dist/interfaces/device/forceboard.interface.d.ts +3 -0
  15. package/dist/interfaces/device/forceboard.interface.js +1 -0
  16. package/dist/interfaces/device/kilterboard.interface.d.ts +3 -0
  17. package/dist/interfaces/device/kilterboard.interface.js +1 -0
  18. package/dist/interfaces/device/motherboard.interface.d.ts +10 -0
  19. package/dist/interfaces/device/motherboard.interface.js +1 -0
  20. package/dist/interfaces/device/mysmartboard.interface.d.ts +3 -0
  21. package/dist/interfaces/device/mysmartboard.interface.js +1 -0
  22. package/dist/interfaces/device/progressor.interface.d.ts +3 -0
  23. package/dist/interfaces/device/progressor.interface.js +1 -0
  24. package/dist/interfaces/device/wh-c06.interface.d.ts +3 -0
  25. package/dist/interfaces/device/wh-c06.interface.js +1 -0
  26. package/dist/interfaces/device.interface.d.ts +76 -0
  27. package/dist/interfaces/device.interface.js +1 -0
  28. package/dist/is-device.d.ts +19 -13
  29. package/dist/is-device.js +19 -13
  30. package/dist/models/base.model.d.ts +7 -0
  31. package/dist/models/base.model.js +10 -0
  32. package/dist/models/device/climbro.model.d.ts +9 -0
  33. package/dist/models/device/climbro.model.js +13 -0
  34. package/dist/models/device/entralpi.model.d.ts +43 -0
  35. package/dist/models/device/entralpi.model.js +246 -0
  36. package/dist/models/device/forceboard.model.d.ts +23 -0
  37. package/dist/models/device/forceboard.model.js +201 -0
  38. package/dist/models/device/kilterboard.model.d.ts +85 -0
  39. package/dist/models/device/kilterboard.model.js +213 -0
  40. package/dist/models/device/motherboard.model.d.ts +76 -0
  41. package/dist/models/device/motherboard.model.js +391 -0
  42. package/dist/models/device/mysmartboard.model.d.ts +9 -0
  43. package/dist/models/device/mysmartboard.model.js +13 -0
  44. package/dist/models/device/progressor.model.d.ts +32 -0
  45. package/dist/models/device/progressor.model.js +177 -0
  46. package/dist/models/device/wh-c06.model.d.ts +15 -0
  47. package/dist/models/device/wh-c06.model.js +97 -0
  48. package/dist/models/device.model.d.ts +45 -0
  49. package/dist/models/device.model.js +140 -0
  50. package/dist/models/index.d.ts +8 -0
  51. package/dist/models/index.js +8 -0
  52. package/dist/notify.js +1 -1
  53. package/dist/read.d.ts +1 -1
  54. package/dist/read.js +1 -2
  55. package/dist/stop.d.ts +1 -1
  56. package/dist/stop.js +1 -2
  57. package/dist/write.d.ts +1 -1
  58. package/dist/write.js +1 -2
  59. package/package.json +4 -2
  60. package/src/characteristic.ts +1 -1
  61. package/src/commands/forceboard.ts +6 -0
  62. package/src/commands/index.ts +4 -2
  63. package/src/index.ts +14 -27
  64. package/src/interfaces/base.interface.ts +5 -0
  65. package/src/interfaces/device/climbro.interface.ts +4 -0
  66. package/src/interfaces/device/entralpi.interface.ts +4 -0
  67. package/src/interfaces/device/forceboard.interface.ts +4 -0
  68. package/src/interfaces/device/kilterboard.interface.ts +4 -0
  69. package/src/interfaces/device/motherboard.interface.ts +11 -0
  70. package/src/interfaces/device/mysmartboard.interface.ts +4 -0
  71. package/src/interfaces/device/progressor.interface.ts +4 -0
  72. package/src/interfaces/device/wh-c06.interface.ts +4 -0
  73. package/src/interfaces/device.interface.ts +85 -0
  74. package/src/is-device.ts +23 -16
  75. package/src/models/base.model.ts +16 -0
  76. package/src/models/device/climbro.model.ts +15 -0
  77. package/src/models/device/entralpi.model.ts +264 -0
  78. package/src/models/device/forceboard.model.ts +206 -0
  79. package/src/models/device/kilterboard.model.ts +229 -0
  80. package/src/models/device/motherboard.model.ts +430 -0
  81. package/src/models/device/mysmartboard.model.ts +15 -0
  82. package/src/models/device/progressor.model.ts +184 -0
  83. package/src/models/device/wh-c06.model.ts +118 -0
  84. package/src/models/device.model.ts +159 -0
  85. package/src/models/index.ts +15 -0
  86. package/src/notify.ts +1 -1
  87. package/src/read.ts +2 -3
  88. package/src/stop.ts +2 -3
  89. package/src/write.ts +2 -3
  90. package/dist/battery.d.ts +0 -10
  91. package/dist/battery.js +0 -34
  92. package/dist/calibration.d.ts +0 -7
  93. package/dist/calibration.js +0 -21
  94. package/dist/connect.d.ts +0 -8
  95. package/dist/connect.js +0 -163
  96. package/dist/data/entralpi.d.ts +0 -5
  97. package/dist/data/entralpi.js +0 -33
  98. package/dist/data/index.d.ts +0 -4
  99. package/dist/data/index.js +0 -4
  100. package/dist/data/motherboard.d.ts +0 -10
  101. package/dist/data/motherboard.js +0 -141
  102. package/dist/data/progressor.d.ts +0 -9
  103. package/dist/data/progressor.js +0 -78
  104. package/dist/data/wh-c06.d.ts +0 -5
  105. package/dist/data/wh-c06.js +0 -38
  106. package/dist/devices/climbro.d.ts +0 -6
  107. package/dist/devices/climbro.js +0 -8
  108. package/dist/devices/entralpi.d.ts +0 -5
  109. package/dist/devices/entralpi.js +0 -59
  110. package/dist/devices/index.d.ts +0 -7
  111. package/dist/devices/index.js +0 -7
  112. package/dist/devices/kilterboard.d.ts +0 -10
  113. package/dist/devices/kilterboard.js +0 -34
  114. package/dist/devices/motherboard.d.ts +0 -5
  115. package/dist/devices/motherboard.js +0 -81
  116. package/dist/devices/mysmartboard.d.ts +0 -6
  117. package/dist/devices/mysmartboard.js +0 -8
  118. package/dist/devices/progressor.d.ts +0 -5
  119. package/dist/devices/progressor.js +0 -37
  120. package/dist/devices/wh-c06.d.ts +0 -6
  121. package/dist/devices/wh-c06.js +0 -17
  122. package/dist/disconnect.d.ts +0 -8
  123. package/dist/disconnect.js +0 -14
  124. package/dist/firmware.d.ts +0 -10
  125. package/dist/firmware.js +0 -34
  126. package/dist/hardware.d.ts +0 -9
  127. package/dist/hardware.js +0 -22
  128. package/dist/is-connected.d.ts +0 -7
  129. package/dist/is-connected.js +0 -13
  130. package/dist/led.d.ts +0 -24
  131. package/dist/led.js +0 -195
  132. package/dist/manufacturer.d.ts +0 -9
  133. package/dist/manufacturer.js +0 -22
  134. package/dist/serial.d.ts +0 -9
  135. package/dist/serial.js +0 -27
  136. package/dist/stream.d.ts +0 -8
  137. package/dist/stream.js +0 -41
  138. package/dist/text.d.ts +0 -12
  139. package/dist/text.js +0 -30
  140. package/dist/types/devices.d.ts +0 -38
  141. package/src/battery.ts +0 -36
  142. package/src/calibration.ts +0 -23
  143. package/src/connect.ts +0 -187
  144. package/src/data/entralpi.ts +0 -41
  145. package/src/data/index.ts +0 -7
  146. package/src/data/motherboard.ts +0 -163
  147. package/src/data/progressor.ts +0 -79
  148. package/src/data/wh-c06.ts +0 -47
  149. package/src/devices/climbro.ts +0 -10
  150. package/src/devices/entralpi.ts +0 -61
  151. package/src/devices/index.ts +0 -13
  152. package/src/devices/kilterboard.ts +0 -37
  153. package/src/devices/motherboard.ts +0 -83
  154. package/src/devices/mysmartboard.ts +0 -10
  155. package/src/devices/progressor.ts +0 -38
  156. package/src/devices/wh-c06.ts +0 -19
  157. package/src/disconnect.ts +0 -16
  158. package/src/firmware.ts +0 -36
  159. package/src/hardware.ts +0 -24
  160. package/src/is-connected.ts +0 -15
  161. package/src/led.ts +0 -210
  162. package/src/manufacturer.ts +0 -24
  163. package/src/serial.ts +0 -29
  164. package/src/stream.ts +0 -43
  165. package/src/text.ts +0 -32
  166. package/src/types/devices.ts +0 -39
  167. /package/dist/{types/devices.js → interfaces/base.interface.js} +0 -0
@@ -0,0 +1,9 @@
1
+ import { Device } from "../device.model";
2
+ import type { ImySmartBoard } from "../../interfaces/device/mysmartboard.interface";
3
+ /**
4
+ * Represents a mySmartBoard device
5
+ * TODO: Add services, do you own a mySmartBoard? Help us!
6
+ */
7
+ export declare class mySmartBoard extends Device implements ImySmartBoard {
8
+ constructor();
9
+ }
@@ -0,0 +1,13 @@
1
+ import { Device } from "../device.model";
2
+ /**
3
+ * Represents a mySmartBoard device
4
+ * TODO: Add services, do you own a mySmartBoard? Help us!
5
+ */
6
+ export class mySmartBoard extends Device {
7
+ constructor() {
8
+ super({
9
+ filters: [{ name: "mySmartBoard" }],
10
+ services: [],
11
+ });
12
+ }
13
+ }
@@ -0,0 +1,32 @@
1
+ import { Device } from "../device.model";
2
+ import type { IProgressor } from "../../interfaces/device/progressor.interface";
3
+ /**
4
+ * Represents a Tindeq Progressor device
5
+ */
6
+ export declare class Progressor extends Device implements IProgressor {
7
+ constructor();
8
+ /**
9
+ * Retrieves battery or voltage information from the device.
10
+ * @returns {Promise<string | undefined>} A Promise that resolves with the battery or voltage information,
11
+ */
12
+ battery: () => Promise<string | undefined>;
13
+ /**
14
+ * Retrieves firmware version from the device.
15
+ * @returns {Promise<string>} A Promise that resolves with the firmware version,
16
+ */
17
+ firmware: () => Promise<string | undefined>;
18
+ /**
19
+ * Handles data received from the Progressor device, processes weight measurements,
20
+ * and updates mass data including maximum and average values.
21
+ * It also handles command responses for retrieving device information.
22
+ *
23
+ * @param {Event} event - The notification event.
24
+ */
25
+ handleNotifications: (event: Event) => void;
26
+ /**
27
+ * Starts streaming data from the specified device.
28
+ * @param {number} [duration=0] - The duration of the stream in milliseconds. If set to 0, stream will continue indefinitely.
29
+ * @returns {Promise<void>} A promise that resolves when the streaming operation is completed.
30
+ */
31
+ stream: (duration?: number) => Promise<void>;
32
+ }
@@ -0,0 +1,177 @@
1
+ import { Device } from "../device.model";
2
+ import { notifyCallback } from "../../notify";
3
+ import { applyTare } from "../../tare";
4
+ import { checkActivity } from "../../is-active";
5
+ import { ProgressorCommands, ProgressorResponses } from "../../commands/progressor";
6
+ import struct from "../../struct";
7
+ import { DownloadPackets, emptyDownloadPackets } from "../../download";
8
+ import { lastWrite, write, writeCallback } from "../../write";
9
+ import { stop } from "../../stop";
10
+ // Constants
11
+ let MASS_MAX = "0";
12
+ let MASS_AVERAGE = "0";
13
+ let MASS_TOTAL_SUM = 0;
14
+ let DATAPOINT_COUNT = 0;
15
+ /**
16
+ * Represents a Tindeq Progressor device
17
+ */
18
+ export class Progressor extends Device {
19
+ constructor() {
20
+ super({
21
+ filters: [{ namePrefix: "Progressor" }],
22
+ services: [
23
+ {
24
+ name: "Progressor Service",
25
+ id: "progressor",
26
+ uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57",
27
+ characteristics: [
28
+ {
29
+ name: "Notify",
30
+ id: "rx",
31
+ uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
32
+ },
33
+ {
34
+ name: "Write",
35
+ id: "tx",
36
+ uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57",
37
+ },
38
+ ],
39
+ },
40
+ {
41
+ name: "Nordic Device Firmware Update (DFU) Service",
42
+ id: "dfu",
43
+ uuid: "0000fe59-0000-1000-8000-00805f9b34fb",
44
+ characteristics: [
45
+ {
46
+ name: "Buttonless DFU",
47
+ id: "dfu",
48
+ uuid: "8ec90003-f315-4f60-9fb8-838830daea50",
49
+ },
50
+ ],
51
+ },
52
+ ],
53
+ });
54
+ }
55
+ /**
56
+ * Retrieves battery or voltage information from the device.
57
+ * @returns {Promise<string | undefined>} A Promise that resolves with the battery or voltage information,
58
+ */
59
+ battery = async () => {
60
+ if (this.isConnected()) {
61
+ let response = undefined;
62
+ await write(this, "progressor", "tx", ProgressorCommands.GET_BATT_VLTG, 250, (data) => {
63
+ response = data;
64
+ });
65
+ return response;
66
+ }
67
+ // If device is not found, return undefined
68
+ return undefined;
69
+ };
70
+ /**
71
+ * Retrieves firmware version from the device.
72
+ * @returns {Promise<string>} A Promise that resolves with the firmware version,
73
+ */
74
+ firmware = async () => {
75
+ // Check if the device is connected
76
+ if (this.isConnected()) {
77
+ // Read firmware version from the device
78
+ let response = undefined;
79
+ await write(this, "progressor", "tx", ProgressorCommands.GET_FW_VERSION, 250, (data) => {
80
+ response = data;
81
+ });
82
+ return response;
83
+ }
84
+ // If device is not found, return undefined
85
+ return undefined;
86
+ };
87
+ /**
88
+ * Handles data received from the Progressor device, processes weight measurements,
89
+ * and updates mass data including maximum and average values.
90
+ * It also handles command responses for retrieving device information.
91
+ *
92
+ * @param {Event} event - The notification event.
93
+ */
94
+ handleNotifications = (event) => {
95
+ const characteristic = event.target;
96
+ const value = characteristic.value;
97
+ if (value) {
98
+ if (value.buffer) {
99
+ const buffer = value.buffer;
100
+ const data = new DataView(buffer);
101
+ const receivedTime = Date.now();
102
+ const [kind] = struct("<bb").unpack(data.buffer.slice(0, 2));
103
+ if (kind === ProgressorResponses.WEIGHT_MEASURE) {
104
+ const iterable = struct("<fi").iter_unpack(data.buffer.slice(2));
105
+ // eslint-disable-next-line prefer-const
106
+ for (let [weight, seconds] of iterable) {
107
+ if (typeof weight === "number" && !isNaN(weight) && typeof seconds === "number" && !isNaN(seconds)) {
108
+ // Add data to downloadable Array: sample and mass are the same
109
+ DownloadPackets.push({
110
+ received: receivedTime,
111
+ sampleNum: seconds,
112
+ battRaw: 0,
113
+ samples: [weight],
114
+ masses: [weight],
115
+ });
116
+ // Tare correction
117
+ weight -= applyTare(weight);
118
+ // Check for max weight
119
+ MASS_MAX = Math.max(Number(MASS_MAX), Number(weight)).toFixed(1);
120
+ // Update running sum and count
121
+ const currentMassTotal = Math.max(-1000, Number(weight));
122
+ MASS_TOTAL_SUM += currentMassTotal;
123
+ DATAPOINT_COUNT++;
124
+ // Calculate the average dynamically
125
+ MASS_AVERAGE = (MASS_TOTAL_SUM / DATAPOINT_COUNT).toFixed(1);
126
+ // Check if device is being used
127
+ checkActivity(weight);
128
+ notifyCallback({
129
+ massMax: MASS_MAX,
130
+ massAverage: MASS_AVERAGE,
131
+ massTotal: Math.max(-1000, weight).toFixed(1),
132
+ });
133
+ }
134
+ }
135
+ }
136
+ else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
137
+ if (!lastWrite)
138
+ return;
139
+ let value = "";
140
+ if (lastWrite === ProgressorCommands.GET_BATT_VLTG) {
141
+ value = new DataView(data.buffer, 2).getUint32(0, true).toString();
142
+ }
143
+ else if (lastWrite === ProgressorCommands.GET_FW_VERSION) {
144
+ value = new TextDecoder().decode(data.buffer.slice(2));
145
+ }
146
+ else if (lastWrite === ProgressorCommands.GET_ERR_INFO) {
147
+ value = new TextDecoder().decode(data.buffer.slice(2));
148
+ }
149
+ writeCallback(value);
150
+ }
151
+ else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
152
+ console.warn("⚠️ Low power detected. Please consider connecting to a power source.");
153
+ }
154
+ else {
155
+ throw new Error(`Unknown message kind detected: ${kind}`);
156
+ }
157
+ }
158
+ }
159
+ };
160
+ /**
161
+ * Starts streaming data from the specified device.
162
+ * @param {number} [duration=0] - The duration of the stream in milliseconds. If set to 0, stream will continue indefinitely.
163
+ * @returns {Promise<void>} A promise that resolves when the streaming operation is completed.
164
+ */
165
+ stream = async (duration = 0) => {
166
+ if (this.isConnected()) {
167
+ // Reset download packets
168
+ emptyDownloadPackets();
169
+ // Start streaming data
170
+ await write(this, "progressor", "tx", ProgressorCommands.START_WEIGHT_MEAS, duration);
171
+ // Stop streaming if duration is set
172
+ if (duration !== 0) {
173
+ await stop(this);
174
+ }
175
+ }
176
+ };
177
+ }
@@ -0,0 +1,15 @@
1
+ import { Device } from "../device.model";
2
+ import type { IWHC06 } from "../../interfaces/device/wh-c06.interface";
3
+ /**
4
+ * Represents a Weiheng - WH-C06 (or MAT Muscle Meter) device
5
+ * Enable 'Experimental Web Platform features' Chrome Flags.
6
+ */
7
+ export declare class WHC06 extends Device implements IWHC06 {
8
+ constructor();
9
+ /**
10
+ * Connects to a Bluetooth device.
11
+ * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
12
+ * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
13
+ */
14
+ connect: (onSuccess?: () => void, onError?: (error: Error) => void) => Promise<void>;
15
+ }
@@ -0,0 +1,97 @@
1
+ import { Device } from "../device.model";
2
+ import { applyTare } from "../../tare";
3
+ import { checkActivity } from "../../is-active";
4
+ import { notifyCallback } from "../../notify";
5
+ // Constants
6
+ let MASS_MAX = "0";
7
+ let MASS_AVERAGE = "0";
8
+ let MASS_TOTAL_SUM = 0;
9
+ let DATAPOINT_COUNT = 0;
10
+ const WEIGHT_OFFSET = 10;
11
+ // const STABLE_OFFSET = 14
12
+ /**
13
+ * Represents a Weiheng - WH-C06 (or MAT Muscle Meter) device
14
+ * Enable 'Experimental Web Platform features' Chrome Flags.
15
+ */
16
+ export class WHC06 extends Device {
17
+ constructor() {
18
+ super({
19
+ filters: [
20
+ {
21
+ // namePrefix: "IF_B7",
22
+ manufacturerData: [
23
+ {
24
+ companyIdentifier: 0x0100, // 256
25
+ },
26
+ ],
27
+ },
28
+ ],
29
+ services: [],
30
+ });
31
+ }
32
+ /**
33
+ * Connects to a Bluetooth device.
34
+ * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
35
+ * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
36
+ */
37
+ connect = async (onSuccess = () => console.log("Connected successfully"), onError = (error) => console.error(error)) => {
38
+ try {
39
+ // Only data matching the optionalManufacturerData parameter to requestDevice is included in the advertisement event: https://github.com/WebBluetoothCG/web-bluetooth/issues/598
40
+ const optionalManufacturerData = this.filters.flatMap((filter) => filter.manufacturerData?.map((data) => data.companyIdentifier) || []);
41
+ this.bluetooth = await navigator.bluetooth.requestDevice({
42
+ filters: this.filters,
43
+ optionalManufacturerData,
44
+ });
45
+ if (!this.bluetooth.gatt) {
46
+ throw new Error("GATT is not available on this device");
47
+ }
48
+ // WH-C06
49
+ const MANUFACTURER_ID = 256; // 0x0100
50
+ this.bluetooth.addEventListener("advertisementreceived", (event) => {
51
+ const data = event.manufacturerData.get(MANUFACTURER_ID);
52
+ if (data) {
53
+ // Device has no services / characteristics
54
+ onSuccess();
55
+ // Handle recieved data
56
+ const weight = (data.getUint8(WEIGHT_OFFSET) << 8) | data.getUint8(WEIGHT_OFFSET + 1);
57
+ // const stable = (data.getUint8(STABLE_OFFSET) & 0xf0) >> 4
58
+ // const unit = data.getUint8(STABLE_OFFSET) & 0x0f
59
+ let numericData = weight / 100;
60
+ // Tare correction
61
+ numericData -= applyTare(numericData);
62
+ // Update MASS_MAX
63
+ MASS_MAX = Math.max(Number(MASS_MAX), numericData).toFixed(1);
64
+ // Update running sum and count
65
+ const currentMassTotal = Math.max(-1000, numericData);
66
+ MASS_TOTAL_SUM += currentMassTotal;
67
+ DATAPOINT_COUNT++;
68
+ // Calculate the average dynamically
69
+ MASS_AVERAGE = (MASS_TOTAL_SUM / DATAPOINT_COUNT).toFixed(1);
70
+ // Check if device is being used
71
+ checkActivity(numericData);
72
+ // Notify with weight data
73
+ notifyCallback({
74
+ massMax: MASS_MAX,
75
+ massAverage: MASS_AVERAGE,
76
+ massTotal: Math.max(-1000, numericData).toFixed(1),
77
+ });
78
+ }
79
+ });
80
+ // When the companyIdentifier is provided we want to get manufacturerData using watchAdvertisements.
81
+ if (optionalManufacturerData.length) {
82
+ // Receive events when the system receives an advertisement packet from a watched device.
83
+ // To use this function in Chrome: chrome://flags/#enable-experimental-web-platform-features has to be enabled.
84
+ // More info: https://chromestatus.com/feature/5180688812736512
85
+ if (typeof this.bluetooth.watchAdvertisements === "function") {
86
+ await this.bluetooth.watchAdvertisements();
87
+ }
88
+ else {
89
+ throw new Error("watchAdvertisements isn't supported. For Chrome, enable it at chrome://flags/#enable-experimental-web-platform-features.");
90
+ }
91
+ }
92
+ }
93
+ catch (error) {
94
+ onError(error);
95
+ }
96
+ };
97
+ }
@@ -0,0 +1,45 @@
1
+ import { BaseModel } from "./../models/base.model";
2
+ import type { IDevice, Service } from "../interfaces/device.interface";
3
+ export declare class Device extends BaseModel implements IDevice {
4
+ filters: BluetoothLEScanFilter[];
5
+ services: Service[];
6
+ bluetooth?: BluetoothDevice | undefined;
7
+ constructor(device: Partial<IDevice>);
8
+ /**
9
+ * Handles the 'disconnected' event.
10
+ * @param {Event} event - The 'disconnected' event.
11
+ */
12
+ onDisconnected: (event: Event) => void;
13
+ /**
14
+ * Handles notifications received from a characteristic.
15
+ * @param {Event} event - The notification event.
16
+ */
17
+ handleNotifications: (event: Event) => void;
18
+ /**
19
+ * Handles the 'connected' event.
20
+ * @param {Function} onSuccess - Callback function to execute on successful connection.
21
+ */
22
+ onConnected: (onSuccess: () => void) => Promise<void>;
23
+ /**
24
+ * Returns UUIDs of all services associated with the device.
25
+ * @returns {string[]} Array of service UUIDs.
26
+ */
27
+ getAllServiceUUIDs: () => string[];
28
+ /**
29
+ * Connects to a Bluetooth device.
30
+ * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
31
+ * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
32
+ */
33
+ connect: (onSuccess?: () => void, onError?: (error: Error) => void) => Promise<void>;
34
+ /**
35
+ * Checks if a Bluetooth device is connected.
36
+ * @returns {boolean} A boolean indicating whether the device is connected.
37
+ */
38
+ isConnected: () => boolean;
39
+ /**
40
+ * Disconnects the device if it is currently connected.
41
+ * - Checks if the device is connected via it's GATT server.
42
+ * - If the device is connected, it attempts to gracefully disconnect.
43
+ */
44
+ disconnect: () => void;
45
+ }
@@ -0,0 +1,140 @@
1
+ import { BaseModel } from "./../models/base.model";
2
+ let server;
3
+ export class Device extends BaseModel {
4
+ filters;
5
+ services;
6
+ bluetooth;
7
+ constructor(device) {
8
+ super(device);
9
+ this.filters = device.filters || [];
10
+ this.services = device.services || [];
11
+ this.bluetooth = device.bluetooth;
12
+ }
13
+ /**
14
+ * Handles the 'disconnected' event.
15
+ * @param {Event} event - The 'disconnected' event.
16
+ */
17
+ onDisconnected = (event) => {
18
+ this.bluetooth = undefined;
19
+ const device = event.target;
20
+ throw new Error(`Device ${device.name} is disconnected.`);
21
+ };
22
+ /**
23
+ * Handles notifications received from a characteristic.
24
+ * @param {Event} event - The notification event.
25
+ */
26
+ handleNotifications = (event) => {
27
+ const characteristic = event.target;
28
+ const value = characteristic.value;
29
+ if (value) {
30
+ if (value.buffer) {
31
+ const buffer = value.buffer;
32
+ console.log(new Uint8Array(buffer));
33
+ const rawData = new DataView(buffer);
34
+ console.log(rawData);
35
+ }
36
+ else {
37
+ console.log(value);
38
+ }
39
+ }
40
+ };
41
+ /**
42
+ * Handles the 'connected' event.
43
+ * @param {Function} onSuccess - Callback function to execute on successful connection.
44
+ */
45
+ onConnected = async (onSuccess) => {
46
+ // Connect to GATT server and set up characteristics
47
+ const services = await server.getPrimaryServices();
48
+ if (!services || services.length === 0) {
49
+ throw new Error("No services found");
50
+ }
51
+ for (const service of services) {
52
+ const matchingService = this.services.find((boardService) => boardService.uuid === service.uuid);
53
+ if (matchingService) {
54
+ // Android bug: Introduce a delay before getting characteristics
55
+ await new Promise((resolve) => setTimeout(resolve, 100));
56
+ const characteristics = await service.getCharacteristics();
57
+ for (const characteristic of matchingService.characteristics) {
58
+ const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid);
59
+ if (matchingCharacteristic) {
60
+ const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid);
61
+ if (element) {
62
+ element.characteristic = matchingCharacteristic;
63
+ // notify
64
+ if (element.id === "rx") {
65
+ matchingCharacteristic.startNotifications();
66
+ matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
67
+ this.handleNotifications(event);
68
+ });
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ throw new Error(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`);
74
+ }
75
+ }
76
+ }
77
+ }
78
+ // Call the onSuccess callback after successful connection and setup
79
+ onSuccess();
80
+ };
81
+ /**
82
+ * Returns UUIDs of all services associated with the device.
83
+ * @returns {string[]} Array of service UUIDs.
84
+ */
85
+ getAllServiceUUIDs = () => {
86
+ return this.services.map((service) => service.uuid);
87
+ };
88
+ /**
89
+ * Connects to a Bluetooth device.
90
+ * @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
91
+ * @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
92
+ */
93
+ connect = async (onSuccess = () => console.log("Connected successfully"), onError = (error) => console.error(error)) => {
94
+ try {
95
+ // Request device and set up connection
96
+ const deviceServices = this.getAllServiceUUIDs();
97
+ this.bluetooth = await navigator.bluetooth.requestDevice({
98
+ filters: this.filters,
99
+ optionalServices: deviceServices,
100
+ });
101
+ if (!this.bluetooth.gatt) {
102
+ throw new Error("GATT is not available on this device");
103
+ }
104
+ this.bluetooth.addEventListener("gattserverdisconnected", (event) => {
105
+ this.onDisconnected(event);
106
+ });
107
+ server = await this.bluetooth.gatt.connect();
108
+ if (server.connected) {
109
+ await this.onConnected(onSuccess);
110
+ }
111
+ }
112
+ catch (error) {
113
+ onError(error);
114
+ }
115
+ };
116
+ /**
117
+ * Checks if a Bluetooth device is connected.
118
+ * @returns {boolean} A boolean indicating whether the device is connected.
119
+ */
120
+ isConnected = () => {
121
+ // Check if the device is defined and available
122
+ if (!this?.bluetooth) {
123
+ return false;
124
+ }
125
+ // Check if the device is connected
126
+ return !!this.bluetooth.gatt?.connected;
127
+ };
128
+ /**
129
+ * Disconnects the device if it is currently connected.
130
+ * - Checks if the device is connected via it's GATT server.
131
+ * - If the device is connected, it attempts to gracefully disconnect.
132
+ */
133
+ disconnect = () => {
134
+ // Verify that the device is connected using the provided helper function
135
+ if (this.isConnected()) {
136
+ // Safely attempt to disconnect the device's GATT server, if available
137
+ this.bluetooth?.gatt?.disconnect();
138
+ }
139
+ };
140
+ }
@@ -0,0 +1,8 @@
1
+ export { Climbro } from "./device/climbro.model";
2
+ export { Entralpi } from "./device/entralpi.model";
3
+ export { ForceBoard } from "./device/forceboard.model";
4
+ export { KilterBoard } from "./device/kilterboard.model";
5
+ export { Motherboard } from "./device/motherboard.model";
6
+ export { mySmartBoard } from "./device/mysmartboard.model";
7
+ export { Progressor } from "./device/progressor.model";
8
+ export { WHC06 } from "./device/wh-c06.model";
@@ -0,0 +1,8 @@
1
+ export { Climbro } from "./device/climbro.model";
2
+ export { Entralpi } from "./device/entralpi.model";
3
+ export { ForceBoard } from "./device/forceboard.model";
4
+ export { KilterBoard } from "./device/kilterboard.model";
5
+ export { Motherboard } from "./device/motherboard.model";
6
+ export { mySmartBoard } from "./device/mysmartboard.model";
7
+ export { Progressor } from "./device/progressor.model";
8
+ export { WHC06 } from "./device/wh-c06.model";
package/dist/notify.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * @callback NotifyCallback
4
4
  * @param {massObject} data - The data passed to the callback.
5
5
  */
6
- export let notifyCallback;
6
+ export let notifyCallback = (data) => console.log(data);
7
7
  /**
8
8
  * Sets the callback function to be called when notifications are received.
9
9
  * @param {NotifyCallback} callback - The callback function to be set.
package/dist/read.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Device } from "./types/devices";
1
+ import type { Device } from "./models/device.model";
2
2
  /**
3
3
  * Reads the value of the specified characteristic from the device.
4
4
  * @param {Device} board - The device to read from.
package/dist/read.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { getCharacteristic } from "./characteristic";
2
- import { isConnected } from "./is-connected";
3
2
  /**
4
3
  * Reads the value of the specified characteristic from the device.
5
4
  * @param {Device} board - The device to read from.
@@ -10,7 +9,7 @@ import { isConnected } from "./is-connected";
10
9
  */
11
10
  export const read = (board, serviceId, characteristicId, duration = 0) => {
12
11
  return new Promise((resolve, reject) => {
13
- if (isConnected(board)) {
12
+ if (board.isConnected()) {
14
13
  const characteristic = getCharacteristic(board, serviceId, characteristicId);
15
14
  if (characteristic) {
16
15
  characteristic
package/dist/stop.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Device } from "./types/devices";
1
+ import type { Device } from "./models/device.model";
2
2
  /**
3
3
  * Stops the data stream on the specified device.
4
4
  * @param {Device} board - The device to stop the stream on.
package/dist/stop.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { write } from "./write";
2
- import { isConnected } from "./is-connected";
3
2
  import { MotherboardCommands, ProgressorCommands } from "./commands";
4
3
  import { isMotherboard, isProgressor } from "./is-device";
5
4
  /**
@@ -8,7 +7,7 @@ import { isMotherboard, isProgressor } from "./is-device";
8
7
  * @returns {Promise<void>} A promise that resolves when the stream is stopped.
9
8
  */
10
9
  export const stop = async (board) => {
11
- if (isConnected(board)) {
10
+ if (board.isConnected()) {
12
11
  if (isMotherboard(board)) {
13
12
  // Stop stream on Motherboard
14
13
  await write(board, "uart", "tx", MotherboardCommands.STOP_WEIGHT_MEAS, 0);
package/dist/write.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Device } from "./types/devices";
1
+ import type { Device } from "./models/device.model";
2
2
  /**
3
3
  * The last message written to the device.
4
4
  * @type {string | Uint8Array | null}
package/dist/write.js CHANGED
@@ -1,4 +1,3 @@
1
- import { isConnected } from "./is-connected";
2
1
  import { getCharacteristic } from "./characteristic";
3
2
  /**
4
3
  * The last message written to the device.
@@ -32,7 +31,7 @@ export let writeCallback = (data) => {
32
31
  * });
33
32
  */
34
33
  export const write = async (board, serviceId, characteristicId, message, duration = 0, callback = writeCallback) => {
35
- if (isConnected(board)) {
34
+ if (board.isConnected()) {
36
35
  // Check if message is provided
37
36
  if (message === undefined) {
38
37
  // If not provided, return without performing write operation
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.4.2",
4
- "description": "A client that can establish connections with various Force-Sensing Hangboards/Plates used by climbers for strength measurement. Examples of such hangboards include the Griptonite Motherboard, Climbro, SmartBoard, Entralpi or Tindeq Progressor",
3
+ "version": "0.5.0",
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",
7
7
  "scripts": {
@@ -23,6 +23,8 @@
23
23
  "motherboard",
24
24
  "tindeq",
25
25
  "progressor",
26
+ "pitchsix",
27
+ "forceboard",
26
28
  "entralpi",
27
29
  "wh-c06",
28
30
  "kilterboard"
@@ -1,4 +1,4 @@
1
- import type { Device } from "./types/devices"
1
+ import type { Device } from "./models/device.model"
2
2
 
3
3
  /**
4
4
  * Retrieves the characteristic from the device's service.
@@ -0,0 +1,6 @@
1
+ import type { Commands } from "../types/commands"
2
+ /**
3
+ * Warning:
4
+ * Using other commands can seriously harm your device
5
+ */
6
+ export const ForceBoardCommands: Commands = {}