@hangtime/grip-connect 0.6.1 → 0.7.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.
- package/README.md +3 -3
- package/deno.json +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/interfaces/command.interface.d.ts +6 -0
- package/dist/interfaces/device/climbro.interface.d.ts +1 -1
- package/dist/interfaces/device/entralpi.interface.d.ts +1 -1
- package/dist/interfaces/device/forceboard.interface.d.ts +30 -4
- package/dist/interfaces/device/kilterboard.interface.d.ts +1 -1
- package/dist/interfaces/device/motherboard.interface.d.ts +1 -1
- package/dist/interfaces/device/mysmartboard.interface.d.ts +1 -1
- package/dist/interfaces/device/progressor.interface.d.ts +1 -1
- package/dist/interfaces/device/wh-c06.interface.d.ts +1 -1
- package/dist/interfaces/device.interface.d.ts +3 -3
- package/dist/interfaces/index.d.ts +8 -8
- package/dist/models/base.model.d.ts +1 -1
- package/dist/models/device/climbro.model.d.ts +2 -2
- package/dist/models/device/climbro.model.js +1 -1
- package/dist/models/device/entralpi.model.d.ts +2 -2
- package/dist/models/device/entralpi.model.js +1 -1
- package/dist/models/device/forceboard.model.d.ts +29 -5
- package/dist/models/device/forceboard.model.js +95 -52
- package/dist/models/device/kilterboard.model.d.ts +3 -3
- package/dist/models/device/kilterboard.model.js +2 -2
- package/dist/models/device/motherboard.model.d.ts +2 -2
- package/dist/models/device/motherboard.model.js +1 -1
- package/dist/models/device/mysmartboard.model.d.ts +2 -2
- package/dist/models/device/mysmartboard.model.js +1 -1
- package/dist/models/device/progressor.model.d.ts +2 -2
- package/dist/models/device/progressor.model.js +1 -1
- package/dist/models/device/wh-c06.model.d.ts +2 -2
- package/dist/models/device/wh-c06.model.js +1 -1
- package/dist/models/device.model.d.ts +13 -23
- package/dist/models/device.model.js +51 -64
- package/dist/models/index.d.ts +8 -8
- package/dist/models/index.js +8 -8
- package/package.json +3 -3
- package/src/index.ts +2 -2
- package/src/interfaces/command.interface.ts +9 -0
- package/src/interfaces/device/climbro.interface.ts +1 -1
- package/src/interfaces/device/entralpi.interface.ts +1 -1
- package/src/interfaces/device/forceboard.interface.ts +34 -4
- package/src/interfaces/device/kilterboard.interface.ts +1 -1
- package/src/interfaces/device/motherboard.interface.ts +1 -1
- package/src/interfaces/device/mysmartboard.interface.ts +1 -1
- package/src/interfaces/device/progressor.interface.ts +1 -1
- package/src/interfaces/device/wh-c06.interface.ts +1 -1
- package/src/interfaces/device.interface.ts +3 -3
- package/src/interfaces/index.ts +8 -8
- package/src/models/base.model.ts +1 -1
- package/src/models/device/climbro.model.ts +2 -2
- package/src/models/device/entralpi.model.ts +2 -2
- package/src/models/device/forceboard.model.ts +104 -53
- package/src/models/device/kilterboard.model.ts +3 -3
- package/src/models/device/motherboard.model.ts +3 -3
- package/src/models/device/mysmartboard.model.ts +2 -2
- package/src/models/device/progressor.model.ts +2 -2
- package/src/models/device/wh-c06.model.ts +2 -2
- package/src/models/device.model.ts +60 -77
- package/src/models/index.ts +8 -8
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Device } from "../device.model
|
|
2
|
-
import type { IForceBoard } from "../../interfaces/device/forceboard.interface
|
|
1
|
+
import { Device } from "../device.model"
|
|
2
|
+
import type { IForceBoard } from "../../interfaces/device/forceboard.interface"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Represents a PitchSix Force Board device.
|
|
@@ -108,13 +108,13 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
108
108
|
uuid: "9a88d681-8df2-4afe-9e0d-c2bbbe773dd0",
|
|
109
109
|
},
|
|
110
110
|
{
|
|
111
|
-
name: "
|
|
111
|
+
name: "Force Data",
|
|
112
112
|
id: "rx",
|
|
113
113
|
uuid: "9a88d682-8df2-4afe-9e0d-c2bbbe773dd0",
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
|
-
name: "
|
|
117
|
-
id: "",
|
|
116
|
+
name: "Tare",
|
|
117
|
+
id: "tare",
|
|
118
118
|
uuid: "9a88d683-8df2-4afe-9e0d-c2bbbe773dd0",
|
|
119
119
|
},
|
|
120
120
|
{
|
|
@@ -123,8 +123,8 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
123
123
|
uuid: "9a88d685-8df2-4afe-9e0d-c2bbbe773dd0",
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
|
-
name: "
|
|
127
|
-
id: "",
|
|
126
|
+
name: "Threshold",
|
|
127
|
+
id: "threshold",
|
|
128
128
|
uuid: "9a88d686-8df2-4afe-9e0d-c2bbbe773dd0",
|
|
129
129
|
},
|
|
130
130
|
{
|
|
@@ -145,12 +145,12 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
145
145
|
],
|
|
146
146
|
},
|
|
147
147
|
{
|
|
148
|
-
name: "Weight
|
|
148
|
+
name: "Weight Service",
|
|
149
149
|
id: "weight",
|
|
150
150
|
uuid: "467a8516-6e39-11eb-9439-0242ac130002",
|
|
151
151
|
characteristics: [
|
|
152
152
|
{
|
|
153
|
-
name: "
|
|
153
|
+
name: "Device Mode",
|
|
154
154
|
id: "tx",
|
|
155
155
|
uuid: "467a8517-6e39-11eb-9439-0242ac130002",
|
|
156
156
|
},
|
|
@@ -163,7 +163,10 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
163
163
|
},
|
|
164
164
|
],
|
|
165
165
|
commands: {
|
|
166
|
-
|
|
166
|
+
START_WEIGHT_MEAS: String.fromCharCode(0x04), // Streaming Data Mode: continuously streams force data.
|
|
167
|
+
TARE_SCALE: String.fromCharCode(0x05), // Tare function: zeroes out the current load value
|
|
168
|
+
START_QUICK_MEAS: String.fromCharCode(0x06), // Quick Start Mode: Starts data transmission when a force value exceeds the Threshold and stops data transmission when the force data drops below the Threshold.
|
|
169
|
+
STOP_WEIGHT_MEAS: String.fromCharCode(0x07), // Idle Mode: Force Board is idle.
|
|
167
170
|
},
|
|
168
171
|
})
|
|
169
172
|
}
|
|
@@ -191,43 +194,51 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
191
194
|
if (value.buffer) {
|
|
192
195
|
const receivedTime: number = Date.now()
|
|
193
196
|
const dataArray = new Uint8Array(value.buffer)
|
|
194
|
-
// Skip the first 2 bytes, which are the command and length
|
|
195
|
-
// The data is sent in groups of 3 bytes
|
|
196
|
-
for (let i = 2; i < dataArray.length; i += 3) {
|
|
197
|
-
const receivedData = (dataArray[i] << 16) | (dataArray[i + 1] << 8) | dataArray[i + 2]
|
|
198
|
-
// Convert from LBS to KG
|
|
199
|
-
const convertedReceivedData = receivedData * 0.453592
|
|
200
|
-
// Tare correction
|
|
201
|
-
const numericData = convertedReceivedData - this.applyTare(convertedReceivedData)
|
|
202
|
-
// Add data to downloadable Array
|
|
203
|
-
this.downloadPackets.push({
|
|
204
|
-
received: receivedTime,
|
|
205
|
-
sampleNum: this.dataPointCount,
|
|
206
|
-
battRaw: 0,
|
|
207
|
-
samples: [convertedReceivedData],
|
|
208
|
-
masses: [numericData],
|
|
209
|
-
})
|
|
210
197
|
|
|
211
|
-
|
|
212
|
-
|
|
198
|
+
// First two bytes contain the number of samples in the packet
|
|
199
|
+
const numSamples = (dataArray[0] << 8) | dataArray[1]
|
|
200
|
+
|
|
201
|
+
// Process each sample (3 bytes per sample)
|
|
202
|
+
for (let i = 0; i < numSamples; i++) {
|
|
203
|
+
const offset = 2 + i * 3 // Skip the first 2 bytes which indicate number of samples
|
|
204
|
+
if (offset + 2 < dataArray.length) {
|
|
205
|
+
// Sample = byte1*32768 + byte2*256 + byte3
|
|
206
|
+
const receivedData = dataArray[offset] * 32768 + dataArray[offset + 1] * 256 + dataArray[offset + 2]
|
|
207
|
+
|
|
208
|
+
// Convert from LBS to KG
|
|
209
|
+
const convertedReceivedData = receivedData * 0.453592
|
|
210
|
+
// Tare correction
|
|
211
|
+
const numericData = convertedReceivedData - this.applyTare(convertedReceivedData)
|
|
212
|
+
// Add data to downloadable Array
|
|
213
|
+
this.downloadPackets.push({
|
|
214
|
+
received: receivedTime,
|
|
215
|
+
sampleNum: this.dataPointCount,
|
|
216
|
+
battRaw: 0,
|
|
217
|
+
samples: [convertedReceivedData],
|
|
218
|
+
masses: [numericData],
|
|
219
|
+
})
|
|
213
220
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.massTotalSum += currentMassTotal
|
|
217
|
-
this.dataPointCount++
|
|
221
|
+
// Update massMax
|
|
222
|
+
this.massMax = Math.max(Number(this.massMax), numericData).toFixed(1)
|
|
218
223
|
|
|
219
|
-
|
|
220
|
-
|
|
224
|
+
// Update running sum and count
|
|
225
|
+
const currentMassTotal = Math.max(-1000, numericData)
|
|
226
|
+
this.massTotalSum += currentMassTotal
|
|
227
|
+
this.dataPointCount++
|
|
221
228
|
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
// Calculate the average dynamically
|
|
230
|
+
this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1)
|
|
224
231
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
232
|
+
// Check if device is being used
|
|
233
|
+
this.activityCheck(numericData)
|
|
234
|
+
|
|
235
|
+
// Notify with weight data
|
|
236
|
+
this.notifyCallback({
|
|
237
|
+
massMax: this.massMax,
|
|
238
|
+
massAverage: this.massAverage,
|
|
239
|
+
massTotal: Math.max(-1000, numericData).toFixed(1),
|
|
240
|
+
})
|
|
241
|
+
}
|
|
231
242
|
}
|
|
232
243
|
}
|
|
233
244
|
}
|
|
@@ -250,7 +261,7 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
250
261
|
}
|
|
251
262
|
|
|
252
263
|
/**
|
|
253
|
-
* Stops the data stream on the specified device.
|
|
264
|
+
* Stops the data stream on the specified device by setting it to Idle mode.
|
|
254
265
|
* @returns {Promise<void>} A promise that resolves when the stream is stopped.
|
|
255
266
|
*/
|
|
256
267
|
stop = async (): Promise<void> => {
|
|
@@ -258,26 +269,66 @@ export class ForceBoard extends Device implements IForceBoard {
|
|
|
258
269
|
}
|
|
259
270
|
|
|
260
271
|
/**
|
|
261
|
-
* Starts streaming data from the specified device.
|
|
272
|
+
* Starts streaming data from the specified device in Streaming Data Mode.
|
|
262
273
|
* @param {number} [duration=0] - The duration of the stream in milliseconds. If set to 0, stream will continue indefinitely.
|
|
263
274
|
* @returns {Promise<void>} A promise that resolves when the streaming operation is completed.
|
|
264
275
|
*/
|
|
265
276
|
stream = async (duration = 0): Promise<void> => {
|
|
266
|
-
//
|
|
267
|
-
this.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
// Start streaming data - Streaming Data Mode
|
|
278
|
+
await this.write("weight", "tx", this.commands.START_WEIGHT_MEAS, duration)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Sets the threshold in Lbs for the Quick Start mode.
|
|
283
|
+
* @param {number} thresholdLbs - The threshold value in pounds.
|
|
284
|
+
* @returns {Promise<void>} A promise that resolves when the threshold is set.
|
|
285
|
+
*/
|
|
286
|
+
threshold = async (thresholdLbs: number): Promise<void> => {
|
|
287
|
+
const thresholdHex = thresholdLbs.toString(16).padStart(6, "0")
|
|
288
|
+
// 3-byte array from the hex string
|
|
289
|
+
const bytes = new Uint8Array(3)
|
|
290
|
+
bytes[0] = parseInt(thresholdHex.substring(0, 2), 16)
|
|
291
|
+
bytes[1] = parseInt(thresholdHex.substring(2, 4), 16)
|
|
292
|
+
bytes[2] = parseInt(thresholdHex.substring(4, 6), 16)
|
|
293
|
+
|
|
294
|
+
await this.write("forceboard", "threshold", String.fromCharCode(...bytes), 0)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Tares the Force Board device using a characteristic to zero out the current load value.
|
|
299
|
+
* @returns {Promise<void>} A promise that resolves when the tare operation is completed.
|
|
300
|
+
*/
|
|
301
|
+
tareByCharacteristic = async (): Promise<void> => {
|
|
302
|
+
// Send tare command (0x01) to the tare characteristic
|
|
303
|
+
const tareValue = String.fromCharCode(0x01)
|
|
304
|
+
await this.write("forceboard", "tare", tareValue, 0)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Initiates a tare routine via the Device Mode characteristic.
|
|
309
|
+
* Writes 0x05 to the Device Mode characteristic to zero out the current load value.
|
|
310
|
+
* @returns {Promise<void>} A promise that resolves when the tare operation is completed.
|
|
311
|
+
*/
|
|
312
|
+
tareByMode = async (): Promise<void> => {
|
|
313
|
+
await this.write("weight", "tx", this.commands.TARE_SCALE, 0)
|
|
274
314
|
}
|
|
275
315
|
|
|
276
316
|
/**
|
|
277
317
|
* Retrieves temperature information from the device.
|
|
278
|
-
* @returns {Promise<string>} A Promise that resolves with the
|
|
318
|
+
* @returns {Promise<string>} A Promise that resolves with the temperature information,
|
|
279
319
|
*/
|
|
280
320
|
temperature = async (): Promise<string | undefined> => {
|
|
281
321
|
return await this.read("temperature", "level", 250)
|
|
282
322
|
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Starts the Force Board in Quick Start mode.
|
|
326
|
+
* Writes 0x06 to the Device Mode characteristic.
|
|
327
|
+
* @param {number} [duration=0] - The duration in milliseconds. If set to 0, mode will continue indefinitely.
|
|
328
|
+
* @returns {Promise<void>} A promise that resolves when the operation is completed.
|
|
329
|
+
*/
|
|
330
|
+
quick = async (duration = 0): Promise<void> => {
|
|
331
|
+
// Start in Quick Start mode
|
|
332
|
+
await this.write("weight", "tx", this.commands.START_QUICK_MEAS, duration)
|
|
333
|
+
}
|
|
283
334
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Device } from "../device.model
|
|
2
|
-
import type { IKilterBoard } from "../../interfaces/device/kilterboard.interface
|
|
1
|
+
import { Device } from "../device.model"
|
|
2
|
+
import type { IKilterBoard } from "../../interfaces/device/kilterboard.interface"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* For API level 2 and API level 3.
|
|
@@ -254,7 +254,7 @@ export class KilterBoard extends Device implements IKilterBoard {
|
|
|
254
254
|
|
|
255
255
|
/**
|
|
256
256
|
* Splits a collection into slices of the specified length.
|
|
257
|
-
* https://github.com/ramda/ramda/blob/master/source/splitEvery
|
|
257
|
+
* https://github.com/ramda/ramda/blob/master/source/splitEvery
|
|
258
258
|
* @param {Number} n
|
|
259
259
|
* @param {Array} list
|
|
260
260
|
* @return {Array<number[]>}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Device } from "../device.model
|
|
2
|
-
import type { IMotherboard } from "../../interfaces/device/motherboard.interface
|
|
3
|
-
import type { DownloadPacket } from "../../interfaces/download.interface
|
|
1
|
+
import { Device } from "../device.model"
|
|
2
|
+
import type { IMotherboard } from "../../interfaces/device/motherboard.interface"
|
|
3
|
+
import type { DownloadPacket } from "../../interfaces/download.interface"
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Represents a Griptonite Motherboard device.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Device } from "../device.model
|
|
2
|
-
import type { ImySmartBoard } from "../../interfaces/device/mysmartboard.interface
|
|
1
|
+
import { Device } from "../device.model"
|
|
2
|
+
import type { ImySmartBoard } from "../../interfaces/device/mysmartboard.interface"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Represents a Smartboard Climbing mySmartBoard device.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Device } from "../device.model
|
|
2
|
-
import type { IProgressor } from "../../interfaces/device/progressor.interface
|
|
1
|
+
import { Device } from "../device.model"
|
|
2
|
+
import type { IProgressor } from "../../interfaces/device/progressor.interface"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Progressor responses
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Device } from "../device.model
|
|
2
|
-
import type { IWHC06 } from "../../interfaces/device/wh-c06.interface
|
|
1
|
+
import { Device } from "../device.model"
|
|
2
|
+
import type { IWHC06 } from "../../interfaces/device/wh-c06.interface"
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Represents a Weiheng - WH-C06 (or MAT Muscle Meter) device.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { BaseModel } from "./../models/base.model
|
|
2
|
-
import type { IDevice, Service } from "../interfaces/device.interface
|
|
3
|
-
import type { ActiveCallback, massObject, NotifyCallback, WriteCallback } from "../interfaces/callback.interface
|
|
4
|
-
import type { DownloadPacket } from "../interfaces/download.interface
|
|
5
|
-
import type { Commands } from "../interfaces/command.interface
|
|
1
|
+
import { BaseModel } from "./../models/base.model"
|
|
2
|
+
import type { IDevice, Service } from "../interfaces/device.interface"
|
|
3
|
+
import type { ActiveCallback, massObject, NotifyCallback, WriteCallback } from "../interfaces/callback.interface"
|
|
4
|
+
import type { DownloadPacket } from "../interfaces/download.interface"
|
|
5
|
+
import type { Commands } from "../interfaces/command.interface"
|
|
6
6
|
|
|
7
7
|
export abstract class Device extends BaseModel implements IDevice {
|
|
8
8
|
/**
|
|
@@ -29,7 +29,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
29
29
|
* @type {BluetoothDevice | undefined}
|
|
30
30
|
* @public
|
|
31
31
|
*/
|
|
32
|
-
bluetooth?: BluetoothDevice
|
|
32
|
+
bluetooth?: BluetoothDevice
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Object representing the set of commands available for this device.
|
|
@@ -240,22 +240,16 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
240
240
|
* @example
|
|
241
241
|
* await device.activityCheck(5.0);
|
|
242
242
|
*/
|
|
243
|
-
protected activityCheck = (input: number): Promise<void> => {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
this.activeCallback(activeNow)
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
resolve()
|
|
257
|
-
}, duration)
|
|
258
|
-
})
|
|
243
|
+
protected activityCheck = async (input: number): Promise<void> => {
|
|
244
|
+
const startValue = input
|
|
245
|
+
const { threshold, duration } = this.activeConfig
|
|
246
|
+
// After waiting for `duration`, check if still active.
|
|
247
|
+
await new Promise((resolve) => setTimeout(resolve, duration))
|
|
248
|
+
const activeNow = startValue > threshold
|
|
249
|
+
if (this.isActive !== activeNow) {
|
|
250
|
+
this.isActive = activeNow
|
|
251
|
+
this.activeCallback?.(activeNow)
|
|
252
|
+
}
|
|
259
253
|
}
|
|
260
254
|
|
|
261
255
|
/**
|
|
@@ -319,7 +313,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
319
313
|
// Remove all notification listeners
|
|
320
314
|
this.services.forEach((service) => {
|
|
321
315
|
service.characteristics.forEach((char) => {
|
|
322
|
-
//
|
|
316
|
+
// Look for the "rx" characteristic that accepts notifications
|
|
323
317
|
if (char.characteristic && char.id === "rx") {
|
|
324
318
|
char.characteristic.stopNotifications()
|
|
325
319
|
const listener = this.notificationListeners.get(char.uuid)
|
|
@@ -494,53 +488,30 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
494
488
|
}
|
|
495
489
|
|
|
496
490
|
/**
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
491
|
+
* Returns the Bluetooth instance available for the current environment.
|
|
492
|
+
* In browsers, it returns the native Web Bluetooth API (i.e. `navigator.bluetooth`).
|
|
493
|
+
* In a Node, Bun, or Deno environment, it dynamically imports the `webbluetooth` package.
|
|
494
|
+
* {@link https://github.com/thegecko/webbluetooth}
|
|
495
|
+
*
|
|
496
|
+
* @returns {Promise<Bluetooth>} A promise that resolves to the Bluetooth instance.
|
|
497
|
+
* @throws {Error} If Web Bluetooth is not available in the current environment.
|
|
500
498
|
*/
|
|
501
|
-
protected async getBluetooth() {
|
|
502
|
-
// If
|
|
503
|
-
if (typeof navigator !== "undefined" &&
|
|
499
|
+
protected async getBluetooth(): Promise<Bluetooth> {
|
|
500
|
+
// If running in a browser with native Web Bluetooth support:
|
|
501
|
+
if (typeof navigator !== "undefined" && navigator.bluetooth) {
|
|
504
502
|
return navigator.bluetooth
|
|
505
503
|
}
|
|
506
504
|
|
|
507
|
-
|
|
508
|
-
// Use a dynamic import for the ESM version:
|
|
509
|
-
const { bluetooth } = await import("webbluetooth")
|
|
510
|
-
return bluetooth
|
|
511
|
-
}
|
|
505
|
+
const process = await import("node:process")
|
|
512
506
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
* @returns {BluetoothRemoteGATTCharacteristic | undefined} The characteristic, if found.
|
|
518
|
-
* @protected
|
|
519
|
-
*
|
|
520
|
-
* @example
|
|
521
|
-
* const characteristic = device.getCharacteristic('battery', 'level');
|
|
522
|
-
* if (characteristic) {
|
|
523
|
-
* console.log('Characteristic found');
|
|
524
|
-
* }
|
|
525
|
-
*/
|
|
526
|
-
protected getCharacteristic = (
|
|
527
|
-
serviceId: string,
|
|
528
|
-
characteristicId: string,
|
|
529
|
-
): BluetoothRemoteGATTCharacteristic | undefined => {
|
|
530
|
-
// Find the service with the specified serviceId
|
|
531
|
-
const boardService = this.services.find((service) => service.id === serviceId)
|
|
532
|
-
if (boardService) {
|
|
533
|
-
// If the service is found, find the characteristic with the specified characteristicId
|
|
534
|
-
const boardCharacteristic = boardService.characteristics.find(
|
|
535
|
-
(characteristic) => characteristic.id === characteristicId,
|
|
536
|
-
)
|
|
537
|
-
if (boardCharacteristic) {
|
|
538
|
-
// If the characteristic is found, return it
|
|
539
|
-
return boardCharacteristic.characteristic
|
|
540
|
-
}
|
|
507
|
+
// If running in Node, Bun, or Deno environment
|
|
508
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
509
|
+
const { bluetooth } = await import("webbluetooth")
|
|
510
|
+
return bluetooth
|
|
541
511
|
}
|
|
542
|
-
|
|
543
|
-
|
|
512
|
+
|
|
513
|
+
// If none of the above conditions are met, throw an error.
|
|
514
|
+
throw new Error("Bluetooth not available.")
|
|
544
515
|
}
|
|
545
516
|
|
|
546
517
|
/**
|
|
@@ -573,7 +544,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
573
544
|
*/
|
|
574
545
|
isConnected = (): boolean => {
|
|
575
546
|
// Check if the device is defined and available
|
|
576
|
-
if (!this
|
|
547
|
+
if (!this.bluetooth) {
|
|
577
548
|
return false
|
|
578
549
|
}
|
|
579
550
|
// Check if the device is connected
|
|
@@ -612,7 +583,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
612
583
|
throw new Error("GATT server is not available")
|
|
613
584
|
}
|
|
614
585
|
// Connect to GATT server and set up characteristics
|
|
615
|
-
const services: BluetoothRemoteGATTService[]
|
|
586
|
+
const services: BluetoothRemoteGATTService[] = await this.server.getPrimaryServices()
|
|
616
587
|
|
|
617
588
|
if (!services || services.length === 0) {
|
|
618
589
|
throw new Error("No services found")
|
|
@@ -622,7 +593,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
622
593
|
const matchingService = this.services.find((boardService) => boardService.uuid === service.uuid)
|
|
623
594
|
|
|
624
595
|
if (matchingService) {
|
|
625
|
-
// Android bug:
|
|
596
|
+
// Android bug: Add a small delay before getting characteristics
|
|
626
597
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
627
598
|
|
|
628
599
|
const characteristics = await service.getCharacteristics()
|
|
@@ -631,21 +602,28 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
631
602
|
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid)
|
|
632
603
|
|
|
633
604
|
if (matchingCharacteristic) {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
605
|
+
// Find the corresponding characteristic descriptor in the service's characteristics array
|
|
606
|
+
const descriptor = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid)
|
|
607
|
+
if (descriptor) {
|
|
608
|
+
// Assign the actual Bluetooth characteristic object to the descriptor so it can be used later
|
|
609
|
+
descriptor.characteristic = matchingCharacteristic
|
|
610
|
+
// Look for the "rx" characteristic id that accepts notifications
|
|
611
|
+
if (descriptor.id === "rx") {
|
|
612
|
+
// Start receiving notifications for changes on this characteristic
|
|
640
613
|
matchingCharacteristic.startNotifications()
|
|
614
|
+
// Triggered when the characteristic's value changes
|
|
641
615
|
const listener = (event: Event) => {
|
|
616
|
+
// Cast the event's target to a BluetoothRemoteGATTCharacteristic to access its properties
|
|
642
617
|
const target = event.target as BluetoothRemoteGATTCharacteristic
|
|
643
618
|
if (target && target.value) {
|
|
619
|
+
// Delegate the data to handleNotifications method
|
|
644
620
|
this.handleNotifications(target)
|
|
645
621
|
}
|
|
646
622
|
}
|
|
623
|
+
// Attach the event listener to listen for changes in the characteristic's value
|
|
647
624
|
matchingCharacteristic.addEventListener("characteristicvaluechanged", listener)
|
|
648
|
-
|
|
625
|
+
// Store the listener so it can be referenced (for later removal)
|
|
626
|
+
this.notificationListeners.set(descriptor.uuid, listener)
|
|
649
627
|
}
|
|
650
628
|
}
|
|
651
629
|
} else {
|
|
@@ -654,7 +632,6 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
654
632
|
}
|
|
655
633
|
}
|
|
656
634
|
}
|
|
657
|
-
|
|
658
635
|
// Call the onSuccess callback after successful connection and setup
|
|
659
636
|
onSuccess()
|
|
660
637
|
}
|
|
@@ -689,7 +666,10 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
689
666
|
return undefined
|
|
690
667
|
}
|
|
691
668
|
// Get the characteristic from the service
|
|
692
|
-
const characteristic = this.
|
|
669
|
+
const characteristic = this.services
|
|
670
|
+
.find((service) => service.id === serviceId)
|
|
671
|
+
?.characteristics.find((char) => char.id === characteristicId)?.characteristic
|
|
672
|
+
|
|
693
673
|
if (!characteristic) {
|
|
694
674
|
throw new Error(`Characteristic "${characteristicId}" not found in service "${serviceId}"`)
|
|
695
675
|
}
|
|
@@ -815,7 +795,10 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
815
795
|
return Promise.resolve()
|
|
816
796
|
}
|
|
817
797
|
// Get the characteristic from the service
|
|
818
|
-
const characteristic = this.
|
|
798
|
+
const characteristic = this.services
|
|
799
|
+
.find((service) => service.id === serviceId)
|
|
800
|
+
?.characteristics.find((char) => char.id === characteristicId)?.characteristic
|
|
801
|
+
|
|
819
802
|
if (!characteristic) {
|
|
820
803
|
throw new Error(`Characteristic "${characteristicId}" not found in service "${serviceId}"`)
|
|
821
804
|
}
|
package/src/models/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export { Climbro } from "./device/climbro.model
|
|
1
|
+
export { Climbro } from "./device/climbro.model"
|
|
2
2
|
|
|
3
|
-
export { Entralpi } from "./device/entralpi.model
|
|
3
|
+
export { Entralpi } from "./device/entralpi.model"
|
|
4
4
|
|
|
5
|
-
export { ForceBoard } from "./device/forceboard.model
|
|
5
|
+
export { ForceBoard } from "./device/forceboard.model"
|
|
6
6
|
|
|
7
|
-
export { KilterBoard } from "./device/kilterboard.model
|
|
7
|
+
export { KilterBoard } from "./device/kilterboard.model"
|
|
8
8
|
|
|
9
|
-
export { Motherboard } from "./device/motherboard.model
|
|
9
|
+
export { Motherboard } from "./device/motherboard.model"
|
|
10
10
|
|
|
11
|
-
export { mySmartBoard } from "./device/mysmartboard.model
|
|
11
|
+
export { mySmartBoard } from "./device/mysmartboard.model"
|
|
12
12
|
|
|
13
|
-
export { Progressor } from "./device/progressor.model
|
|
13
|
+
export { Progressor } from "./device/progressor.model"
|
|
14
14
|
|
|
15
|
-
export { WHC06 } from "./device/wh-c06.model
|
|
15
|
+
export { WHC06 } from "./device/wh-c06.model"
|