@hangtime/grip-connect 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ Force-Sensing Hangboards / Plates used by climbers for strength measurement. Exa
13
13
 
14
14
  ## Roadmap
15
15
 
16
- - ✅ Griptonte Motherboard
16
+ - ✅ Griptonite Motherboard
17
17
  - ✅️ Connect with devices
18
18
  - ✅️ Read / Write / Notify using Bluetooth
19
19
  - ➡️ Calibrate Devices
@@ -80,15 +80,11 @@ motherboardButton.addEventListener("click", () => {
80
80
  await read(Motherboard, "device", "hardware", 1000)
81
81
  await read(Motherboard, "device", "firmware", 1000)
82
82
 
83
- // recalibrate
84
- await write(Motherboard, "uart", "tx", "", 0)
85
- await write(Motherboard, "uart", "tx", "", 0)
86
- await write(Motherboard, "uart", "tx", "", 1000)
87
-
88
- await write(Motherboard, "uart", "tx", "C3,0,0,0", 5000)
83
+ // read calibration (required before reading data)
84
+ await write(Motherboard, "uart", "tx", "C", 5000)
89
85
 
90
86
  // start stream
91
- await write(Motherboard, "uart", "tx", "S20", 15000)
87
+ await write(Motherboard, "uart", "tx", "S30", 15000)
92
88
 
93
89
  // end stream
94
90
  await write(Motherboard, "uart", "tx", "", 0)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
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": {
@@ -2,6 +2,7 @@ import { Device } from "./types";
2
2
  export declare const Motherboard: Device;
3
3
  /**
4
4
  * handleMotherboardData
5
- * @param line
5
+ * @param uuid - Unique identifier
6
+ * @param receivedString - Received data string
6
7
  */
7
8
  export declare function handleMotherboardData(uuid: string, receivedString: string): void;
@@ -87,35 +87,47 @@ export const Motherboard = {
87
87
  * @param calibration
88
88
  */
89
89
  const applyCalibration = (sample, calibration) => {
90
- const zeroCalib = calibration[0][2];
91
- let sgn = 1;
90
+ // Extract the calibrated value for the zero point
91
+ const zeroCalibration = calibration[0][2];
92
+ // Initialize sign as positive
93
+ let sign = 1;
94
+ // Initialize the final calibrated value
92
95
  let final = 0;
93
- if (sample < zeroCalib) {
94
- sgn = -1;
95
- sample = 2 * zeroCalib - sample;
96
+ // If the sample value is less than the zero calibration point
97
+ if (sample < zeroCalibration) {
98
+ // Change the sign to negative
99
+ sign = -1;
100
+ // Reflect the sample around the zero calibration point
101
+ sample = 2 * zeroCalibration - sample;
96
102
  }
103
+ // Iterate through the calibration data
97
104
  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) {
105
+ // Extract the lower and upper bounds of the current calibration range
106
+ const calibrationStart = calibration[i - 1][2];
107
+ const calibrationEnd = calibration[i][2];
108
+ // If the sample value is within the current calibration range
109
+ if (sample < calibrationEnd) {
110
+ // Interpolate to get the calibrated value within the range
101
111
  final =
102
112
  calibration[i - 1][1] +
103
- ((sample - calibStart) / (calibEnd - calibStart)) * (calibration[i][1] - calibration[i - 1][1]);
113
+ ((sample - calibrationStart) / (calibrationEnd - calibrationStart)) * (calibration[i][1] - calibration[i - 1][1]);
104
114
  break;
105
115
  }
106
116
  }
107
- return sgn * final;
117
+ // Return the calibrated value with the appropriate sign (positive/negative)
118
+ return sign * final;
108
119
  };
109
120
  /**
110
121
  * handleMotherboardData
111
- * @param line
122
+ * @param uuid - Unique identifier
123
+ * @param receivedString - Received data string
112
124
  */
113
125
  export function handleMotherboardData(uuid, receivedString) {
114
126
  const receivedTime = Date.now();
115
127
  // 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) {
128
+ const isAllHex = /^[0-9A-Fa-f]+$/g.test(receivedString);
129
+ // Handle streaming packet
130
+ if (isAllHex && receivedString.length === PACKET_LENGTH) {
119
131
  // Base-16 decode the string: convert hex pairs to byte values
120
132
  const bytes = Array.from({ length: receivedString.length / 2 }, (_, i) => Number(`0x${receivedString.substring(i * 2, i * 2 + 2)}`));
121
133
  // Translate header into packet, number of samples from the packet length
@@ -126,9 +138,15 @@ export function handleMotherboardData(uuid, receivedString) {
126
138
  samples: [],
127
139
  masses: [],
128
140
  };
141
+ const dataView = new DataView(new Uint8Array(bytes).buffer);
129
142
  for (let i = 0; i < NUM_SAMPLES; i++) {
130
143
  const sampleStart = 4 + 3 * i;
131
- packet.samples[i] = bytes[sampleStart] | (bytes[sampleStart + 1] << 8) | (bytes[sampleStart + 2] << 16);
144
+ // Use DataView to read the 24-bit unsigned integer
145
+ const rawValue = dataView.getUint8(sampleStart) |
146
+ (dataView.getUint8(sampleStart + 1) << 8) |
147
+ (dataView.getUint8(sampleStart + 2) << 16);
148
+ // Ensure unsigned 32-bit integer
149
+ packet.samples[i] = rawValue >>> 0;
132
150
  if (packet.samples[i] >= 0x7fffff) {
133
151
  packet.samples[i] -= 0x1000000;
134
152
  }
@@ -146,11 +164,12 @@ export function handleMotherboardData(uuid, receivedString) {
146
164
  massTotal: Math.max(-1000, left + right + center).toFixed(3),
147
165
  massLeft: Math.max(-1000, left).toFixed(3),
148
166
  massRight: Math.max(-1000, right).toFixed(3),
149
- massCentre: Math.max(-1000, center).toFixed(3),
167
+ massCenter: Math.max(-1000, center).toFixed(3),
150
168
  },
151
169
  });
152
170
  }
153
171
  else if ((receivedString.match(/,/g) || []).length === 3) {
172
+ console.log(receivedString);
154
173
  // if the returned notification is a calibration string add them to the array
155
174
  const parts = receivedString.split(",");
156
175
  const numericParts = parts.map((x) => parseFloat(x));
@@ -90,28 +90,41 @@ export const Motherboard: Device = {
90
90
  * @param calibration
91
91
  */
92
92
  const applyCalibration = (sample: number, calibration: number[][]): number => {
93
- const zeroCalib: number = calibration[0][2]
94
- let sgn: number = 1
95
- let final: number = 0
93
+ // Extract the calibrated value for the zero point
94
+ const zeroCalibration: number = calibration[0][2];
96
95
 
97
- if (sample < zeroCalib) {
98
- sgn = -1
99
- sample = 2 * zeroCalib - sample
96
+ // Initialize sign as positive
97
+ let sign: number = 1;
98
+
99
+ // Initialize the final calibrated value
100
+ let final: number = 0;
101
+
102
+ // If the sample value is less than the zero calibration point
103
+ if (sample < zeroCalibration) {
104
+ // Change the sign to negative
105
+ sign = -1;
106
+
107
+ // Reflect the sample around the zero calibration point
108
+ sample = 2 * zeroCalibration - sample;
100
109
  }
101
110
 
111
+ // Iterate through the calibration data
102
112
  for (let i = 1; i < calibration.length; i++) {
103
- const calibStart: number = calibration[i - 1][2]
104
- const calibEnd: number = calibration[i][2]
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];
105
116
 
106
- if (sample < calibEnd) {
117
+ // If the sample value is within the current calibration range
118
+ if (sample < calibrationEnd) {
119
+ // Interpolate to get the calibrated value within the range
107
120
  final =
108
121
  calibration[i - 1][1] +
109
- ((sample - calibStart) / (calibEnd - calibStart)) * (calibration[i][1] - calibration[i - 1][1])
110
- break
122
+ ((sample - calibrationStart) / (calibrationEnd - calibrationStart)) * (calibration[i][1] - calibration[i - 1][1]);
123
+ break;
111
124
  }
112
125
  }
113
-
114
- return sgn * final
126
+ // Return the calibrated value with the appropriate sign (positive/negative)
127
+ return sign * final;
115
128
  }
116
129
 
117
130
  interface Packet {
@@ -124,16 +137,17 @@ interface Packet {
124
137
 
125
138
  /**
126
139
  * handleMotherboardData
127
- * @param line
140
+ * @param uuid - Unique identifier
141
+ * @param receivedString - Received data string
128
142
  */
129
143
  export function handleMotherboardData(uuid: string, receivedString: string): void {
130
144
  const receivedTime: number = Date.now()
131
145
 
132
146
  // Check if the line is entirely hex characters
133
- const allHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedString)
147
+ const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedString);
134
148
 
135
- // Decide if this is a streaming packet
136
- if (allHex && receivedString.length === PACKET_LENGTH) {
149
+ // Handle streaming packet
150
+ if (isAllHex && receivedString.length === PACKET_LENGTH) {
137
151
  // Base-16 decode the string: convert hex pairs to byte values
138
152
  const bytes: number[] = Array.from({ length: receivedString.length / 2 }, (_, i) =>
139
153
  Number(`0x${receivedString.substring(i * 2, i * 2 + 2)}`),
@@ -148,17 +162,24 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
148
162
  masses: [],
149
163
  }
150
164
 
165
+ const dataView = new DataView(new Uint8Array(bytes).buffer);
166
+
151
167
  for (let i = 0; i < NUM_SAMPLES; i++) {
152
168
  const sampleStart: number = 4 + 3 * i
153
- packet.samples[i] = bytes[sampleStart] | (bytes[sampleStart + 1] << 8) | (bytes[sampleStart + 2] << 16)
169
+ // Use DataView to read the 24-bit unsigned integer
170
+ const rawValue = dataView.getUint8(sampleStart) |
171
+ (dataView.getUint8(sampleStart + 1) << 8) |
172
+ (dataView.getUint8(sampleStart + 2) << 16);
173
+
174
+ // Ensure unsigned 32-bit integer
175
+ packet.samples[i] = rawValue >>> 0;
154
176
 
155
177
  if (packet.samples[i] >= 0x7fffff) {
156
- packet.samples[i] -= 0x1000000
178
+ packet.samples[i] -= 0x1000000;
157
179
  }
158
180
 
159
181
  // TODO: make sure device is calibrated
160
182
  if (!CALIBRATION[0].length) return
161
-
162
183
  packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i])
163
184
  }
164
185
 
@@ -172,10 +193,11 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
172
193
  massTotal: Math.max(-1000, left + right + center).toFixed(3),
173
194
  massLeft: Math.max(-1000, left).toFixed(3),
174
195
  massRight: Math.max(-1000, right).toFixed(3),
175
- massCentre: Math.max(-1000, center).toFixed(3),
196
+ massCenter: Math.max(-1000, center).toFixed(3),
176
197
  },
177
198
  })
178
199
  } else if ((receivedString.match(/,/g) || []).length === 3) {
200
+ console.log(receivedString)
179
201
  // if the returned notification is a calibration string add them to the array
180
202
  const parts: string[] = receivedString.split(",")
181
203
  const numericParts: number[] = parts.map((x) => parseFloat(x))
@@ -185,3 +207,5 @@ export function handleMotherboardData(uuid: string, receivedString: string): voi
185
207
  console.log(receivedString)
186
208
  }
187
209
  }
210
+
211
+
package/src/write.js CHANGED
@@ -10,9 +10,8 @@ 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";
14
13
  characteristic
15
- .writeValue(encoder.encode(value))
14
+ .writeValue(encoder.encode(message))
16
15
  .then(() => {
17
16
  setTimeout(() => {
18
17
  resolve();
package/src/write.ts CHANGED
@@ -19,9 +19,8 @@ export const write = (
19
19
  const characteristic = getCharacteristic(board, serviceId, characteristicId)
20
20
 
21
21
  if (characteristic) {
22
- const value = message + "\n"
23
22
  characteristic
24
- .writeValue(encoder.encode(value))
23
+ .writeValue(encoder.encode(message))
25
24
  .then(() => {
26
25
  setTimeout(() => {
27
26
  resolve()
package/tsconfig.json CHANGED
@@ -3,7 +3,5 @@
3
3
  "include": ["src"],
4
4
  "compilerOptions": {
5
5
  "tsBuildInfoFile": "node_modules/.cache/.tsbuildinfo",
6
- "types": ["web-bluetooth"],
7
- "outDir": "./build",
8
6
  },
9
7
  }
@@ -1,9 +0,0 @@
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;
@@ -1,15 +0,0 @@
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
- };
@@ -1,7 +0,0 @@
1
- import { Device } from "./devices/types";
2
- /**
3
- * Connect to the BluetoothDevice
4
- * @param device
5
- * @param onSuccess
6
- */
7
- export declare const connect: (board: Device, onSuccess: () => void) => Promise<void>;
package/build/connect.js DELETED
@@ -1,150 +0,0 @@
1
- import { notifyCallback } from "./notify";
2
- import { handleMotherboardData } from "./devices/moterboard";
3
- let server;
4
- const receiveBuffer = [];
5
- /**
6
- * onDisconnected
7
- * @param board
8
- * @param event
9
- */
10
- const onDisconnected = (event, board) => {
11
- board.device = undefined;
12
- const device = event.target;
13
- console.log(`Device ${device.name} is disconnected.`);
14
- };
15
- /**
16
- * handleNotifications
17
- * @param event
18
- * @param onNotify
19
- */
20
- const handleNotifications = (event, board) => {
21
- const characteristic = event.target;
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
- }
38
- }
39
- }
40
- else if (board.name === "ENTRALPI") {
41
- // TODO: handle Entralpi notify
42
- // characteristic.value!.getInt16(0) / 100;
43
- if (notifyCallback) {
44
- notifyCallback({ uuid: characteristic.uuid, value: value });
45
- }
46
- }
47
- else if (board.name === "Tindeq") {
48
- // TODO: handle Tindeq notify
49
- }
50
- else {
51
- if (notifyCallback) {
52
- notifyCallback({ uuid: characteristic.uuid, value: value });
53
- }
54
- }
55
- }
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;
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);
99
- }
100
- };
101
- /**
102
- * Return all service UUIDs
103
- * @param device
104
- */
105
- function getAllServiceUUIDs(device) {
106
- return device.services.map((service) => service.uuid);
107
- }
108
- /**
109
- * Connect to the BluetoothDevice
110
- * @param device
111
- * @param onSuccess
112
- */
113
- export const connect = async (board, onSuccess) => {
114
- try {
115
- const deviceServices = getAllServiceUUIDs(board);
116
- // setup filter list
117
- const filters = [];
118
- if (board.name) {
119
- filters.push({
120
- name: board.name,
121
- });
122
- }
123
- if (board.companyId) {
124
- filters.push({
125
- manufacturerData: [
126
- {
127
- companyIdentifier: board.companyId,
128
- },
129
- ],
130
- });
131
- }
132
- const device = await navigator.bluetooth.requestDevice({
133
- filters: filters,
134
- optionalServices: deviceServices,
135
- });
136
- board.device = device;
137
- if (!board.device.gatt) {
138
- console.error("GATT is not available on this device");
139
- return;
140
- }
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);
145
- }
146
- }
147
- catch (error) {
148
- console.error(error);
149
- }
150
- };
@@ -1,2 +0,0 @@
1
- import { Device } from "./types";
2
- export declare const Entralpi: Device;
@@ -1,52 +0,0 @@
1
- export const Entralpi = {
2
- name: "ENTRALPI",
3
- services: [
4
- {
5
- name: "Device Information",
6
- id: "device",
7
- uuid: "0000180a-0000-1000-8000-00805f9b34fb",
8
- characteristics: [],
9
- },
10
- {
11
- name: "Battery Service",
12
- id: "battery",
13
- uuid: "0000180f-0000-1000-8000-00805f9b34fb",
14
- characteristics: [],
15
- },
16
- {
17
- name: "Generic Attribute",
18
- id: "attribute",
19
- uuid: "00001801-0000-1000-8000-00805f9b34fb",
20
- characteristics: [],
21
- },
22
- {
23
- name: "UART ISSC Transparent Service",
24
- id: "uart",
25
- uuid: "0000fff0-0000-1000-8000-00805f9b34fb",
26
- characteristics: [
27
- {
28
- name: "TX",
29
- id: "tx",
30
- uuid: "0000fff5-0000-1000-8000-00805f9b34fb",
31
- },
32
- {
33
- name: "RX",
34
- id: "rx",
35
- uuid: "0000fff4-0000-1000-8000-00805f9b34fb",
36
- },
37
- ],
38
- },
39
- {
40
- name: "Weight Scale",
41
- id: "weight",
42
- uuid: "0000181d-0000-1000-8000-00805f9b34fb",
43
- characteristics: [],
44
- },
45
- {
46
- name: "Generic Access",
47
- id: "access",
48
- uuid: "00001800-0000-1000-8000-00805f9b34fb",
49
- characteristics: [],
50
- },
51
- ],
52
- };
@@ -1,3 +0,0 @@
1
- export { Motherboard } from "./moterboard";
2
- export { Entralpi } from "./entralpi";
3
- export { Tindeq } from "./tindeq";
@@ -1,3 +0,0 @@
1
- export { Motherboard } from "./moterboard";
2
- export { Entralpi } from "./entralpi";
3
- export { Tindeq } from "./tindeq";
@@ -1,7 +0,0 @@
1
- import { Device } from "./types";
2
- export declare const Motherboard: Device;
3
- /**
4
- * handleMotherboardData
5
- * @param line
6
- */
7
- export declare function handleMotherboardData(uuid: string, receivedString: string): void;
@@ -1,163 +0,0 @@
1
- import { notifyCallback } from "../notify";
2
- const PACKET_LENGTH = 32;
3
- const NUM_SAMPLES = 3;
4
- const CALIBRATION = [[], [], [], []];
5
- export const Motherboard = {
6
- name: "Motherboard",
7
- companyId: 0x2a29,
8
- services: [
9
- {
10
- name: "Device Information",
11
- id: "device",
12
- uuid: "0000180a-0000-1000-8000-00805f9b34fb",
13
- characteristics: [
14
- // {
15
- // name: 'Serial Number (Blocked)',
16
- // id: 'serial'
17
- // uuid: '00002a25-0000-1000-8000-00805f9b34fb'
18
- // },
19
- {
20
- name: "Firmware Revision",
21
- id: "firmware",
22
- uuid: "00002a26-0000-1000-8000-00805f9b34fb",
23
- },
24
- {
25
- name: "Hardware Revision",
26
- id: "hardware",
27
- uuid: "00002a27-0000-1000-8000-00805f9b34fb",
28
- },
29
- {
30
- name: "Manufacturer Name",
31
- id: "manufacturer",
32
- uuid: "00002a29-0000-1000-8000-00805f9b34fb",
33
- },
34
- ],
35
- },
36
- {
37
- name: "Battery Service",
38
- id: "battery",
39
- uuid: "0000180f-0000-1000-8000-00805f9b34fb",
40
- characteristics: [
41
- {
42
- name: "Battery Level",
43
- id: "level",
44
- uuid: "00002a19-0000-1000-8000-00805f9b34fb",
45
- },
46
- ],
47
- },
48
- {
49
- name: "Unknown Service",
50
- id: "unknown",
51
- uuid: "10ababcd-15e1-28ff-de13-725bea03b127",
52
- characteristics: [
53
- {
54
- name: "Unknown 01",
55
- id: "01",
56
- uuid: "10ab1524-15e1-28ff-de13-725bea03b127",
57
- },
58
- {
59
- name: "Unknown 02",
60
- id: "02",
61
- uuid: "10ab1525-15e1-28ff-de13-725bea03b127",
62
- },
63
- ],
64
- },
65
- {
66
- name: "UART Nordic Service",
67
- id: "uart",
68
- uuid: "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
69
- characteristics: [
70
- {
71
- name: "TX",
72
- id: "tx",
73
- uuid: "6e400002-b5a3-f393-e0a9-e50e24dcca9e",
74
- },
75
- {
76
- name: "RX",
77
- id: "rx",
78
- uuid: "6e400003-b5a3-f393-e0a9-e50e24dcca9e",
79
- },
80
- ],
81
- },
82
- ],
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
- }
@@ -1,17 +0,0 @@
1
- import { Device } from "./types";
2
- export declare const Tindeq: Device;
3
- export declare const Commands: {
4
- TARE_SCALE: number;
5
- START_MEASURING: number;
6
- STOP_MEASURING: number;
7
- GET_APP_VERSION: number;
8
- GET_ERROR_INFO: number;
9
- CLEAR_ERR_INFO: number;
10
- GET_BATTERY_LEVEL: number;
11
- SLEEP: number;
12
- };
13
- export declare const NotificationTypes: {
14
- COMMAND_RESPONSE: number;
15
- WEIGHT_MEASURE: number;
16
- LOW_BATTERY_WARNING: number;
17
- };
@@ -1,37 +0,0 @@
1
- export const Tindeq = {
2
- name: "Tindeq",
3
- services: [
4
- {
5
- name: "Progressor Service",
6
- id: "progressor",
7
- uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57",
8
- characteristics: [
9
- {
10
- name: "Write",
11
- id: "tx",
12
- uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57",
13
- },
14
- {
15
- name: "Notify",
16
- id: "rx",
17
- uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
18
- },
19
- ],
20
- },
21
- ],
22
- };
23
- export const Commands = {
24
- TARE_SCALE: 0x64,
25
- START_MEASURING: 0x65,
26
- STOP_MEASURING: 0x66,
27
- GET_APP_VERSION: 0x6b,
28
- GET_ERROR_INFO: 0x6c,
29
- CLEAR_ERR_INFO: 0x6d,
30
- GET_BATTERY_LEVEL: 0x6f,
31
- SLEEP: 0x6e,
32
- };
33
- export const NotificationTypes = {
34
- COMMAND_RESPONSE: 0,
35
- WEIGHT_MEASURE: 1,
36
- LOW_BATTERY_WARNING: 2,
37
- };
@@ -1,20 +0,0 @@
1
- /// <reference types="web-bluetooth" />
2
- interface Characteristic {
3
- name: string;
4
- id: string;
5
- uuid: string;
6
- characteristic?: BluetoothRemoteGATTCharacteristic;
7
- }
8
- interface Service {
9
- name: string;
10
- id: string;
11
- uuid: string;
12
- characteristics: Characteristic[];
13
- }
14
- export interface Device {
15
- name: string;
16
- companyId?: number;
17
- services: Service[];
18
- device?: BluetoothDevice;
19
- }
20
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,6 +0,0 @@
1
- import { Device } from "./devices/types";
2
- /**
3
- * disconnect
4
- * @param board
5
- */
6
- export declare const disconnect: (board: Device) => void;
@@ -1,11 +0,0 @@
1
- /**
2
- * disconnect
3
- * @param board
4
- */
5
- export const disconnect = (board) => {
6
- if (!board.device)
7
- return;
8
- if (board.device.gatt?.connected) {
9
- board.device.gatt?.disconnect();
10
- }
11
- };
package/build/index.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export { Motherboard, Entralpi, Tindeq } from "./devices/index";
2
- export { connect } from "./connect";
3
- export { disconnect } from "./disconnect";
4
- export { notify } from "./notify";
5
- export { read } from "./read";
6
- export { write } from "./write";
package/build/index.js DELETED
@@ -1,6 +0,0 @@
1
- export { Motherboard, Entralpi, Tindeq } from "./devices/index";
2
- export { connect } from "./connect";
3
- export { disconnect } from "./disconnect";
4
- export { notify } from "./notify";
5
- export { read } from "./read";
6
- export { write } from "./write";
package/build/notify.d.ts DELETED
@@ -1,4 +0,0 @@
1
- type NotifyCallback = (data: object) => void;
2
- export declare let notifyCallback: NotifyCallback;
3
- export declare const notify: (callback: NotifyCallback) => void;
4
- export {};
package/build/notify.js DELETED
@@ -1,6 +0,0 @@
1
- // Initialize the callback variable
2
- export let notifyCallback;
3
- // Export a function to set the callback
4
- export const notify = (callback) => {
5
- notifyCallback = callback;
6
- };
package/build/read.d.ts DELETED
@@ -1,6 +0,0 @@
1
- import { Device } from "./devices/types";
2
- /**
3
- * read
4
- * @param characteristic
5
- */
6
- export declare const read: (board: Device, serviceId: string, characteristicId: string, duration?: number) => Promise<void>;
package/build/read.js DELETED
@@ -1,44 +0,0 @@
1
- import { notifyCallback } from "./notify";
2
- import { getCharacteristic } from "./characteristic";
3
- /**
4
- * read
5
- * @param characteristic
6
- */
7
- export const read = (board, serviceId, characteristicId, duration = 0) => {
8
- return new Promise((resolve, reject) => {
9
- if (board.device?.gatt?.connected) {
10
- const characteristic = getCharacteristic(board, serviceId, characteristicId);
11
- if (characteristic) {
12
- characteristic
13
- .readValue()
14
- .then((value) => {
15
- let decodedValue;
16
- const decoder = new TextDecoder("utf-8");
17
- switch (characteristicId) {
18
- case "level":
19
- decodedValue = value.getUint8(0);
20
- break;
21
- default:
22
- decodedValue = decoder.decode(value);
23
- break;
24
- }
25
- if (notifyCallback) {
26
- notifyCallback({ uuid: characteristic.uuid, value: decodedValue });
27
- }
28
- setTimeout(() => {
29
- resolve();
30
- }, duration);
31
- })
32
- .catch((error) => {
33
- reject(error);
34
- });
35
- }
36
- else {
37
- reject(new Error("Characteristic is undefined"));
38
- }
39
- }
40
- else {
41
- reject(new Error("Device is not connected"));
42
- }
43
- });
44
- };
package/build/write.d.ts DELETED
@@ -1,7 +0,0 @@
1
- import { Device } from "./devices/types";
2
- /**
3
- * write
4
- * @param characteristic
5
- * @param message
6
- */
7
- export declare const write: (board: Device, serviceId: string, characteristicId: string, message: string, duration?: number) => Promise<void>;
package/build/write.js DELETED
@@ -1,33 +0,0 @@
1
- import { getCharacteristic } from "./characteristic";
2
- /**
3
- * write
4
- * @param characteristic
5
- * @param message
6
- */
7
- export const write = (board, serviceId, characteristicId, message, duration = 0) => {
8
- return new Promise((resolve, reject) => {
9
- if (board.device?.gatt?.connected) {
10
- const encoder = new TextEncoder();
11
- const characteristic = getCharacteristic(board, serviceId, characteristicId);
12
- if (characteristic) {
13
- const value = message + "\n";
14
- characteristic
15
- .writeValue(encoder.encode(value))
16
- .then(() => {
17
- setTimeout(() => {
18
- resolve();
19
- }, duration);
20
- })
21
- .catch((error) => {
22
- reject(error);
23
- });
24
- }
25
- else {
26
- reject(new Error("Characteristics is undefined"));
27
- }
28
- }
29
- else {
30
- reject(new Error("Device is not connected"));
31
- }
32
- });
33
- };