@hangtime/grip-connect 0.0.9 → 0.0.10
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/package.json +1 -1
- package/src/connect.js +14 -18
- package/src/connect.ts +13 -17
- package/src/devices/entralpi.d.ts +6 -0
- package/src/devices/entralpi.js +14 -0
- package/src/devices/entralpi.ts +15 -0
- package/src/devices/moterboard.d.ts +2 -2
- package/src/devices/moterboard.js +11 -10
- package/src/devices/moterboard.ts +27 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
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": {
|
package/src/connect.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { notifyCallback } from "./notify";
|
|
2
2
|
import { handleMotherboardData } from "./devices/moterboard";
|
|
3
|
+
import { handleEntralpiData } from "./devices/entralpi";
|
|
3
4
|
let server;
|
|
4
5
|
const receiveBuffer = [];
|
|
5
6
|
/**
|
|
@@ -22,27 +23,22 @@ const handleNotifications = (event, board) => {
|
|
|
22
23
|
const value = characteristic.value;
|
|
23
24
|
if (value) {
|
|
24
25
|
if (board.name === "Motherboard") {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
handleMotherboardData(characteristic.uuid, receivedString);
|
|
37
|
-
}
|
|
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 receivedData = decoder.decode(new Uint8Array(line));
|
|
36
|
+
handleMotherboardData(characteristic.uuid, receivedData);
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
else if (board.name === "ENTRALPI") {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (notifyCallback) {
|
|
44
|
-
notifyCallback({ uuid: characteristic.uuid, value: value });
|
|
45
|
-
}
|
|
40
|
+
const receivedData = value.getInt16(0) / 100;
|
|
41
|
+
handleEntralpiData(characteristic.uuid, receivedData);
|
|
46
42
|
}
|
|
47
43
|
else if (board.name === "Tindeq") {
|
|
48
44
|
// TODO: handle Tindeq notify
|
package/src/connect.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Device } from "./devices/types"
|
|
2
2
|
import { notifyCallback } from "./notify"
|
|
3
3
|
import { handleMotherboardData } from "./devices/moterboard"
|
|
4
|
+
import { handleEntralpiData } from "./devices/entralpi"
|
|
4
5
|
|
|
5
6
|
let server: BluetoothRemoteGATTServer
|
|
6
7
|
const receiveBuffer: number[] = []
|
|
@@ -25,26 +26,21 @@ const handleNotifications = (event: Event, board: Device): void => {
|
|
|
25
26
|
const value = characteristic.value
|
|
26
27
|
if (value) {
|
|
27
28
|
if (board.name === "Motherboard") {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
29
|
+
for (let i = 0; i < value.byteLength; i++) {
|
|
30
|
+
receiveBuffer.push(value.getUint8(i))
|
|
31
|
+
}
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
33
|
+
let idx: number
|
|
34
|
+
while ((idx = receiveBuffer.indexOf(10)) >= 0) {
|
|
35
|
+
const line = receiveBuffer.splice(0, idx + 1).slice(0, -1) // Combine and remove LF
|
|
36
|
+
if (line.length > 0 && line[line.length - 1] === 13) line.pop() // Remove CR
|
|
37
|
+
const decoder = new TextDecoder("utf-8")
|
|
38
|
+
const receivedData = decoder.decode(new Uint8Array(line))
|
|
39
|
+
handleMotherboardData(characteristic.uuid, receivedData)
|
|
41
40
|
}
|
|
42
41
|
} else if (board.name === "ENTRALPI") {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (notifyCallback) {
|
|
46
|
-
notifyCallback({ uuid: characteristic.uuid, value: value })
|
|
47
|
-
}
|
|
42
|
+
const receivedData: number = value.getInt16(0) / 100
|
|
43
|
+
handleEntralpiData(characteristic.uuid, receivedData)
|
|
48
44
|
} else if (board.name === "Tindeq") {
|
|
49
45
|
// TODO: handle Tindeq notify
|
|
50
46
|
} else {
|
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import { Device } from "./types";
|
|
2
2
|
export declare const Entralpi: Device;
|
|
3
|
+
/**
|
|
4
|
+
* handleEntralpiData
|
|
5
|
+
* @param uuid - Unique identifier
|
|
6
|
+
* @param receivedData - Received data string
|
|
7
|
+
*/
|
|
8
|
+
export declare function handleEntralpiData(uuid: string, receivedData: number): void;
|
package/src/devices/entralpi.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { notifyCallback } from "../notify";
|
|
1
2
|
export const Entralpi = {
|
|
2
3
|
name: "ENTRALPI",
|
|
3
4
|
services: [
|
|
@@ -50,3 +51,16 @@ export const Entralpi = {
|
|
|
50
51
|
},
|
|
51
52
|
],
|
|
52
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* handleEntralpiData
|
|
56
|
+
* @param uuid - Unique identifier
|
|
57
|
+
* @param receivedData - Received data string
|
|
58
|
+
*/
|
|
59
|
+
export function handleEntralpiData(uuid, receivedData) {
|
|
60
|
+
notifyCallback({
|
|
61
|
+
uuid,
|
|
62
|
+
value: {
|
|
63
|
+
massTotal: receivedData,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
package/src/devices/entralpi.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Device } from "./types"
|
|
2
|
+
import { notifyCallback } from "../notify"
|
|
2
3
|
|
|
3
4
|
export const Entralpi: Device = {
|
|
4
5
|
name: "ENTRALPI",
|
|
@@ -52,3 +53,17 @@ export const Entralpi: Device = {
|
|
|
52
53
|
},
|
|
53
54
|
],
|
|
54
55
|
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* handleEntralpiData
|
|
59
|
+
* @param uuid - Unique identifier
|
|
60
|
+
* @param receivedData - Received data string
|
|
61
|
+
*/
|
|
62
|
+
export function handleEntralpiData(uuid: string, receivedData: number): void {
|
|
63
|
+
notifyCallback({
|
|
64
|
+
uuid,
|
|
65
|
+
value: {
|
|
66
|
+
massTotal: receivedData,
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
}
|
|
@@ -3,6 +3,6 @@ export declare const Motherboard: Device;
|
|
|
3
3
|
/**
|
|
4
4
|
* handleMotherboardData
|
|
5
5
|
* @param uuid - Unique identifier
|
|
6
|
-
* @param
|
|
6
|
+
* @param receivedData - Received data string
|
|
7
7
|
*/
|
|
8
|
-
export declare function handleMotherboardData(uuid: string,
|
|
8
|
+
export declare function handleMotherboardData(uuid: string, receivedData: string): void;
|
|
@@ -110,7 +110,8 @@ const applyCalibration = (sample, calibration) => {
|
|
|
110
110
|
// Interpolate to get the calibrated value within the range
|
|
111
111
|
final =
|
|
112
112
|
calibration[i - 1][1] +
|
|
113
|
-
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
|
|
113
|
+
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
|
|
114
|
+
(calibration[i][1] - calibration[i - 1][1]);
|
|
114
115
|
break;
|
|
115
116
|
}
|
|
116
117
|
}
|
|
@@ -120,16 +121,16 @@ const applyCalibration = (sample, calibration) => {
|
|
|
120
121
|
/**
|
|
121
122
|
* handleMotherboardData
|
|
122
123
|
* @param uuid - Unique identifier
|
|
123
|
-
* @param
|
|
124
|
+
* @param receivedData - Received data string
|
|
124
125
|
*/
|
|
125
|
-
export function handleMotherboardData(uuid,
|
|
126
|
+
export function handleMotherboardData(uuid, receivedData) {
|
|
126
127
|
const receivedTime = Date.now();
|
|
127
128
|
// Check if the line is entirely hex characters
|
|
128
|
-
const isAllHex = /^[0-9A-Fa-f]+$/g.test(
|
|
129
|
+
const isAllHex = /^[0-9A-Fa-f]+$/g.test(receivedData);
|
|
129
130
|
// Handle streaming packet
|
|
130
|
-
if (isAllHex &&
|
|
131
|
+
if (isAllHex && receivedData.length === PACKET_LENGTH) {
|
|
131
132
|
// Base-16 decode the string: convert hex pairs to byte values
|
|
132
|
-
const bytes = Array.from({ length:
|
|
133
|
+
const bytes = Array.from({ length: receivedData.length / 2 }, (_, i) => Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`));
|
|
133
134
|
// Translate header into packet, number of samples from the packet length
|
|
134
135
|
const packet = {
|
|
135
136
|
received: receivedTime,
|
|
@@ -168,15 +169,15 @@ export function handleMotherboardData(uuid, receivedString) {
|
|
|
168
169
|
},
|
|
169
170
|
});
|
|
170
171
|
}
|
|
171
|
-
else if ((
|
|
172
|
-
console.log(
|
|
172
|
+
else if ((receivedData.match(/,/g) || []).length === 3) {
|
|
173
|
+
console.log(receivedData);
|
|
173
174
|
// if the returned notification is a calibration string add them to the array
|
|
174
|
-
const parts =
|
|
175
|
+
const parts = receivedData.split(",");
|
|
175
176
|
const numericParts = parts.map((x) => parseFloat(x));
|
|
176
177
|
CALIBRATION[numericParts[0]].push(numericParts.slice(1));
|
|
177
178
|
}
|
|
178
179
|
else {
|
|
179
180
|
// unhanded data
|
|
180
|
-
console.log(
|
|
181
|
+
console.log(receivedData);
|
|
181
182
|
}
|
|
182
183
|
}
|
|
@@ -91,40 +91,41 @@ export const Motherboard: Device = {
|
|
|
91
91
|
*/
|
|
92
92
|
const applyCalibration = (sample: number, calibration: number[][]): number => {
|
|
93
93
|
// Extract the calibrated value for the zero point
|
|
94
|
-
const zeroCalibration: number = calibration[0][2]
|
|
94
|
+
const zeroCalibration: number = calibration[0][2]
|
|
95
95
|
|
|
96
96
|
// Initialize sign as positive
|
|
97
|
-
let sign: number = 1
|
|
97
|
+
let sign: number = 1
|
|
98
98
|
|
|
99
99
|
// Initialize the final calibrated value
|
|
100
|
-
let final: number = 0
|
|
100
|
+
let final: number = 0
|
|
101
101
|
|
|
102
102
|
// If the sample value is less than the zero calibration point
|
|
103
103
|
if (sample < zeroCalibration) {
|
|
104
104
|
// Change the sign to negative
|
|
105
|
-
sign = -1
|
|
105
|
+
sign = -1
|
|
106
106
|
|
|
107
107
|
// Reflect the sample around the zero calibration point
|
|
108
|
-
sample = 2 * zeroCalibration - sample
|
|
108
|
+
sample = 2 * zeroCalibration - sample
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// Iterate through the calibration data
|
|
112
112
|
for (let i = 1; i < calibration.length; i++) {
|
|
113
113
|
// Extract the lower and upper bounds of the current calibration range
|
|
114
|
-
const calibrationStart: number = calibration[i - 1][2]
|
|
115
|
-
const calibrationEnd: number = calibration[i][2]
|
|
114
|
+
const calibrationStart: number = calibration[i - 1][2]
|
|
115
|
+
const calibrationEnd: number = calibration[i][2]
|
|
116
116
|
|
|
117
117
|
// If the sample value is within the current calibration range
|
|
118
118
|
if (sample < calibrationEnd) {
|
|
119
119
|
// Interpolate to get the calibrated value within the range
|
|
120
120
|
final =
|
|
121
121
|
calibration[i - 1][1] +
|
|
122
|
-
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
|
|
123
|
-
|
|
122
|
+
((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
|
|
123
|
+
(calibration[i][1] - calibration[i - 1][1])
|
|
124
|
+
break
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
// Return the calibrated value with the appropriate sign (positive/negative)
|
|
127
|
-
return sign * final
|
|
128
|
+
return sign * final
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
interface Packet {
|
|
@@ -138,19 +139,19 @@ interface Packet {
|
|
|
138
139
|
/**
|
|
139
140
|
* handleMotherboardData
|
|
140
141
|
* @param uuid - Unique identifier
|
|
141
|
-
* @param
|
|
142
|
+
* @param receivedData - Received data string
|
|
142
143
|
*/
|
|
143
|
-
export function handleMotherboardData(uuid: string,
|
|
144
|
+
export function handleMotherboardData(uuid: string, receivedData: string): void {
|
|
144
145
|
const receivedTime: number = Date.now()
|
|
145
146
|
|
|
146
147
|
// Check if the line is entirely hex characters
|
|
147
|
-
const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(
|
|
148
|
+
const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedData)
|
|
148
149
|
|
|
149
150
|
// Handle streaming packet
|
|
150
|
-
if (isAllHex &&
|
|
151
|
+
if (isAllHex && receivedData.length === PACKET_LENGTH) {
|
|
151
152
|
// Base-16 decode the string: convert hex pairs to byte values
|
|
152
|
-
const bytes: number[] = Array.from({ length:
|
|
153
|
-
Number(`0x${
|
|
153
|
+
const bytes: number[] = Array.from({ length: receivedData.length / 2 }, (_, i) =>
|
|
154
|
+
Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`),
|
|
154
155
|
)
|
|
155
156
|
|
|
156
157
|
// Translate header into packet, number of samples from the packet length
|
|
@@ -162,20 +163,21 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
|
|
|
162
163
|
masses: [],
|
|
163
164
|
}
|
|
164
165
|
|
|
165
|
-
const dataView = new DataView(new Uint8Array(bytes).buffer)
|
|
166
|
+
const dataView = new DataView(new Uint8Array(bytes).buffer)
|
|
166
167
|
|
|
167
168
|
for (let i = 0; i < NUM_SAMPLES; i++) {
|
|
168
169
|
const sampleStart: number = 4 + 3 * i
|
|
169
170
|
// Use DataView to read the 24-bit unsigned integer
|
|
170
|
-
const rawValue =
|
|
171
|
+
const rawValue =
|
|
172
|
+
dataView.getUint8(sampleStart) |
|
|
171
173
|
(dataView.getUint8(sampleStart + 1) << 8) |
|
|
172
|
-
(dataView.getUint8(sampleStart + 2) << 16)
|
|
174
|
+
(dataView.getUint8(sampleStart + 2) << 16)
|
|
173
175
|
|
|
174
176
|
// Ensure unsigned 32-bit integer
|
|
175
|
-
packet.samples[i] = rawValue >>> 0
|
|
177
|
+
packet.samples[i] = rawValue >>> 0
|
|
176
178
|
|
|
177
179
|
if (packet.samples[i] >= 0x7fffff) {
|
|
178
|
-
packet.samples[i] -= 0x1000000
|
|
180
|
+
packet.samples[i] -= 0x1000000
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
// TODO: make sure device is calibrated
|
|
@@ -196,16 +198,14 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
|
|
|
196
198
|
massCenter: Math.max(-1000, center).toFixed(3),
|
|
197
199
|
},
|
|
198
200
|
})
|
|
199
|
-
} else if ((
|
|
200
|
-
console.log(
|
|
201
|
+
} else if ((receivedData.match(/,/g) || []).length === 3) {
|
|
202
|
+
console.log(receivedData)
|
|
201
203
|
// if the returned notification is a calibration string add them to the array
|
|
202
|
-
const parts: string[] =
|
|
204
|
+
const parts: string[] = receivedData.split(",")
|
|
203
205
|
const numericParts: number[] = parts.map((x) => parseFloat(x))
|
|
204
206
|
;(CALIBRATION[numericParts[0]] as number[][]).push(numericParts.slice(1))
|
|
205
207
|
} else {
|
|
206
208
|
// unhanded data
|
|
207
|
-
console.log(
|
|
209
|
+
console.log(receivedData)
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
|
-
|
|
211
|
-
|