@hangtime/grip-connect 0.1.2 → 0.2.1

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.
Files changed (71) hide show
  1. package/README.md +22 -25
  2. package/package.json +2 -2
  3. package/src/battery.ts +21 -0
  4. package/src/calibration.ts +4 -4
  5. package/src/commands/index.ts +2 -2
  6. package/src/commands/{tindeq.ts → progressor.ts} +7 -3
  7. package/src/commands/types.ts +5 -5
  8. package/src/connect.ts +74 -9
  9. package/src/data.ts +133 -0
  10. package/src/devices/entralpi.ts +0 -17
  11. package/src/devices/index.ts +1 -1
  12. package/src/devices/motherboard.ts +0 -134
  13. package/src/devices/{tindeq.ts → progressor.ts} +17 -5
  14. package/src/disconnect.ts +3 -3
  15. package/src/index.ts +10 -2
  16. package/src/info.ts +25 -0
  17. package/src/is-connected.ts +11 -0
  18. package/src/notify.ts +2 -2
  19. package/src/read.ts +2 -1
  20. package/src/stop.ts +7 -7
  21. package/src/stream.ts +16 -11
  22. package/src/write.ts +10 -5
  23. package/tsconfig.json +2 -2
  24. package/src/calibration.d.ts +0 -6
  25. package/src/calibration.js +0 -16
  26. package/src/characteristic.d.ts +0 -9
  27. package/src/characteristic.js +0 -15
  28. package/src/commands/climbro.d.ts +0 -6
  29. package/src/commands/climbro.js +0 -5
  30. package/src/commands/entralpi.d.ts +0 -6
  31. package/src/commands/entralpi.js +0 -5
  32. package/src/commands/index.d.ts +0 -5
  33. package/src/commands/index.js +0 -5
  34. package/src/commands/motherboard.d.ts +0 -6
  35. package/src/commands/motherboard.js +0 -13
  36. package/src/commands/smartboard.d.ts +0 -6
  37. package/src/commands/smartboard.js +0 -5
  38. package/src/commands/tindeq.d.ts +0 -11
  39. package/src/commands/tindeq.js +0 -23
  40. package/src/commands/types.d.ts +0 -18
  41. package/src/commands/types.js +0 -1
  42. package/src/connect.d.ts +0 -7
  43. package/src/connect.js +0 -150
  44. package/src/devices/climbro.d.ts +0 -2
  45. package/src/devices/climbro.js +0 -4
  46. package/src/devices/entralpi.d.ts +0 -8
  47. package/src/devices/entralpi.js +0 -68
  48. package/src/devices/index.d.ts +0 -5
  49. package/src/devices/index.js +0 -5
  50. package/src/devices/motherboard.d.ts +0 -8
  51. package/src/devices/motherboard.js +0 -189
  52. package/src/devices/smartboard.d.ts +0 -2
  53. package/src/devices/smartboard.js +0 -4
  54. package/src/devices/tindeq.d.ts +0 -2
  55. package/src/devices/tindeq.js +0 -22
  56. package/src/devices/types.d.ts +0 -20
  57. package/src/devices/types.js +0 -1
  58. package/src/disconnect.d.ts +0 -6
  59. package/src/disconnect.js +0 -11
  60. package/src/index.d.ts +0 -8
  61. package/src/index.js +0 -8
  62. package/src/notify.d.ts +0 -4
  63. package/src/notify.js +0 -6
  64. package/src/read.d.ts +0 -6
  65. package/src/read.js +0 -44
  66. package/src/stop.d.ts +0 -6
  67. package/src/stop.js +0 -19
  68. package/src/stream.d.ts +0 -6
  69. package/src/stream.js +0 -31
  70. package/src/write.d.ts +0 -7
  71. package/src/write.js +0 -34
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
@@ -100,13 +94,16 @@ motherboardButton.addEventListener("click", () => {
100
94
  A special thank you to:
101
95
 
102
96
  - [@CassimLadha](https://github.com/CassimLadha) for sharing insights on reading the Motherboards data.
103
- - [@donaldharvey](https://github.com/donaldharvey) for a valuable example on connecting to the motherboard.
104
- - [@ecstrema](https://github.com/ecstrema) for providing an example on how to play games with the entralpi.
97
+ - [@donaldharvey](https://github.com/donaldharvey) for a valuable example on connecting to the Motherboard.
98
+ - [@ecstrema](https://github.com/ecstrema) for providing [examples](https://github.com/ecstrema/entralpi-games) on how
99
+ to play games with the Entralpi.
100
+ - [@StuartLittlefair](https://github.com/StuartLittlefair) for his
101
+ [PyTindeq](https://github.com/StuartLittlefair/PyTindeq) implementation.
105
102
 
106
- ## Disclamer
103
+ ## Disclaimer
107
104
 
108
- THIS SOFTWARE IS NOT OFFICIALY SUPPORTED, SUPPLIED OR MAINTAINED BY THE DEVICE MANUFACTURER. BY USING THE SOFTWARE YOU
109
- ARE ACKNOWLEDGEING THIS AND UNDERSTAND THAT USING THIS SOFTWARE WILL INVALIDATE THE MANUFACTURERS WARRANTY.
105
+ THIS SOFTWARE IS NOT OFFICIALLY SUPPORTED, SUPPLIED OR MAINTAINED BY THE DEVICE MANUFACTURER. BY USING THE SOFTWARE YOU
106
+ ARE ACKNOWLEDGING THIS AND UNDERSTAND THAT USING THIS SOFTWARE WILL INVALIDATE THE MANUFACTURERS WARRANTY.
110
107
 
111
108
  ## License
112
109
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.1.2",
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",
3
+ "version": "0.2.1",
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 Griptonite Motherboard, Climbro, SmartBoard, Entralpi or Tindeq Progressor",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
7
7
  "build": "tsc --build"
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, Progressor } from "./devices"
6
+ import { ProgressorCommands } 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 && board.name.startsWith("Progressor")) {
18
+ await write(Progressor, "progressor", "tx", ProgressorCommands.GET_BATT_VLTG, 250)
19
+ }
20
+ }
21
+ }
@@ -1,17 +1,17 @@
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
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500)
14
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_CALIBRATION, 2500)
15
15
  }
16
16
  }
17
17
  }
@@ -4,6 +4,6 @@ export { EntralpiCommands } from "./entralpi"
4
4
 
5
5
  export { MotherboardCommands } from "./motherboard"
6
6
 
7
- export { SmartBoardCommands } from "./smartboard"
7
+ export { ProgressorCommands } from "./progressor"
8
8
 
9
- export { TindeqCommands } from "./tindeq"
9
+ export { SmartBoardCommands } from "./smartboard"
@@ -3,7 +3,7 @@ import { Commands } from "../commands/types"
3
3
  * Warning:
4
4
  * Using other commands can seriously harm your device
5
5
  */
6
- export const TindeqCommands: Commands = {
6
+ export const ProgressorCommands: Commands = {
7
7
  TARE_SCALE: "d", // 0x64,
8
8
  START_WEIGHT_MEAS: "e", // 0x65,
9
9
  STOP_WEIGHT_MEAS: "f", // 0x66,
@@ -11,14 +11,18 @@ export const TindeqCommands: Commands = {
11
11
  START_PEAK_RFD_MEAS_SERIES: "h", // 0x68,
12
12
  ADD_CALIB_POINT: "i", // 0x69,
13
13
  SAVE_CALIB: "j", // 0x6a,
14
- GET_APP_VERSION: "k", // 0x6b,
14
+ GET_FW_VERSION: "k", // 0x6b,
15
15
  GET_ERR_INFO: "l", // 0x6c,
16
16
  CLR_ERR_INFO: "m", // 0x6d,
17
17
  SLEEP: "n", // 0x6e,
18
18
  GET_BATT_VLTG: "o", // 0x6f,
19
19
  }
20
20
 
21
- export const NotificationTypes = {
21
+ /**
22
+ * The Progressor returns a Uint8Array.
23
+ * The first item [0] is the type of response it returns
24
+ */
25
+ export const ProgressorResponses = {
22
26
  COMMAND_RESPONSE: 0,
23
27
  WEIGHT_MEASURE: 1,
24
28
  LOW_BATTERY_WARNING: 2,
@@ -1,20 +1,20 @@
1
1
  export interface Commands {
2
- // Motherboard, Tindeq
2
+ // Motherboard, Progressor
3
3
  START_WEIGHT_MEAS?: string
4
4
  STOP_WEIGHT_MEAS?: string
5
5
  SLEEP?: number | string
6
- // Motherboard
7
- GET_TEXT?: string
8
6
  GET_SERIAL?: string
7
+ // Griptonite Motherboard
8
+ GET_TEXT?: string
9
9
  DEBUG_STREAM?: string
10
10
  GET_CALIBRATION?: string
11
- // Tindeq
11
+ // Tindeq Progressor
12
12
  TARE_SCALE?: string
13
13
  START_PEAK_RFD_MEAS?: string
14
14
  START_PEAK_RFD_MEAS_SERIES?: string
15
15
  ADD_CALIB_POINT?: string
16
16
  SAVE_CALIB?: string
17
- GET_APP_VERSION?: string
17
+ GET_FW_VERSION?: string
18
18
  GET_ERR_INFO?: string
19
19
  CLR_ERR_INFO?: string
20
20
  GET_BATT_VLTG?: string
package/src/connect.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Device } from "./devices/types"
2
+ import { ProgressorCommands, ProgressorResponses } from "./commands/progressor"
2
3
  import { notifyCallback } from "./notify"
3
- import { handleMotherboardData } from "./devices/motherboard"
4
- import { handleEntralpiData } from "./devices/entralpi"
4
+ import { handleMotherboardData } from "./data"
5
+ import { lastWrite } from "./write"
5
6
 
6
7
  let server: BluetoothRemoteGATTServer
7
8
  const receiveBuffer: number[] = []
@@ -24,6 +25,19 @@ const onDisconnected = (event: Event, board: Device): void => {
24
25
  const handleNotifications = (event: Event, board: Device): void => {
25
26
  const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
26
27
  const value: DataView | undefined = characteristic.value
28
+
29
+ function _unpackFloat(bytes: Uint8Array) {
30
+ const view = new DataView(new ArrayBuffer(4))
31
+ for (let i = 0; i < 4; i++) {
32
+ view.setUint8(i, bytes[i])
33
+ }
34
+ return view.getFloat32(0, true)
35
+ }
36
+
37
+ // function _unpackInt(bytes: Uint8Array) {
38
+ // return (bytes[1] << 8) + bytes[0];
39
+ // }
40
+
27
41
  if (value) {
28
42
  if (board.name === "Motherboard") {
29
43
  for (let i: number = 0; i < value.byteLength; i++) {
@@ -43,10 +57,62 @@ const handleNotifications = (event: Event, board: Device): void => {
43
57
  const buffer: ArrayBuffer = value.buffer
44
58
  const rawData: DataView = new DataView(buffer)
45
59
  const receivedData: number = rawData.getUint16(0) / 100
46
- handleEntralpiData(characteristic.uuid, receivedData)
60
+ if (notifyCallback) {
61
+ notifyCallback({
62
+ uuid: characteristic.uuid,
63
+ value: {
64
+ massTotal: receivedData,
65
+ },
66
+ })
67
+ }
68
+ }
69
+ } else if (board.name && board.name.startsWith("Progressor")) {
70
+ if (value.buffer) {
71
+ const buffer: ArrayBuffer = value.buffer
72
+ const rawData: Uint8Array = new Uint8Array(buffer)
73
+ const kind: number = rawData[0]
74
+ const tare: number = 0 // todo: add tare
75
+ if (kind === ProgressorResponses.WEIGHT_MEASURE) {
76
+ for (let i = 2; i < rawData.length; i += 6) {
77
+ const weight = _unpackFloat(rawData.slice(i, i + 4))
78
+ // let useconds = _unpackInt(rawData.slice(i + 4, i + 6));
79
+ // let now = useconds / 1.0e6;
80
+
81
+ if (notifyCallback) {
82
+ notifyCallback({
83
+ uuid: characteristic.uuid,
84
+ value: {
85
+ massTotal: weight - tare,
86
+ },
87
+ })
88
+ }
89
+ }
90
+ } else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
91
+ if (!lastWrite) return
92
+
93
+ let value: string = ""
94
+
95
+ if (lastWrite === ProgressorCommands.GET_BATT_VLTG) {
96
+ const vdd = new DataView(rawData.buffer, 2).getUint32(0, true)
97
+ value = `Battery level = ${vdd} [mV]`
98
+ } else if (lastWrite === ProgressorCommands.GET_FW_VERSION) {
99
+ value = new TextDecoder().decode(rawData.slice(2))
100
+ } else if (lastWrite === ProgressorCommands.GET_ERR_INFO) {
101
+ value = new TextDecoder().decode(rawData.slice(2))
102
+ }
103
+ if (notifyCallback) {
104
+ notifyCallback({ uuid: characteristic.uuid, value: value })
105
+ }
106
+ } else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
107
+ if (notifyCallback) {
108
+ notifyCallback({ uuid: characteristic.uuid, value: "low power warning" })
109
+ }
110
+ } else {
111
+ if (notifyCallback) {
112
+ notifyCallback({ uuid: characteristic.uuid, value: `unknown message kind ${kind}` })
113
+ }
114
+ }
47
115
  }
48
- } else if (board.name === "Tindeq") {
49
- // TODO: handle Tindeq notify
50
116
  } else {
51
117
  if (notifyCallback) {
52
118
  notifyCallback({ uuid: characteristic.uuid, value: value })
@@ -110,7 +176,7 @@ const onConnected = async (board: Device, onSuccess: () => void): Promise<void>
110
176
  * Return all service UUIDs
111
177
  * @param device
112
178
  */
113
- function getAllServiceUUIDs(device: Device) {
179
+ const getAllServiceUUIDs = (device: Device) => {
114
180
  return device.services.map((service) => service.uuid)
115
181
  }
116
182
  /**
@@ -126,9 +192,8 @@ export const connect = async (board: Device, onSuccess: () => void): Promise<voi
126
192
  const filters = []
127
193
 
128
194
  if (board.name) {
129
- filters.push({
130
- name: board.name,
131
- })
195
+ const filterName = board.name === "Progressor" ? { namePrefix: board.name } : { name: board.name }
196
+ filters.push(filterName)
132
197
  }
133
198
  if (board.companyId) {
134
199
  filters.push({
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,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
- }
@@ -6,4 +6,4 @@ export { Motherboard } from "./motherboard"
6
6
 
7
7
  export { SmartBoard } from "./smartboard"
8
8
 
9
- export { Tindeq } from "./tindeq"
9
+ export { Progressor } from "./progressor"
@@ -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
- }