@hangtime/grip-connect 0.0.6 → 0.0.8
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 +56 -26
- package/build/connect.d.ts +1 -1
- package/build/connect.js +79 -78
- package/build/devices/moterboard.d.ts +5 -0
- package/build/devices/moterboard.js +84 -0
- package/build/read.d.ts +1 -1
- package/build/read.js +7 -3
- package/build/write.js +2 -1
- package/package.json +2 -2
- package/src/characteristic.d.ts +9 -0
- package/src/characteristic.js +15 -0
- package/src/connect.d.ts +1 -1
- package/src/connect.js +79 -78
- package/src/connect.ts +86 -83
- package/src/devices/entralpi.d.ts +2 -0
- package/src/devices/entralpi.js +52 -0
- package/src/devices/index.d.ts +3 -0
- package/src/devices/index.js +3 -0
- package/src/devices/moterboard.d.ts +7 -0
- package/src/devices/moterboard.js +163 -0
- package/src/devices/moterboard.ts +106 -0
- package/src/devices/tindeq.d.ts +17 -0
- package/src/devices/tindeq.js +37 -0
- package/src/devices/types.d.ts +20 -0
- package/src/devices/types.js +1 -0
- package/src/disconnect.d.ts +6 -0
- package/src/disconnect.js +11 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +6 -0
- package/src/notify.d.ts +4 -0
- package/src/notify.js +6 -0
- package/src/read.d.ts +6 -0
- package/src/read.js +44 -0
- package/src/read.ts +12 -3
- package/src/write.d.ts +7 -0
- package/src/write.js +33 -0
- package/src/write.ts +2 -1
- package/tsconfig.json +2 -2
package/README.md
CHANGED
|
@@ -2,23 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
**Force-Sensing Climbing Training**
|
|
4
4
|
|
|
5
|
-
The objective of this project is to create a client that can establish connections with various
|
|
6
|
-
/ Plates used by climbers for strength measurement. Examples of such hangboards include the
|
|
5
|
+
The objective of this project is to create a Web Bluetooth API client that can establish connections with various
|
|
6
|
+
Force-Sensing Hangboards / Plates used by climbers for strength measurement. Examples of such hangboards include the
|
|
7
7
|
[Motherboard](https://griptonite.io/shop/motherboard/), [Climbro](https://climbro.com/),
|
|
8
8
|
[SmartBoard](https://www.smartboard-climbing.com/), [Entralpi](https://entralpi.com/) or
|
|
9
9
|
[Tindeq Progressor](https://tindeq.com/)
|
|
10
10
|
|
|
11
|
+
[Try it out](https://grip-connect.vercel.app/) - [Docs](https://stevie-ray.github.io/hangtime-grip-connect/) -
|
|
12
|
+
[Browser Support](https://caniuse.com/web-bluetooth)
|
|
13
|
+
|
|
11
14
|
## Roadmap
|
|
12
15
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- ➡️
|
|
16
|
+
- ✅ Griptonte Motherboard
|
|
17
|
+
- ✅️ Connect with devices
|
|
18
|
+
- ✅️ Read / Write / Notify using Bluetooth
|
|
19
|
+
- ➡️ Calibrate Devices
|
|
20
|
+
- ✅️ Output weight/force stream
|
|
21
|
+
- ✅ Tindeq Progressor
|
|
22
|
+
- ✅️ Connect with devices
|
|
23
|
+
- ✅️ Read / Write / Notify using Bluetooth
|
|
24
|
+
- ➡️ Calibrate Devices
|
|
25
|
+
- ➡️ Output weight/force stream
|
|
26
|
+
- ✅ Entralpi
|
|
27
|
+
- ✅️ Connect with devices
|
|
28
|
+
- ✅️ Read / Write / Notify using Bluetooth
|
|
29
|
+
- ➡️ Calibrate Devices
|
|
30
|
+
- ➡️ Output weight/force stream
|
|
31
|
+
- ➡️ Climbro
|
|
32
|
+
- ➡️ Connect with devices
|
|
33
|
+
- ➡️ Read / Write / Notify using Bluetooth
|
|
34
|
+
- ➡️ Calibrate Devices
|
|
35
|
+
- ➡️ Output weight/force stream
|
|
36
|
+
- ➡️ SmartBoard
|
|
37
|
+
- ➡️ Connect with devices
|
|
38
|
+
- ➡️ Read / Write / Notify using Bluetooth
|
|
39
|
+
- ➡️ Calibrate Devices
|
|
40
|
+
- ➡️ Output weight/force stream
|
|
22
41
|
|
|
23
42
|
## Development
|
|
24
43
|
|
|
@@ -56,30 +75,41 @@ motherboardButton.addEventListener("click", () => {
|
|
|
56
75
|
})
|
|
57
76
|
|
|
58
77
|
// read battery + device info
|
|
59
|
-
await read(Motherboard, "battery", "level")
|
|
60
|
-
await read(Motherboard, "device", "manufacturer")
|
|
61
|
-
await read(Motherboard, "device", "hardware")
|
|
62
|
-
await read(Motherboard, "device", "firmware")
|
|
78
|
+
await read(Motherboard, "battery", "level", 1000)
|
|
79
|
+
await read(Motherboard, "device", "manufacturer", 1000)
|
|
80
|
+
await read(Motherboard, "device", "hardware", 1000)
|
|
81
|
+
await read(Motherboard, "device", "firmware", 1000)
|
|
63
82
|
|
|
64
|
-
//
|
|
65
|
-
await write(Motherboard, "uart", "tx", "
|
|
83
|
+
// recalibrate
|
|
84
|
+
await write(Motherboard, "uart", "tx", "", 0)
|
|
85
|
+
await write(Motherboard, "uart", "tx", "", 0)
|
|
86
|
+
await write(Motherboard, "uart", "tx", "", 1000)
|
|
66
87
|
|
|
67
|
-
|
|
68
|
-
await write(Motherboard, "unknown", "01", "1", 2500)
|
|
69
|
-
await write(Motherboard, "unknown", "02", "0", 2500)
|
|
70
|
-
await write(Motherboard, "uart", "tx", "S30", 5000)
|
|
88
|
+
await write(Motherboard, "uart", "tx", "C3,0,0,0", 5000)
|
|
71
89
|
|
|
72
|
-
//
|
|
73
|
-
await write(Motherboard, "
|
|
74
|
-
await write(Motherboard, "unknown", "02", "1", 2500)
|
|
75
|
-
await write(Motherboard, "uart", "tx", "S30", 5000)
|
|
90
|
+
// start stream
|
|
91
|
+
await write(Motherboard, "uart", "tx", "S20", 15000)
|
|
76
92
|
|
|
93
|
+
// end stream
|
|
94
|
+
await write(Motherboard, "uart", "tx", "", 0)
|
|
77
95
|
// disconnect from device after we are done
|
|
78
96
|
disconnect(Motherboard)
|
|
79
97
|
})
|
|
80
98
|
})
|
|
81
99
|
```
|
|
82
100
|
|
|
101
|
+
## Credits
|
|
102
|
+
|
|
103
|
+
A special thank you to:
|
|
104
|
+
|
|
105
|
+
- [@CassimLadha](https://github.com/CassimLadha) for sharing insights on reading the Motherboards data.
|
|
106
|
+
- [@donaldharvey](https://github.com/donaldharvey) for a valuable example on connecting to the motherboard.
|
|
107
|
+
|
|
108
|
+
## Disclamer
|
|
109
|
+
|
|
110
|
+
THIS SOFTWARE IS NOT OFFICIALY SUPPORTED, SUPPLIED OR MAINTAINED BY THE DEVICE MANUFACTURER. BY USING THE SOFTWARE YOU
|
|
111
|
+
ARE ACKNOWLEDGEING THIS AND UNDERSTAND THAT USING THIS SOFTWARE WILL INVALIDATE THE MANUFACTURERS WARRANTY.
|
|
112
|
+
|
|
83
113
|
## License
|
|
84
114
|
|
|
85
|
-
|
|
115
|
+
BSD 2-Clause © [Stevie-Ray Hartog](https://github.com/Stevie-Ray)
|
package/build/connect.d.ts
CHANGED
package/build/connect.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { notifyCallback } from "./notify";
|
|
2
|
+
import { handleMotherboardData } from "./devices/moterboard";
|
|
3
|
+
let server;
|
|
4
|
+
const receiveBuffer = [];
|
|
2
5
|
/**
|
|
3
6
|
* onDisconnected
|
|
4
7
|
* @param board
|
|
@@ -16,63 +19,83 @@ const onDisconnected = (event, board) => {
|
|
|
16
19
|
*/
|
|
17
20
|
const handleNotifications = (event, board) => {
|
|
18
21
|
const characteristic = event.target;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const elementKeys = [
|
|
36
|
-
"frames",
|
|
37
|
-
"cycle",
|
|
38
|
-
"unknown",
|
|
39
|
-
"eleven",
|
|
40
|
-
"dynamic1",
|
|
41
|
-
"pressure1",
|
|
42
|
-
"left",
|
|
43
|
-
"dynamic2",
|
|
44
|
-
"pressure2",
|
|
45
|
-
"right",
|
|
46
|
-
];
|
|
47
|
-
const dataObject = {};
|
|
48
|
-
if (parsedDecimalArray) {
|
|
49
|
-
elementKeys.forEach((key, index) => {
|
|
50
|
-
dataObject[key] = parsedDecimalArray[index];
|
|
51
|
-
});
|
|
22
|
+
const value = characteristic.value;
|
|
23
|
+
if (value) {
|
|
24
|
+
if (board.name === "Motherboard") {
|
|
25
|
+
if (value) {
|
|
26
|
+
for (let i = 0; i < value.byteLength; i++) {
|
|
27
|
+
receiveBuffer.push(value.getUint8(i));
|
|
28
|
+
}
|
|
29
|
+
let idx;
|
|
30
|
+
while ((idx = receiveBuffer.indexOf(10)) >= 0) {
|
|
31
|
+
const line = receiveBuffer.splice(0, idx + 1).slice(0, -1); // Combine and remove LF
|
|
32
|
+
if (line.length > 0 && line[line.length - 1] === 13)
|
|
33
|
+
line.pop(); // Remove CR
|
|
34
|
+
const decoder = new TextDecoder("utf-8");
|
|
35
|
+
const receivedString = decoder.decode(new Uint8Array(line));
|
|
36
|
+
handleMotherboardData(characteristic.uuid, receivedString);
|
|
37
|
+
}
|
|
52
38
|
}
|
|
39
|
+
}
|
|
40
|
+
else if (board.name === "ENTRALPI") {
|
|
41
|
+
// TODO: handle Entralpi notify
|
|
42
|
+
// characteristic.value!.getInt16(0) / 100;
|
|
53
43
|
if (notifyCallback) {
|
|
54
|
-
notifyCallback({ uuid: characteristic.uuid, value:
|
|
44
|
+
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
55
45
|
}
|
|
56
46
|
}
|
|
57
|
-
else if (
|
|
58
|
-
// TODO: handle
|
|
59
|
-
// notifyCallback({ uuid: characteristic.uuid, value: characteristic.value!.getInt8(0) / 100 })
|
|
47
|
+
else if (board.name === "Tindeq") {
|
|
48
|
+
// TODO: handle Tindeq notify
|
|
60
49
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (notifyCallback) {
|
|
66
|
-
notifyCallback({ uuid: characteristic.uuid, value: receivedString });
|
|
50
|
+
else {
|
|
51
|
+
if (notifyCallback) {
|
|
52
|
+
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
53
|
+
}
|
|
67
54
|
}
|
|
68
55
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* onConnected
|
|
59
|
+
* @param event
|
|
60
|
+
* @param board
|
|
61
|
+
*/
|
|
62
|
+
const onConnected = async (board, onSuccess) => {
|
|
63
|
+
try {
|
|
64
|
+
const services = await server?.getPrimaryServices();
|
|
65
|
+
if (!services || services.length === 0) {
|
|
66
|
+
console.error("No services found");
|
|
67
|
+
return;
|
|
75
68
|
}
|
|
69
|
+
for (const service of services) {
|
|
70
|
+
const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid);
|
|
71
|
+
if (matchingService) {
|
|
72
|
+
// Android bug: Introduce a delay before getting characteristics
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
74
|
+
const characteristics = await service.getCharacteristics();
|
|
75
|
+
for (const characteristic of matchingService.characteristics) {
|
|
76
|
+
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid);
|
|
77
|
+
if (matchingCharacteristic) {
|
|
78
|
+
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid);
|
|
79
|
+
if (element) {
|
|
80
|
+
element.characteristic = matchingCharacteristic;
|
|
81
|
+
// notify
|
|
82
|
+
if (element.id === "rx") {
|
|
83
|
+
matchingCharacteristic.startNotifications();
|
|
84
|
+
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => handleNotifications(event, board));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Call the onSuccess callback after successful connection and setup
|
|
95
|
+
onSuccess();
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
console.error(error);
|
|
76
99
|
}
|
|
77
100
|
};
|
|
78
101
|
/**
|
|
@@ -83,7 +106,7 @@ function getAllServiceUUIDs(device) {
|
|
|
83
106
|
return device.services.map((service) => service.uuid);
|
|
84
107
|
}
|
|
85
108
|
/**
|
|
86
|
-
*
|
|
109
|
+
* Connect to the BluetoothDevice
|
|
87
110
|
* @param device
|
|
88
111
|
* @param onSuccess
|
|
89
112
|
*/
|
|
@@ -111,37 +134,15 @@ export const connect = async (board, onSuccess) => {
|
|
|
111
134
|
optionalServices: deviceServices,
|
|
112
135
|
});
|
|
113
136
|
board.device = device;
|
|
114
|
-
device.
|
|
115
|
-
|
|
116
|
-
const services = await server?.getPrimaryServices();
|
|
117
|
-
if (!services || services.length === 0) {
|
|
118
|
-
console.error("No services found");
|
|
137
|
+
if (!board.device.gatt) {
|
|
138
|
+
console.error("GATT is not available on this device");
|
|
119
139
|
return;
|
|
120
140
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for (const characteristic of matchingService.characteristics) {
|
|
126
|
-
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid);
|
|
127
|
-
if (matchingCharacteristic) {
|
|
128
|
-
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid);
|
|
129
|
-
if (element) {
|
|
130
|
-
element.characteristic = matchingCharacteristic;
|
|
131
|
-
// notify
|
|
132
|
-
if (element.id === "rx") {
|
|
133
|
-
matchingCharacteristic.startNotifications();
|
|
134
|
-
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => handleNotifications(event, board));
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
141
|
+
server = await board.device?.gatt?.connect();
|
|
142
|
+
board.device.addEventListener("gattserverdisconnected", (event) => onDisconnected(event, board));
|
|
143
|
+
if (server.connected) {
|
|
144
|
+
await onConnected(board, onSuccess);
|
|
143
145
|
}
|
|
144
|
-
onSuccess();
|
|
145
146
|
}
|
|
146
147
|
catch (error) {
|
|
147
148
|
console.error(error);
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { notifyCallback } from "../notify";
|
|
2
|
+
const PACKET_LENGTH = 32;
|
|
3
|
+
const NUM_SAMPLES = 3;
|
|
4
|
+
const CALIBRATION = [[], [], [], []];
|
|
1
5
|
export const Motherboard = {
|
|
2
6
|
name: "Motherboard",
|
|
3
7
|
companyId: 0x2a29,
|
|
@@ -77,3 +81,83 @@ export const Motherboard = {
|
|
|
77
81
|
},
|
|
78
82
|
],
|
|
79
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* applyCalibration
|
|
86
|
+
* @param sample
|
|
87
|
+
* @param calibration
|
|
88
|
+
*/
|
|
89
|
+
const applyCalibration = (sample, calibration) => {
|
|
90
|
+
const zeroCalib = calibration[0][2];
|
|
91
|
+
let sgn = 1;
|
|
92
|
+
let final = 0;
|
|
93
|
+
if (sample < zeroCalib) {
|
|
94
|
+
sgn = -1;
|
|
95
|
+
sample = 2 * zeroCalib - sample;
|
|
96
|
+
}
|
|
97
|
+
for (let i = 1; i < calibration.length; i++) {
|
|
98
|
+
const calibStart = calibration[i - 1][2];
|
|
99
|
+
const calibEnd = calibration[i][2];
|
|
100
|
+
if (sample < calibEnd) {
|
|
101
|
+
final =
|
|
102
|
+
calibration[i - 1][1] +
|
|
103
|
+
((sample - calibStart) / (calibEnd - calibStart)) * (calibration[i][1] - calibration[i - 1][1]);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return sgn * final;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* handleMotherboardData
|
|
111
|
+
* @param line
|
|
112
|
+
*/
|
|
113
|
+
export function handleMotherboardData(uuid, receivedString) {
|
|
114
|
+
const receivedTime = Date.now();
|
|
115
|
+
// Check if the line is entirely hex characters
|
|
116
|
+
const allHex = /^[0-9A-Fa-f]+$/g.test(receivedString);
|
|
117
|
+
// Decide if this is a streaming packet
|
|
118
|
+
if (allHex && receivedString.length === PACKET_LENGTH) {
|
|
119
|
+
// Base-16 decode the string: convert hex pairs to byte values
|
|
120
|
+
const bytes = Array.from({ length: receivedString.length / 2 }, (_, i) => Number(`0x${receivedString.substring(i * 2, i * 2 + 2)}`));
|
|
121
|
+
// Translate header into packet, number of samples from the packet length
|
|
122
|
+
const packet = {
|
|
123
|
+
received: receivedTime,
|
|
124
|
+
sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
|
|
125
|
+
battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
|
|
126
|
+
samples: [],
|
|
127
|
+
masses: [],
|
|
128
|
+
};
|
|
129
|
+
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
130
|
+
const sampleStart = 4 + 3 * i;
|
|
131
|
+
packet.samples[i] = bytes[sampleStart] | (bytes[sampleStart + 1] << 8) | (bytes[sampleStart + 2] << 16);
|
|
132
|
+
if (packet.samples[i] >= 0x7fffff) {
|
|
133
|
+
packet.samples[i] -= 0x1000000;
|
|
134
|
+
}
|
|
135
|
+
// TODO: make sure device is calibrated
|
|
136
|
+
if (!CALIBRATION[0].length)
|
|
137
|
+
return;
|
|
138
|
+
packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i]);
|
|
139
|
+
}
|
|
140
|
+
const left = packet.masses[0];
|
|
141
|
+
const center = packet.masses[1];
|
|
142
|
+
const right = packet.masses[2];
|
|
143
|
+
notifyCallback({
|
|
144
|
+
uuid,
|
|
145
|
+
value: {
|
|
146
|
+
massTotal: Math.max(-1000, left + right + center).toFixed(3),
|
|
147
|
+
massLeft: Math.max(-1000, left).toFixed(3),
|
|
148
|
+
massRight: Math.max(-1000, right).toFixed(3),
|
|
149
|
+
massCentre: Math.max(-1000, center).toFixed(3),
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else if ((receivedString.match(/,/g) || []).length === 3) {
|
|
154
|
+
// if the returned notification is a calibration string add them to the array
|
|
155
|
+
const parts = receivedString.split(",");
|
|
156
|
+
const numericParts = parts.map((x) => parseFloat(x));
|
|
157
|
+
CALIBRATION[numericParts[0]].push(numericParts.slice(1));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// unhanded data
|
|
161
|
+
console.log(receivedString);
|
|
162
|
+
}
|
|
163
|
+
}
|
package/build/read.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ import { Device } from "./devices/types";
|
|
|
3
3
|
* read
|
|
4
4
|
* @param characteristic
|
|
5
5
|
*/
|
|
6
|
-
export declare const read: (board: Device, serviceId: string, characteristicId: string) => Promise<void>;
|
|
6
|
+
export declare const read: (board: Device, serviceId: string, characteristicId: string, duration?: number) => Promise<void>;
|
package/build/read.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getCharacteristic } from "./characteristic";
|
|
|
4
4
|
* read
|
|
5
5
|
* @param characteristic
|
|
6
6
|
*/
|
|
7
|
-
export const read = (board, serviceId, characteristicId) => {
|
|
7
|
+
export const read = (board, serviceId, characteristicId, duration = 0) => {
|
|
8
8
|
return new Promise((resolve, reject) => {
|
|
9
9
|
if (board.device?.gatt?.connected) {
|
|
10
10
|
const characteristic = getCharacteristic(board, serviceId, characteristicId);
|
|
@@ -22,8 +22,12 @@ export const read = (board, serviceId, characteristicId) => {
|
|
|
22
22
|
decodedValue = decoder.decode(value);
|
|
23
23
|
break;
|
|
24
24
|
}
|
|
25
|
-
notifyCallback
|
|
26
|
-
|
|
25
|
+
if (notifyCallback) {
|
|
26
|
+
notifyCallback({ uuid: characteristic.uuid, value: decodedValue });
|
|
27
|
+
}
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
resolve();
|
|
30
|
+
}, duration);
|
|
27
31
|
})
|
|
28
32
|
.catch((error) => {
|
|
29
33
|
reject(error);
|
package/build/write.js
CHANGED
|
@@ -10,8 +10,9 @@ export const write = (board, serviceId, characteristicId, message, duration = 0)
|
|
|
10
10
|
const encoder = new TextEncoder();
|
|
11
11
|
const characteristic = getCharacteristic(board, serviceId, characteristicId);
|
|
12
12
|
if (characteristic) {
|
|
13
|
+
const value = message + "\n";
|
|
13
14
|
characteristic
|
|
14
|
-
.writeValue(encoder.encode(
|
|
15
|
+
.writeValue(encoder.encode(value))
|
|
15
16
|
.then(() => {
|
|
16
17
|
setTimeout(() => {
|
|
17
18
|
resolve();
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
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 Motherboard, Climbro, SmartBoard, Entralpi or Tindeq Progressor",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsc --build"
|
|
8
8
|
},
|
|
9
9
|
"author": "Stevie-Ray Hartog <mail@stevie-ray.nl>",
|
|
10
|
-
"license": "
|
|
10
|
+
"license": "BSD-2-Clause",
|
|
11
11
|
"devDependencies": {},
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="web-bluetooth" />
|
|
2
|
+
import { Device } from "./devices/types";
|
|
3
|
+
/**
|
|
4
|
+
* getCharacteristic
|
|
5
|
+
* @param board
|
|
6
|
+
* @param serviceId
|
|
7
|
+
* @param characteristicId
|
|
8
|
+
*/
|
|
9
|
+
export declare const getCharacteristic: (board: Device, serviceId: string, characteristicId: string) => BluetoothRemoteGATTCharacteristic | undefined;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getCharacteristic
|
|
3
|
+
* @param board
|
|
4
|
+
* @param serviceId
|
|
5
|
+
* @param characteristicId
|
|
6
|
+
*/
|
|
7
|
+
export const getCharacteristic = (board, serviceId, characteristicId) => {
|
|
8
|
+
const boardService = board.services.find((service) => service.id === serviceId);
|
|
9
|
+
if (boardService) {
|
|
10
|
+
const boardCharacteristic = boardService.characteristics.find((characteristic) => characteristic.id === characteristicId);
|
|
11
|
+
if (boardCharacteristic) {
|
|
12
|
+
return boardCharacteristic.characteristic;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
};
|