@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/src/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 receivedData = new Uint8Array(characteristic.value.buffer);
20
- // Create an array to store the parsed decimal values
21
- const decimalArray = [];
22
- // Iterate through each byte and convert to decimal
23
- for (let i = 0; i < receivedData.length; i++) {
24
- decimalArray.push(receivedData[i]);
25
- }
26
- // Convert the decimal array to a string representation
27
- const receivedString = String.fromCharCode(...decimalArray);
28
- if (board.name === "Motherboard") {
29
- // Split the string into pairs of characters
30
- const hexPairs = receivedString.match(/.{1,2}/g);
31
- // Convert each hexadecimal pair to decimal
32
- const parsedDecimalArray = hexPairs?.map((hexPair) => parseInt(hexPair, 16));
33
- // Handle different types of data
34
- if (characteristic.value.byteLength === 20) {
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: dataObject });
44
+ notifyCallback({ uuid: characteristic.uuid, value: value });
55
45
  }
56
46
  }
57
- else if (characteristic.value.byteLength === 14) {
58
- // TODO: handle 14 byte data
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
- else if (board.name === "ENTRALPI") {
63
- // TODO: handle Entralpi notify
64
- // characteristic.value!.getInt16(0) / 100;
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
- else if (board.name === "Tindeq") {
70
- // TODO: handle Tindeq notify
71
- }
72
- else {
73
- if (notifyCallback) {
74
- notifyCallback({ uuid: characteristic.uuid, value: receivedString });
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
- * connect
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.addEventListener("gattserverdisconnected", (event) => onDisconnected(event, board));
115
- const server = await device.gatt?.connect();
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
- for (const service of services) {
122
- const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid);
123
- if (matchingService) {
124
- const characteristics = await service.getCharacteristics();
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);
package/src/connect.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  import { Device } from "./devices/types"
2
2
  import { notifyCallback } from "./notify"
3
+ import { handleMotherboardData } from "./devices/moterboard"
4
+
5
+ let server: BluetoothRemoteGATTServer
6
+ const receiveBuffer: number[] = []
3
7
 
4
8
  /**
5
9
  * onDisconnected
@@ -18,62 +22,88 @@ const onDisconnected = (event: Event, board: Device): void => {
18
22
  */
19
23
  const handleNotifications = (event: Event, board: Device): void => {
20
24
  const characteristic = event.target as BluetoothRemoteGATTCharacteristic
21
- const receivedData = new Uint8Array(characteristic.value!.buffer)
22
- // Create an array to store the parsed decimal values
23
- const decimalArray: number[] = []
25
+ const value = characteristic.value
26
+ if (value) {
27
+ if (board.name === "Motherboard") {
28
+ if (value) {
29
+ for (let i = 0; i < value.byteLength; i++) {
30
+ receiveBuffer.push(value.getUint8(i))
31
+ }
24
32
 
25
- // Iterate through each byte and convert to decimal
26
- for (let i = 0; i < receivedData.length; i++) {
27
- decimalArray.push(receivedData[i])
28
- }
29
- // Convert the decimal array to a string representation
30
- const receivedString: string = String.fromCharCode(...decimalArray)
31
-
32
- if (board.name === "Motherboard") {
33
- // Split the string into pairs of characters
34
- const hexPairs: RegExpMatchArray | null = receivedString.match(/.{1,2}/g)
35
- // Convert each hexadecimal pair to decimal
36
- const parsedDecimalArray: number[] | undefined = hexPairs?.map((hexPair) => parseInt(hexPair, 16))
37
- // Handle different types of data
38
- if (characteristic.value!.byteLength === 20) {
39
- const elementKeys = [
40
- "frames",
41
- "cycle",
42
- "unknown",
43
- "eleven",
44
- "dynamic1",
45
- "pressure1",
46
- "left",
47
- "dynamic2",
48
- "pressure2",
49
- "right",
50
- ]
51
- const dataObject: { [key: string]: number } = {};
52
-
53
- if (parsedDecimalArray) {
54
- elementKeys.forEach((key: string, index: number) => {
55
- dataObject[key] = parsedDecimalArray[index];
56
- });
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 receivedString = decoder.decode(new Uint8Array(line))
39
+ handleMotherboardData(characteristic.uuid, receivedString)
40
+ }
41
+ }
42
+ } else if (board.name === "ENTRALPI") {
43
+ // TODO: handle Entralpi notify
44
+ // characteristic.value!.getInt16(0) / 100;
45
+ if (notifyCallback) {
46
+ notifyCallback({ uuid: characteristic.uuid, value: value })
57
47
  }
48
+ } else if (board.name === "Tindeq") {
49
+ // TODO: handle Tindeq notify
50
+ } else {
58
51
  if (notifyCallback) {
59
- notifyCallback({ uuid: characteristic.uuid, value: dataObject })
52
+ notifyCallback({ uuid: characteristic.uuid, value: value })
60
53
  }
61
- } else if (characteristic.value!.byteLength === 14) {
62
- // TODO: handle 14 byte data
63
- // notifyCallback({ uuid: characteristic.uuid, value: characteristic.value!.getInt8(0) / 100 })
64
54
  }
65
- } else if (board.name === "ENTRALPI") {
66
- // TODO: handle Entralpi notify
67
- // characteristic.value!.getInt16(0) / 100;
68
- if (notifyCallback) {
69
- notifyCallback({ uuid: characteristic.uuid, value: receivedString })
55
+ }
56
+ }
57
+ /**
58
+ * onConnected
59
+ * @param event
60
+ * @param board
61
+ */
62
+ const onConnected = async (board: Device, onSuccess: () => void): Promise<void> => {
63
+ try {
64
+ const services = await server?.getPrimaryServices()
65
+
66
+ if (!services || services.length === 0) {
67
+ console.error("No services found")
68
+ return
70
69
  }
71
- } else if (board.name === "Tindeq") {
72
- // TODO: handle Tindeq notify
73
- } else {
74
- if (notifyCallback) {
75
- notifyCallback({ uuid: characteristic.uuid, value: receivedString })
70
+
71
+ for (const service of services) {
72
+ const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid)
73
+
74
+ if (matchingService) {
75
+ // Android bug: Introduce a delay before getting characteristics
76
+ await new Promise((resolve) => setTimeout(resolve, 100))
77
+
78
+ const characteristics = await service.getCharacteristics()
79
+
80
+ for (const characteristic of matchingService.characteristics) {
81
+ const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid)
82
+
83
+ if (matchingCharacteristic) {
84
+ const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid)
85
+ if (element) {
86
+ element.characteristic = matchingCharacteristic
87
+
88
+ // notify
89
+ if (element.id === "rx") {
90
+ matchingCharacteristic.startNotifications()
91
+ matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) =>
92
+ handleNotifications(event, board),
93
+ )
94
+ }
95
+ }
96
+ } else {
97
+ console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`)
98
+ }
99
+ }
100
+ }
76
101
  }
102
+
103
+ // Call the onSuccess callback after successful connection and setup
104
+ onSuccess()
105
+ } catch (error) {
106
+ console.error(error)
77
107
  }
78
108
  }
79
109
  /**
@@ -84,7 +114,7 @@ function getAllServiceUUIDs(device: Device) {
84
114
  return device.services.map((service) => service.uuid)
85
115
  }
86
116
  /**
87
- * connect
117
+ * Connect to the BluetoothDevice
88
118
  * @param device
89
119
  * @param onSuccess
90
120
  */
@@ -117,45 +147,18 @@ export const connect = async (board: Device, onSuccess: () => void): Promise<voi
117
147
 
118
148
  board.device = device
119
149
 
120
- device.addEventListener("gattserverdisconnected", (event) => onDisconnected(event, board))
121
-
122
- const server = await device.gatt?.connect()
123
- const services = await server?.getPrimaryServices()
124
-
125
- if (!services || services.length === 0) {
126
- console.error("No services found")
150
+ if (!board.device.gatt) {
151
+ console.error("GATT is not available on this device")
127
152
  return
128
153
  }
129
154
 
130
- for (const service of services) {
131
- const matchingService = board.services.find((boardService) => boardService.uuid === service.uuid)
155
+ server = await board.device?.gatt?.connect()
132
156
 
133
- if (matchingService) {
134
- const characteristics = await service.getCharacteristics()
157
+ board.device.addEventListener("gattserverdisconnected", (event) => onDisconnected(event, board))
135
158
 
136
- for (const characteristic of matchingService.characteristics) {
137
- const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid)
138
-
139
- if (matchingCharacteristic) {
140
- const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid)
141
- if (element) {
142
- element.characteristic = matchingCharacteristic
143
-
144
- // notify
145
- if (element.id === "rx") {
146
- matchingCharacteristic.startNotifications()
147
- matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) =>
148
- handleNotifications(event, board),
149
- )
150
- }
151
- }
152
- } else {
153
- console.warn(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`)
154
- }
155
- }
156
- }
159
+ if (server.connected) {
160
+ await onConnected(board, onSuccess)
157
161
  }
158
- onSuccess()
159
162
  } catch (error) {
160
163
  console.error(error)
161
164
  }
@@ -0,0 +1,2 @@
1
+ import { Device } from "./types";
2
+ export declare const Entralpi: Device;
@@ -0,0 +1,52 @@
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
+ };
@@ -0,0 +1,3 @@
1
+ export { Motherboard } from "./moterboard";
2
+ export { Entralpi } from "./entralpi";
3
+ export { Tindeq } from "./tindeq";
@@ -0,0 +1,3 @@
1
+ export { Motherboard } from "./moterboard";
2
+ export { Entralpi } from "./entralpi";
3
+ export { Tindeq } from "./tindeq";
@@ -0,0 +1,7 @@
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;
@@ -0,0 +1,163 @@
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
+ }