@hangtime/grip-connect 0.1.2 → 0.2.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 CHANGED
@@ -13,30 +13,29 @@ Force-Sensing Hangboards / Plates used by climbers for strength measurement. Exa
13
13
 
14
14
  ## Roadmap
15
15
 
16
+ **Help wanted:** Do you own any of these devices? Use Google Chrome's Bluetooth Internals
17
+ `chrome://bluetooth-internals/#devices` and press `Start Scan` to look for your device, click on `Inspect` and share all
18
+ available services with us.
19
+
16
20
  - ✅ Griptonite Motherboard
17
21
  - ✅️ Connect with devices
18
22
  - ✅️ Read / Write / Notify using Bluetooth
19
- - ➡️ Calibrate Devices
20
23
  - ✅️ Output weight/force stream
21
24
  - ✅ Tindeq Progressor
22
25
  - ✅️ Connect with devices
23
26
  - ✅️ Read / Write / Notify using Bluetooth
24
- - ➡️ Calibrate Devices
25
- - ➡️ Output weight/force stream
27
+ - ✅️ Output weight/force stream
26
28
  - ✅ Entralpi
27
29
  - ✅️ Connect with devices
28
30
  - ✅️ Read / Write / Notify using Bluetooth
29
- - ➡️ Calibrate Devices
30
- - ➡️ Output weight/force stream
31
+ - ✅️️ Output weight/force stream
31
32
  - ➡️ Climbro
32
33
  - ➡️ Connect with devices
33
34
  - ➡️ Read / Write / Notify using Bluetooth
34
- - ➡️ Calibrate Devices
35
35
  - ➡️ Output weight/force stream
36
36
  - ➡️ SmartBoard
37
37
  - ➡️ Connect with devices
38
38
  - ➡️ Read / Write / Notify using Bluetooth
39
- - ➡️ Calibrate Devices
40
39
  - ➡️ Output weight/force stream
41
40
 
42
41
  ## Development
@@ -53,37 +52,32 @@ npm install
53
52
  $ npm install @hangtime/grip-connect
54
53
  ```
55
54
 
56
- ## Example usage (Motherboard)
55
+ ## Example usage (with a Motherboard)
57
56
 
58
- Simply importing the utilities you need from `@hangtime/grip-connect`. Devices that are currently supported:
59
- `Motherboard`, `Tindeq` and `Entralpi`.
57
+ Simply importing the utilities you need from `@hangtime/grip-connect`.
60
58
 
61
59
  ```html
62
60
  <button id="motherboard" type="button">Connect Motherboard</button>
63
61
  ```
64
62
 
65
63
  ```js
66
- import { Motherboard, calibration, connect, disconnect, notify, read, stream } from "@hangtime/grip-connect"
64
+ import { Motherboard, battery, connect, disconnect, info, notify, stream } from "@hangtime/grip-connect"
67
65
 
68
66
  const motherboardButton = document.querySelector("#motherboard")
69
67
 
70
68
  motherboardButton.addEventListener("click", () => {
71
69
  connect(Motherboard, async () => {
72
- // Listen for notifications
70
+ // Listen for stream notifications
73
71
  notify((data) => {
72
+ // data: { massTotal: 0, massLeft: 0, massRight: 0, massCenter: 0 }
74
73
  console.log(data)
75
74
  })
76
75
 
77
76
  // Read battery + device info
78
- await read(Motherboard, "battery", "level", 250)
79
- await read(Motherboard, "device", "manufacturer", 250)
80
- await read(Motherboard, "device", "hardware", 250)
81
- await read(Motherboard, "device", "firmware", 250)
82
-
83
- // Read calibration (required before reading data)
84
- await calibration(Motherboard)
77
+ await battery(Motherboard)
78
+ await info(Motherboard)
85
79
 
86
- // Start streaming (for a minute) remove parameter for a continues stream
80
+ // Start weight streaming (for a minute) remove parameter for a continues stream
87
81
  await stream(Motherboard, 60000)
88
82
 
89
83
  // Manually call stop method if stream is continues
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
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": {
@@ -0,0 +1,6 @@
1
+ import { Device } from "./devices/types";
2
+ /**
3
+ * Get Battery / Voltage information
4
+ * @param board
5
+ */
6
+ export declare const battery: (board: Device) => Promise<void>;
package/src/battery.js ADDED
@@ -0,0 +1,19 @@
1
+ import { write } from "./write";
2
+ import { read } from "./read";
3
+ import { isConnected } from "./is-connected";
4
+ import { Motherboard, Tindeq } from "./devices";
5
+ import { TindeqCommands } from "./commands";
6
+ /**
7
+ * Get Battery / Voltage information
8
+ * @param board
9
+ */
10
+ export const battery = async (board) => {
11
+ if (isConnected(board)) {
12
+ if (board.name === "Motherboard") {
13
+ await read(Motherboard, "battery", "level", 250);
14
+ }
15
+ if (board.name === "Tindeq") {
16
+ await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_BATT_VLTG), 250);
17
+ }
18
+ }
19
+ };
package/src/battery.ts ADDED
@@ -0,0 +1,21 @@
1
+ import { Device } from "./devices/types"
2
+ import { write } from "./write"
3
+ import { read } from "./read"
4
+ import { isConnected } from "./is-connected"
5
+ import { Motherboard, Tindeq } from "./devices"
6
+ import { TindeqCommands } from "./commands"
7
+
8
+ /**
9
+ * Get Battery / Voltage information
10
+ * @param board
11
+ */
12
+ export const battery = async (board: Device): Promise<void> => {
13
+ if (isConnected(board)) {
14
+ if (board.name === "Motherboard") {
15
+ await read(Motherboard, "battery", "level", 250)
16
+ }
17
+ if (board.name === "Tindeq") {
18
+ await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_BATT_VLTG), 250)
19
+ }
20
+ }
21
+ }
@@ -1,14 +1,13 @@
1
+ import { isConnected } from "./is-connected";
2
+ import { write } from "./write";
1
3
  import { Motherboard } from "./devices";
2
4
  import { MotherboardCommands } from "./commands";
3
- import { write } from "./write";
4
5
  /**
5
6
  * write command to get calibration
6
7
  * @param board
7
8
  */
8
9
  export const calibration = async (board) => {
9
- if (!board.device)
10
- return;
11
- if (board.device.gatt?.connected) {
10
+ if (isConnected(board)) {
12
11
  if (board.name === "Motherboard") {
13
12
  await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500);
14
13
  }
@@ -1,15 +1,15 @@
1
1
  import { Device } from "./devices/types"
2
+ import { isConnected } from "./is-connected"
3
+ import { write } from "./write"
2
4
  import { Motherboard } from "./devices"
3
5
  import { MotherboardCommands } from "./commands"
4
- import { write } from "./write"
5
6
 
6
7
  /**
7
8
  * write command to get calibration
8
9
  * @param board
9
10
  */
10
11
  export const calibration = async (board: Device): Promise<void> => {
11
- if (!board.device) return
12
- if (board.device.gatt?.connected) {
12
+ if (isConnected(board)) {
13
13
  if (board.name === "Motherboard") {
14
14
  await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500)
15
15
  }
package/src/connect.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { notifyCallback } from "./notify";
2
- import { handleMotherboardData } from "./devices/motherboard";
3
- import { handleEntralpiData } from "./devices/entralpi";
2
+ import { handleMotherboardData } from "./data";
4
3
  let server;
5
4
  const receiveBuffer = [];
6
5
  /**
@@ -41,7 +40,14 @@ const handleNotifications = (event, board) => {
41
40
  const buffer = value.buffer;
42
41
  const rawData = new DataView(buffer);
43
42
  const receivedData = rawData.getUint16(0) / 100;
44
- handleEntralpiData(characteristic.uuid, receivedData);
43
+ if (notifyCallback) {
44
+ notifyCallback({
45
+ uuid: characteristic.uuid,
46
+ value: {
47
+ massTotal: receivedData,
48
+ },
49
+ });
50
+ }
45
51
  }
46
52
  }
47
53
  else if (board.name === "Tindeq") {
@@ -102,9 +108,9 @@ const onConnected = async (board, onSuccess) => {
102
108
  * Return all service UUIDs
103
109
  * @param device
104
110
  */
105
- function getAllServiceUUIDs(device) {
111
+ const getAllServiceUUIDs = (device) => {
106
112
  return device.services.map((service) => service.uuid);
107
- }
113
+ };
108
114
  /**
109
115
  * Connect to the BluetoothDevice
110
116
  * @param device
package/src/connect.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Device } from "./devices/types"
2
2
  import { notifyCallback } from "./notify"
3
- import { handleMotherboardData } from "./devices/motherboard"
4
- import { handleEntralpiData } from "./devices/entralpi"
3
+ import { handleMotherboardData } from "./data"
5
4
 
6
5
  let server: BluetoothRemoteGATTServer
7
6
  const receiveBuffer: number[] = []
@@ -43,7 +42,14 @@ const handleNotifications = (event: Event, board: Device): void => {
43
42
  const buffer: ArrayBuffer = value.buffer
44
43
  const rawData: DataView = new DataView(buffer)
45
44
  const receivedData: number = rawData.getUint16(0) / 100
46
- handleEntralpiData(characteristic.uuid, receivedData)
45
+ if (notifyCallback) {
46
+ notifyCallback({
47
+ uuid: characteristic.uuid,
48
+ value: {
49
+ massTotal: receivedData,
50
+ },
51
+ })
52
+ }
47
53
  }
48
54
  } else if (board.name === "Tindeq") {
49
55
  // TODO: handle Tindeq notify
@@ -110,7 +116,7 @@ const onConnected = async (board: Device, onSuccess: () => void): Promise<void>
110
116
  * Return all service UUIDs
111
117
  * @param device
112
118
  */
113
- function getAllServiceUUIDs(device: Device) {
119
+ const getAllServiceUUIDs = (device: Device) => {
114
120
  return device.services.map((service) => service.uuid)
115
121
  }
116
122
  /**
package/src/data.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ export declare const CALIBRATION: never[][];
2
+ /**
3
+ * handleMotherboardData
4
+ *
5
+ * @param uuid - Unique identifier
6
+ * @param receivedData - Received data string
7
+ */
8
+ export declare const handleMotherboardData: (uuid: string, receivedData: string) => void;
package/src/data.js ADDED
@@ -0,0 +1,109 @@
1
+ import { notifyCallback } from "./notify";
2
+ const PACKET_LENGTH = 32;
3
+ const NUM_SAMPLES = 3;
4
+ export const CALIBRATION = [[], [], [], []];
5
+ /**
6
+ * applyCalibration
7
+ * @param sample
8
+ * @param calibration
9
+ */
10
+ const applyCalibration = (sample, calibration) => {
11
+ // Extract the calibrated value for the zero point
12
+ const zeroCalibration = calibration[0][2];
13
+ // Initialize sign as positive
14
+ let sign = 1;
15
+ // Initialize the final calibrated value
16
+ let final = 0;
17
+ // If the sample value is less than the zero calibration point
18
+ if (sample < zeroCalibration) {
19
+ // Change the sign to negative
20
+ sign = -1;
21
+ // Reflect the sample around the zero calibration point
22
+ sample = /* 2 * zeroCalibration */ -sample;
23
+ }
24
+ // Iterate through the calibration data
25
+ for (let i = 1; i < calibration.length; i++) {
26
+ // Extract the lower and upper bounds of the current calibration range
27
+ const calibrationStart = calibration[i - 1][2];
28
+ const calibrationEnd = calibration[i][2];
29
+ // If the sample value is within the current calibration range
30
+ if (sample < calibrationEnd) {
31
+ // Interpolate to get the calibrated value within the range
32
+ final =
33
+ calibration[i - 1][1] +
34
+ ((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
35
+ (calibration[i][1] - calibration[i - 1][1]);
36
+ break;
37
+ }
38
+ }
39
+ // Return the calibrated value with the appropriate sign (positive/negative)
40
+ return sign * final;
41
+ };
42
+ /**
43
+ * handleMotherboardData
44
+ *
45
+ * @param uuid - Unique identifier
46
+ * @param receivedData - Received data string
47
+ */
48
+ export const handleMotherboardData = (uuid, receivedData) => {
49
+ const receivedTime = Date.now();
50
+ // Check if the line is entirely hex characters
51
+ const isAllHex = /^[0-9A-Fa-f]+$/g.test(receivedData);
52
+ // Handle streaming packet
53
+ if (isAllHex && receivedData.length === PACKET_LENGTH) {
54
+ // Base-16 decode the string: convert hex pairs to byte values
55
+ const bytes = Array.from({ length: receivedData.length / 2 }, (_, i) => Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`));
56
+ // Translate header into packet, number of samples from the packet length
57
+ const packet = {
58
+ received: receivedTime,
59
+ sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
60
+ battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
61
+ samples: [],
62
+ masses: [],
63
+ };
64
+ const dataView = new DataView(new Uint8Array(bytes).buffer);
65
+ for (let i = 0; i < NUM_SAMPLES; i++) {
66
+ const sampleStart = 4 + 3 * i;
67
+ // Use DataView to read the 24-bit unsigned integer
68
+ const rawValue = dataView.getUint8(sampleStart) |
69
+ (dataView.getUint8(sampleStart + 1) << 8) |
70
+ (dataView.getUint8(sampleStart + 2) << 16);
71
+ // Ensure unsigned 32-bit integer
72
+ packet.samples[i] = rawValue >>> 0;
73
+ if (packet.samples[i] >= 0x7fffff) {
74
+ packet.samples[i] -= 0x1000000;
75
+ }
76
+ // if (!CALIBRATION[0].length) return
77
+ packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i]);
78
+ }
79
+ // invert center and right values
80
+ packet.masses[1] *= -1;
81
+ packet.masses[2] *= -1;
82
+ // map to variables
83
+ const left = packet.masses[0];
84
+ const center = packet.masses[1];
85
+ const right = packet.masses[2];
86
+ if (notifyCallback) {
87
+ notifyCallback({
88
+ uuid,
89
+ value: {
90
+ massTotal: Math.max(-1000, left + right + center).toFixed(1),
91
+ massLeft: Math.max(-1000, left).toFixed(1),
92
+ massRight: Math.max(-1000, right).toFixed(1),
93
+ massCenter: Math.max(-1000, center).toFixed(1),
94
+ },
95
+ });
96
+ }
97
+ }
98
+ else if ((receivedData.match(/,/g) || []).length === 3) {
99
+ console.log(receivedData);
100
+ // if the returned notification is a calibration string add them to the array
101
+ const parts = receivedData.split(",");
102
+ const numericParts = parts.map((x) => parseFloat(x));
103
+ CALIBRATION[numericParts[0]].push(numericParts.slice(1));
104
+ }
105
+ else {
106
+ // unhanded data
107
+ console.log(receivedData);
108
+ }
109
+ };
package/src/data.ts ADDED
@@ -0,0 +1,133 @@
1
+ import { notifyCallback } from "./notify"
2
+
3
+ const PACKET_LENGTH: number = 32
4
+ const NUM_SAMPLES: number = 3
5
+ export const CALIBRATION = [[], [], [], []]
6
+ /**
7
+ * applyCalibration
8
+ * @param sample
9
+ * @param calibration
10
+ */
11
+ const applyCalibration = (sample: number, calibration: number[][]): number => {
12
+ // Extract the calibrated value for the zero point
13
+ const zeroCalibration: number = calibration[0][2]
14
+
15
+ // Initialize sign as positive
16
+ let sign: number = 1
17
+
18
+ // Initialize the final calibrated value
19
+ let final: number = 0
20
+
21
+ // If the sample value is less than the zero calibration point
22
+ if (sample < zeroCalibration) {
23
+ // Change the sign to negative
24
+ sign = -1
25
+
26
+ // Reflect the sample around the zero calibration point
27
+ sample = /* 2 * zeroCalibration */ -sample
28
+ }
29
+
30
+ // Iterate through the calibration data
31
+ for (let i = 1; i < calibration.length; i++) {
32
+ // Extract the lower and upper bounds of the current calibration range
33
+ const calibrationStart: number = calibration[i - 1][2]
34
+ const calibrationEnd: number = calibration[i][2]
35
+
36
+ // If the sample value is within the current calibration range
37
+ if (sample < calibrationEnd) {
38
+ // Interpolate to get the calibrated value within the range
39
+ final =
40
+ calibration[i - 1][1] +
41
+ ((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
42
+ (calibration[i][1] - calibration[i - 1][1])
43
+ break
44
+ }
45
+ }
46
+ // Return the calibrated value with the appropriate sign (positive/negative)
47
+ return sign * final
48
+ }
49
+
50
+ interface Packet {
51
+ received: number
52
+ sampleNum: number
53
+ battRaw: number
54
+ samples: number[]
55
+ masses: number[]
56
+ }
57
+
58
+ /**
59
+ * handleMotherboardData
60
+ *
61
+ * @param uuid - Unique identifier
62
+ * @param receivedData - Received data string
63
+ */
64
+ export const handleMotherboardData = (uuid: string, receivedData: string): void => {
65
+ const receivedTime: number = Date.now()
66
+
67
+ // Check if the line is entirely hex characters
68
+ const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedData)
69
+
70
+ // Handle streaming packet
71
+ if (isAllHex && receivedData.length === PACKET_LENGTH) {
72
+ // Base-16 decode the string: convert hex pairs to byte values
73
+ const bytes: number[] = Array.from({ length: receivedData.length / 2 }, (_, i) =>
74
+ Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`),
75
+ )
76
+
77
+ // Translate header into packet, number of samples from the packet length
78
+ const packet: Packet = {
79
+ received: receivedTime,
80
+ sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
81
+ battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
82
+ samples: [],
83
+ masses: [],
84
+ }
85
+
86
+ const dataView = new DataView(new Uint8Array(bytes).buffer)
87
+
88
+ for (let i = 0; i < NUM_SAMPLES; i++) {
89
+ const sampleStart: number = 4 + 3 * i
90
+ // Use DataView to read the 24-bit unsigned integer
91
+ const rawValue =
92
+ dataView.getUint8(sampleStart) |
93
+ (dataView.getUint8(sampleStart + 1) << 8) |
94
+ (dataView.getUint8(sampleStart + 2) << 16)
95
+
96
+ // Ensure unsigned 32-bit integer
97
+ packet.samples[i] = rawValue >>> 0
98
+
99
+ if (packet.samples[i] >= 0x7fffff) {
100
+ packet.samples[i] -= 0x1000000
101
+ }
102
+ // if (!CALIBRATION[0].length) return
103
+ packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i])
104
+ }
105
+ // invert center and right values
106
+ packet.masses[1] *= -1
107
+ packet.masses[2] *= -1
108
+ // map to variables
109
+ const left: number = packet.masses[0]
110
+ const center: number = packet.masses[1]
111
+ const right: number = packet.masses[2]
112
+ if (notifyCallback) {
113
+ notifyCallback({
114
+ uuid,
115
+ value: {
116
+ massTotal: Math.max(-1000, left + right + center).toFixed(1),
117
+ massLeft: Math.max(-1000, left).toFixed(1),
118
+ massRight: Math.max(-1000, right).toFixed(1),
119
+ massCenter: Math.max(-1000, center).toFixed(1),
120
+ },
121
+ })
122
+ }
123
+ } else if ((receivedData.match(/,/g) || []).length === 3) {
124
+ console.log(receivedData)
125
+ // if the returned notification is a calibration string add them to the array
126
+ const parts: string[] = receivedData.split(",")
127
+ const numericParts: number[] = parts.map((x) => parseFloat(x))
128
+ ;(CALIBRATION[numericParts[0]] as number[][]).push(numericParts.slice(1))
129
+ } else {
130
+ // unhanded data
131
+ console.log(receivedData)
132
+ }
133
+ }
@@ -1,8 +1,2 @@
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;
@@ -1,4 +1,3 @@
1
- import { notifyCallback } from "../notify";
2
1
  export const Entralpi = {
3
2
  name: "ENTRALPI",
4
3
  services: [
@@ -51,18 +50,3 @@ export const Entralpi = {
51
50
  },
52
51
  ],
53
52
  };
54
- /**
55
- * handleEntralpiData
56
- * @param uuid - Unique identifier
57
- * @param receivedData - Received data string
58
- */
59
- export function handleEntralpiData(uuid, receivedData) {
60
- if (notifyCallback) {
61
- notifyCallback({
62
- uuid,
63
- value: {
64
- massTotal: receivedData,
65
- },
66
- });
67
- }
68
- }
@@ -1,5 +1,4 @@
1
1
  import { Device } from "./types"
2
- import { notifyCallback } from "../notify"
3
2
 
4
3
  export const Entralpi: Device = {
5
4
  name: "ENTRALPI",
@@ -53,19 +52,3 @@ export const Entralpi: Device = {
53
52
  },
54
53
  ],
55
54
  }
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
- if (notifyCallback) {
64
- notifyCallback({
65
- uuid,
66
- value: {
67
- massTotal: receivedData,
68
- },
69
- })
70
- }
71
- }
@@ -1,8 +1,2 @@
1
1
  import { Device } from "./types";
2
2
  export declare const Motherboard: Device;
3
- /**
4
- * handleMotherboardData
5
- * @param uuid - Unique identifier
6
- * @param receivedData - Received data string
7
- */
8
- export declare function handleMotherboardData(uuid: string, receivedData: string): void;
@@ -1,7 +1,3 @@
1
- import { notifyCallback } from "../notify";
2
- const PACKET_LENGTH = 32;
3
- const NUM_SAMPLES = 3;
4
- const CALIBRATION = [[], [], [], []];
5
1
  export const Motherboard = {
6
2
  name: "Motherboard",
7
3
  companyId: 0x2a29,
@@ -81,109 +77,3 @@ export const Motherboard = {
81
77
  },
82
78
  ],
83
79
  };
84
- /**
85
- * applyCalibration
86
- * @param sample
87
- * @param calibration
88
- */
89
- const applyCalibration = (sample, calibration) => {
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
95
- let final = 0;
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;
102
- }
103
- // Iterate through the calibration data
104
- for (let i = 1; i < calibration.length; i++) {
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
111
- final =
112
- calibration[i - 1][1] +
113
- ((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
114
- (calibration[i][1] - calibration[i - 1][1]);
115
- break;
116
- }
117
- }
118
- // Return the calibrated value with the appropriate sign (positive/negative)
119
- return sign * final;
120
- };
121
- /**
122
- * handleMotherboardData
123
- * @param uuid - Unique identifier
124
- * @param receivedData - Received data string
125
- */
126
- export function handleMotherboardData(uuid, receivedData) {
127
- const receivedTime = Date.now();
128
- // Check if the line is entirely hex characters
129
- const isAllHex = /^[0-9A-Fa-f]+$/g.test(receivedData);
130
- // Handle streaming packet
131
- if (isAllHex && receivedData.length === PACKET_LENGTH) {
132
- // Base-16 decode the string: convert hex pairs to byte values
133
- const bytes = Array.from({ length: receivedData.length / 2 }, (_, i) => Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`));
134
- // Translate header into packet, number of samples from the packet length
135
- const packet = {
136
- received: receivedTime,
137
- sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
138
- battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
139
- samples: [],
140
- masses: [],
141
- };
142
- const dataView = new DataView(new Uint8Array(bytes).buffer);
143
- for (let i = 0; i < NUM_SAMPLES; i++) {
144
- const sampleStart = 4 + 3 * i;
145
- // Use DataView to read the 24-bit unsigned integer
146
- const rawValue = dataView.getUint8(sampleStart) |
147
- (dataView.getUint8(sampleStart + 1) << 8) |
148
- (dataView.getUint8(sampleStart + 2) << 16);
149
- // Ensure unsigned 32-bit integer
150
- packet.samples[i] = rawValue >>> 0;
151
- if (packet.samples[i] >= 0x7fffff) {
152
- packet.samples[i] -= 0x1000000;
153
- }
154
- // TODO: make sure device is calibrated
155
- if (!CALIBRATION[0].length)
156
- return;
157
- packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i]);
158
- }
159
- // invert center and right values
160
- packet.masses[1] *= -1;
161
- packet.masses[2] *= -1;
162
- // map to variables
163
- const left = packet.masses[0];
164
- const center = packet.masses[1];
165
- const right = packet.masses[2];
166
- if (notifyCallback) {
167
- notifyCallback({
168
- uuid,
169
- value: {
170
- massTotal: Math.max(-1000, left + right + center).toFixed(1),
171
- massLeft: Math.max(-1000, left).toFixed(1),
172
- massRight: Math.max(-1000, right).toFixed(1),
173
- massCenter: Math.max(-1000, center).toFixed(1),
174
- },
175
- });
176
- }
177
- }
178
- else if ((receivedData.match(/,/g) || []).length === 3) {
179
- console.log(receivedData);
180
- // if the returned notification is a calibration string add them to the array
181
- const parts = receivedData.split(",");
182
- const numericParts = parts.map((x) => parseFloat(x));
183
- CALIBRATION[numericParts[0]].push(numericParts.slice(1));
184
- }
185
- else {
186
- // unhanded data
187
- console.log(receivedData);
188
- }
189
- }
@@ -1,9 +1,4 @@
1
1
  import { Device } from "./types"
2
- import { notifyCallback } from "../notify"
3
-
4
- const PACKET_LENGTH: number = 32
5
- const NUM_SAMPLES: number = 3
6
- const CALIBRATION = [[], [], [], []]
7
2
 
8
3
  export const Motherboard: Device = {
9
4
  name: "Motherboard",
@@ -84,132 +79,3 @@ export const Motherboard: Device = {
84
79
  },
85
80
  ],
86
81
  }
87
- /**
88
- * applyCalibration
89
- * @param sample
90
- * @param calibration
91
- */
92
- const applyCalibration = (sample: number, calibration: number[][]): number => {
93
- // Extract the calibrated value for the zero point
94
- const zeroCalibration: number = calibration[0][2]
95
-
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
109
- }
110
-
111
- // Iterate through the calibration data
112
- for (let i = 1; i < calibration.length; i++) {
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]
116
-
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
120
- final =
121
- calibration[i - 1][1] +
122
- ((sample - calibrationStart) / (calibrationEnd - calibrationStart)) *
123
- (calibration[i][1] - calibration[i - 1][1])
124
- break
125
- }
126
- }
127
- // Return the calibrated value with the appropriate sign (positive/negative)
128
- return sign * final
129
- }
130
-
131
- interface Packet {
132
- received: number
133
- sampleNum: number
134
- battRaw: number
135
- samples: number[]
136
- masses: number[]
137
- }
138
-
139
- /**
140
- * handleMotherboardData
141
- * @param uuid - Unique identifier
142
- * @param receivedData - Received data string
143
- */
144
- export function handleMotherboardData(uuid: string, receivedData: string): void {
145
- const receivedTime: number = Date.now()
146
-
147
- // Check if the line is entirely hex characters
148
- const isAllHex: boolean = /^[0-9A-Fa-f]+$/g.test(receivedData)
149
-
150
- // Handle streaming packet
151
- if (isAllHex && receivedData.length === PACKET_LENGTH) {
152
- // Base-16 decode the string: convert hex pairs to byte values
153
- const bytes: number[] = Array.from({ length: receivedData.length / 2 }, (_, i) =>
154
- Number(`0x${receivedData.substring(i * 2, i * 2 + 2)}`),
155
- )
156
-
157
- // Translate header into packet, number of samples from the packet length
158
- const packet: Packet = {
159
- received: receivedTime,
160
- sampleNum: new DataView(new Uint8Array(bytes).buffer).getUint16(0, true),
161
- battRaw: new DataView(new Uint8Array(bytes).buffer).getUint16(2, true),
162
- samples: [],
163
- masses: [],
164
- }
165
-
166
- const dataView = new DataView(new Uint8Array(bytes).buffer)
167
-
168
- for (let i = 0; i < NUM_SAMPLES; i++) {
169
- const sampleStart: number = 4 + 3 * i
170
- // Use DataView to read the 24-bit unsigned integer
171
- const rawValue =
172
- dataView.getUint8(sampleStart) |
173
- (dataView.getUint8(sampleStart + 1) << 8) |
174
- (dataView.getUint8(sampleStart + 2) << 16)
175
-
176
- // Ensure unsigned 32-bit integer
177
- packet.samples[i] = rawValue >>> 0
178
-
179
- if (packet.samples[i] >= 0x7fffff) {
180
- packet.samples[i] -= 0x1000000
181
- }
182
-
183
- // TODO: make sure device is calibrated
184
- if (!CALIBRATION[0].length) return
185
- packet.masses[i] = applyCalibration(packet.samples[i], CALIBRATION[i])
186
- }
187
- // invert center and right values
188
- packet.masses[1] *= -1
189
- packet.masses[2] *= -1
190
- // map to variables
191
- const left: number = packet.masses[0]
192
- const center: number = packet.masses[1]
193
- const right: number = packet.masses[2]
194
- if (notifyCallback) {
195
- notifyCallback({
196
- uuid,
197
- value: {
198
- massTotal: Math.max(-1000, left + right + center).toFixed(1),
199
- massLeft: Math.max(-1000, left).toFixed(1),
200
- massRight: Math.max(-1000, right).toFixed(1),
201
- massCenter: Math.max(-1000, center).toFixed(1),
202
- },
203
- })
204
- }
205
- } else if ((receivedData.match(/,/g) || []).length === 3) {
206
- console.log(receivedData)
207
- // if the returned notification is a calibration string add them to the array
208
- const parts: string[] = receivedData.split(",")
209
- const numericParts: number[] = parts.map((x) => parseFloat(x))
210
- ;(CALIBRATION[numericParts[0]] as number[][]).push(numericParts.slice(1))
211
- } else {
212
- // unhanded data
213
- console.log(receivedData)
214
- }
215
- }
package/src/disconnect.js CHANGED
@@ -1,11 +1,10 @@
1
+ import { isConnected } from "./is-connected";
1
2
  /**
2
3
  * disconnect
3
4
  * @param board
4
5
  */
5
6
  export const disconnect = (board) => {
6
- if (!board.device)
7
- return;
8
- if (board.device.gatt?.connected) {
9
- board.device.gatt?.disconnect();
7
+ if (isConnected(board)) {
8
+ board.device?.gatt?.disconnect();
10
9
  }
11
10
  };
package/src/disconnect.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { Device } from "./devices/types"
2
+ import { isConnected } from "./is-connected"
2
3
 
3
4
  /**
4
5
  * disconnect
5
6
  * @param board
6
7
  */
7
8
  export const disconnect = (board: Device): void => {
8
- if (!board.device) return
9
- if (board.device.gatt?.connected) {
10
- board.device.gatt?.disconnect()
9
+ if (isConnected(board)) {
10
+ board.device?.gatt?.disconnect()
11
11
  }
12
12
  }
package/src/index.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export { Climbro, Entralpi, Motherboard, SmartBoard, Tindeq } from "./devices/index";
2
+ export { battery } from "./battery";
2
3
  export { calibration } from "./calibration";
3
4
  export { connect } from "./connect";
4
5
  export { disconnect } from "./disconnect";
6
+ export { isConnected } from "./is-connected";
7
+ export { info } from "./info";
5
8
  export { notify } from "./notify";
6
- export { read } from "./read";
7
9
  export { stop } from "./stop";
8
10
  export { stream } from "./stream";
package/src/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
  export { Climbro, Entralpi, Motherboard, SmartBoard, Tindeq } from "./devices/index";
2
+ export { battery } from "./battery";
2
3
  export { calibration } from "./calibration";
3
4
  export { connect } from "./connect";
4
5
  export { disconnect } from "./disconnect";
6
+ export { isConnected } from "./is-connected";
7
+ export { info } from "./info";
5
8
  export { notify } from "./notify";
6
- export { read } from "./read";
9
+ // export { read } from "./read"
7
10
  export { stop } from "./stop";
8
11
  export { stream } from "./stream";
12
+ // export { write } from "./write"
package/src/index.ts CHANGED
@@ -1,15 +1,23 @@
1
1
  export { Climbro, Entralpi, Motherboard, SmartBoard, Tindeq } from "./devices/index"
2
2
 
3
+ export { battery } from "./battery"
4
+
3
5
  export { calibration } from "./calibration"
4
6
 
5
7
  export { connect } from "./connect"
6
8
 
7
9
  export { disconnect } from "./disconnect"
8
10
 
11
+ export { isConnected } from "./is-connected"
12
+
13
+ export { info } from "./info"
14
+
9
15
  export { notify } from "./notify"
10
16
 
11
- export { read } from "./read"
17
+ // export { read } from "./read"
12
18
 
13
19
  export { stop } from "./stop"
14
20
 
15
21
  export { stream } from "./stream"
22
+
23
+ // export { write } from "./write"
package/src/info.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { Device } from "./devices/types";
2
+ /**
3
+ * Get device information
4
+ * @param board
5
+ */
6
+ export declare const info: (board: Device) => Promise<void>;
package/src/info.js ADDED
@@ -0,0 +1,23 @@
1
+ import { write } from "./write";
2
+ import { read } from "./read";
3
+ import { isConnected } from "./is-connected";
4
+ import { Motherboard, Tindeq } from "./devices";
5
+ import { MotherboardCommands, TindeqCommands } from "./commands";
6
+ /**
7
+ * Get device information
8
+ * @param board
9
+ */
10
+ export const info = async (board) => {
11
+ if (isConnected(board)) {
12
+ if (board.name === "Motherboard") {
13
+ await read(Motherboard, "device", "manufacturer", 250);
14
+ await read(Motherboard, "device", "hardware", 250);
15
+ await read(Motherboard, "device", "firmware", 250);
16
+ await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_TEXT), 250);
17
+ await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_SERIAL), 250);
18
+ }
19
+ if (board.name === "Tindeq") {
20
+ await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_APP_VERSION), 250);
21
+ }
22
+ }
23
+ };
package/src/info.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { Device } from "./devices/types"
2
+ import { write } from "./write"
3
+ import { read } from "./read"
4
+ import { isConnected } from "./is-connected"
5
+ import { Motherboard, Tindeq } from "./devices"
6
+ import { MotherboardCommands, TindeqCommands } from "./commands"
7
+
8
+ /**
9
+ * Get device information
10
+ * @param board
11
+ */
12
+ export const info = async (board: Device): Promise<void> => {
13
+ if (isConnected(board)) {
14
+ if (board.name === "Motherboard") {
15
+ await read(Motherboard, "device", "manufacturer", 250)
16
+ await read(Motherboard, "device", "hardware", 250)
17
+ await read(Motherboard, "device", "firmware", 250)
18
+ await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_TEXT), 250)
19
+ await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_SERIAL), 250)
20
+ }
21
+ if (board.name === "Tindeq") {
22
+ await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_APP_VERSION), 250)
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,7 @@
1
+ import { Device } from "./devices/types";
2
+ /**
3
+ * Checks if a Bluetooth device is connected
4
+ * @param board
5
+ * @return boolean
6
+ */
7
+ export declare const isConnected: (board: Device) => boolean;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Checks if a Bluetooth device is connected
3
+ * @param board
4
+ * @return boolean
5
+ */
6
+ export const isConnected = (board) => {
7
+ if (!board.device)
8
+ return false;
9
+ return !!board.device.gatt?.connected;
10
+ };
@@ -0,0 +1,11 @@
1
+ import { Device } from "./devices/types"
2
+
3
+ /**
4
+ * Checks if a Bluetooth device is connected
5
+ * @param board
6
+ * @return boolean
7
+ */
8
+ export const isConnected = (board: Device): boolean => {
9
+ if (!board.device) return false
10
+ return !!board.device.gatt?.connected
11
+ }
package/src/notify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Initialize the callback variable
2
2
  export let notifyCallback;
3
- // Export a function to set the callback
3
+ // Export a cost to set the callback
4
4
  export const notify = (callback) => {
5
5
  notifyCallback = callback;
6
6
  };
package/src/notify.ts CHANGED
@@ -1,10 +1,10 @@
1
- // Define the callback function type
1
+ // Define the callbacks type
2
2
  type NotifyCallback = (data: object) => void
3
3
 
4
4
  // Initialize the callback variable
5
5
  export let notifyCallback: NotifyCallback
6
6
 
7
- // Export a function to set the callback
7
+ // Export a cost to set the callback
8
8
  export const notify = (callback: NotifyCallback) => {
9
9
  notifyCallback = callback
10
10
  }
package/src/read.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { notifyCallback } from "./notify";
2
2
  import { getCharacteristic } from "./characteristic";
3
+ import { isConnected } from "./is-connected";
3
4
  /**
4
5
  * read
5
6
  * @param characteristic
6
7
  */
7
8
  export const read = (board, serviceId, characteristicId, duration = 0) => {
8
9
  return new Promise((resolve, reject) => {
9
- if (board.device?.gatt?.connected) {
10
+ if (isConnected(board)) {
10
11
  const characteristic = getCharacteristic(board, serviceId, characteristicId);
11
12
  if (characteristic) {
12
13
  characteristic
package/src/read.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Device } from "./devices/types"
2
2
  import { notifyCallback } from "./notify"
3
3
  import { getCharacteristic } from "./characteristic"
4
+ import { isConnected } from "./is-connected"
4
5
 
5
6
  /**
6
7
  * read
@@ -13,7 +14,7 @@ export const read = (
13
14
  duration: number = 0,
14
15
  ): Promise<void> => {
15
16
  return new Promise((resolve, reject) => {
16
- if (board.device?.gatt?.connected) {
17
+ if (isConnected(board)) {
17
18
  const characteristic = getCharacteristic(board, serviceId, characteristicId)
18
19
 
19
20
  if (characteristic) {
package/src/stop.js CHANGED
@@ -1,14 +1,13 @@
1
+ import { write } from "./write";
2
+ import { isConnected } from "./is-connected";
1
3
  import { Motherboard, Tindeq } from "./devices";
2
4
  import { MotherboardCommands, TindeqCommands } from "./commands";
3
- import { write } from "./write";
4
5
  /**
5
6
  * read calibration
6
7
  * @param board
7
8
  */
8
9
  export const stop = async (board) => {
9
- if (!board.device)
10
- return;
11
- if (board.device.gatt?.connected) {
10
+ if (isConnected(board)) {
12
11
  if (board.name === "Motherboard") {
13
12
  await write(Motherboard, "uart", "tx", String(MotherboardCommands.STOP_WEIGHT_MEAS), 0);
14
13
  }
package/src/stop.ts CHANGED
@@ -1,15 +1,15 @@
1
1
  import { Device } from "./devices/types"
2
+ import { write } from "./write"
3
+ import { isConnected } from "./is-connected"
2
4
  import { Motherboard, Tindeq } from "./devices"
3
5
  import { MotherboardCommands, TindeqCommands } from "./commands"
4
- import { write } from "./write"
5
6
 
6
7
  /**
7
8
  * read calibration
8
9
  * @param board
9
10
  */
10
11
  export const stop = async (board: Device): Promise<void> => {
11
- if (!board.device) return
12
- if (board.device.gatt?.connected) {
12
+ if (isConnected(board)) {
13
13
  if (board.name === "Motherboard") {
14
14
  await write(Motherboard, "uart", "tx", String(MotherboardCommands.STOP_WEIGHT_MEAS), 0)
15
15
  }
package/src/stream.js CHANGED
@@ -1,20 +1,24 @@
1
- import { Motherboard, Tindeq } from "./devices";
2
- import { MotherboardCommands, TindeqCommands } from "./commands";
1
+ import { isConnected } from "./is-connected";
3
2
  import { write } from "./write";
4
3
  import { stop } from "./stop";
4
+ import { Motherboard, Tindeq } from "./devices";
5
+ import { MotherboardCommands, TindeqCommands } from "./commands";
6
+ import { CALIBRATION } from "./data";
7
+ import { calibration } from "./calibration";
5
8
  /**
6
9
  * stream output
7
10
  * @param board
8
11
  */
9
12
  export const stream = async (board, duration = 0) => {
10
- if (!board.device)
11
- return;
12
- if (board.device.gatt?.connected) {
13
+ if (isConnected(board)) {
13
14
  if (board.name === "Motherboard") {
14
- // TODO: add check if device is recalibrated
15
+ // read calibration (required before reading data)
16
+ if (!CALIBRATION[0].length) {
17
+ await calibration(Motherboard);
18
+ }
15
19
  // start stream
16
20
  await write(Motherboard, "uart", "tx", String(MotherboardCommands.START_WEIGHT_MEAS), duration);
17
- // end stream
21
+ // end stream if duration is set
18
22
  if (duration !== 0) {
19
23
  await stop(Motherboard);
20
24
  }
@@ -22,7 +26,7 @@ export const stream = async (board, duration = 0) => {
22
26
  if (board.name === "Tindeq") {
23
27
  // start stream
24
28
  await write(Tindeq, "progressor", "tx", String(TindeqCommands.START_WEIGHT_MEAS), duration);
25
- // end stream
29
+ // end stream if duration is set
26
30
  if (duration !== 0) {
27
31
  await stop(Tindeq);
28
32
  }
package/src/stream.ts CHANGED
@@ -1,21 +1,26 @@
1
1
  import { Device } from "./devices/types"
2
- import { Motherboard, Tindeq } from "./devices"
3
- import { MotherboardCommands, TindeqCommands } from "./commands"
2
+ import { isConnected } from "./is-connected"
4
3
  import { write } from "./write"
5
4
  import { stop } from "./stop"
5
+ import { Motherboard, Tindeq } from "./devices"
6
+ import { MotherboardCommands, TindeqCommands } from "./commands"
7
+ import { CALIBRATION } from "./data"
8
+ import { calibration } from "./calibration"
6
9
 
7
10
  /**
8
11
  * stream output
9
12
  * @param board
10
13
  */
11
14
  export const stream = async (board: Device, duration: number = 0): Promise<void> => {
12
- if (!board.device) return
13
- if (board.device.gatt?.connected) {
15
+ if (isConnected(board)) {
14
16
  if (board.name === "Motherboard") {
15
- // TODO: add check if device is recalibrated
17
+ // read calibration (required before reading data)
18
+ if (!CALIBRATION[0].length) {
19
+ await calibration(Motherboard)
20
+ }
16
21
  // start stream
17
22
  await write(Motherboard, "uart", "tx", String(MotherboardCommands.START_WEIGHT_MEAS), duration)
18
- // end stream
23
+ // end stream if duration is set
19
24
  if (duration !== 0) {
20
25
  await stop(Motherboard)
21
26
  }
@@ -23,7 +28,7 @@ export const stream = async (board: Device, duration: number = 0): Promise<void>
23
28
  if (board.name === "Tindeq") {
24
29
  // start stream
25
30
  await write(Tindeq, "progressor", "tx", String(TindeqCommands.START_WEIGHT_MEAS), duration)
26
- // end stream
31
+ // end stream if duration is set
27
32
  if (duration !== 0) {
28
33
  await stop(Tindeq)
29
34
  }
package/src/write.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isConnected } from "./is-connected";
1
2
  import { getCharacteristic } from "./characteristic";
2
3
  /**
3
4
  * write
@@ -6,7 +7,7 @@ import { getCharacteristic } from "./characteristic";
6
7
  */
7
8
  export const write = (board, serviceId, characteristicId, message, duration = 0) => {
8
9
  return new Promise((resolve, reject) => {
9
- if (board.device?.gatt?.connected) {
10
+ if (isConnected(board)) {
10
11
  const encoder = new TextEncoder();
11
12
  const characteristic = getCharacteristic(board, serviceId, characteristicId);
12
13
  if (characteristic) {
package/src/write.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Device } from "./devices/types"
2
+ import { isConnected } from "./is-connected"
2
3
  import { getCharacteristic } from "./characteristic"
4
+
3
5
  /**
4
6
  * write
5
7
  * @param characteristic
@@ -13,7 +15,7 @@ export const write = (
13
15
  duration: number = 0,
14
16
  ): Promise<void> => {
15
17
  return new Promise((resolve, reject) => {
16
- if (board.device?.gatt?.connected) {
18
+ if (isConnected(board)) {
17
19
  const encoder = new TextEncoder()
18
20
 
19
21
  const characteristic = getCharacteristic(board, serviceId, characteristicId)
package/tsconfig.json CHANGED
@@ -2,6 +2,6 @@
2
2
  "extends": "../../tsconfig.json",
3
3
  "include": ["src"],
4
4
  "compilerOptions": {
5
- "tsBuildInfoFile": "node_modules/.cache/.tsbuildinfo",
6
- },
5
+ "tsBuildInfoFile": "node_modules/.cache/.tsbuildinfo"
6
+ }
7
7
  }