@hangtime/grip-connect 0.10.1 → 0.10.2
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 +1 -1
- package/dist/cjs/index.d.ts +1 -2
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interfaces/callback.interface.d.ts +19 -12
- package/dist/cjs/interfaces/callback.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/command.interface.d.ts +10 -6
- package/dist/cjs/interfaces/command.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device/progressor.interface.d.ts +42 -0
- package/dist/cjs/interfaces/device/progressor.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device.interface.d.ts +1 -1
- package/dist/cjs/interfaces/download.interface.d.ts +8 -11
- package/dist/cjs/interfaces/download.interface.d.ts.map +1 -1
- package/dist/cjs/models/device/climbro.model.d.ts.map +1 -1
- package/dist/cjs/models/device/climbro.model.js +34 -16
- package/dist/cjs/models/device/climbro.model.js.map +1 -1
- package/dist/cjs/models/device/entralpi.model.d.ts.map +1 -1
- package/dist/cjs/models/device/entralpi.model.js +11 -14
- package/dist/cjs/models/device/entralpi.model.js.map +1 -1
- package/dist/cjs/models/device/forceboard.model.d.ts.map +1 -1
- package/dist/cjs/models/device/forceboard.model.js +12 -17
- package/dist/cjs/models/device/forceboard.model.js.map +1 -1
- package/dist/cjs/models/device/motherboard.model.d.ts.map +1 -1
- package/dist/cjs/models/device/motherboard.model.js +28 -20
- package/dist/cjs/models/device/motherboard.model.js.map +1 -1
- package/dist/cjs/models/device/pb-700bt.model.d.ts.map +1 -1
- package/dist/cjs/models/device/pb-700bt.model.js +11 -14
- package/dist/cjs/models/device/pb-700bt.model.js.map +1 -1
- package/dist/cjs/models/device/progressor.model.d.ts +47 -5
- package/dist/cjs/models/device/progressor.model.d.ts.map +1 -1
- package/dist/cjs/models/device/progressor.model.js +185 -49
- package/dist/cjs/models/device/progressor.model.js.map +1 -1
- package/dist/cjs/models/device/smartboard-pro.model.d.ts.map +1 -1
- package/dist/cjs/models/device/smartboard-pro.model.js +11 -16
- package/dist/cjs/models/device/smartboard-pro.model.js.map +1 -1
- package/dist/cjs/models/device/wh-c06.model.d.ts.map +1 -1
- package/dist/cjs/models/device/wh-c06.model.js +11 -14
- package/dist/cjs/models/device/wh-c06.model.js.map +1 -1
- package/dist/cjs/models/device.model.d.ts +64 -3
- package/dist/cjs/models/device.model.d.ts.map +1 -1
- package/dist/cjs/models/device.model.js +142 -37
- package/dist/cjs/models/device.model.js.map +1 -1
- package/dist/cjs/utils.d.ts +1 -1
- package/dist/cjs/utils.d.ts.map +1 -1
- package/dist/cjs/utils.js +29 -6
- package/dist/cjs/utils.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/callback.interface.d.ts +19 -12
- package/dist/interfaces/callback.interface.d.ts.map +1 -1
- package/dist/interfaces/command.interface.d.ts +10 -6
- package/dist/interfaces/command.interface.d.ts.map +1 -1
- package/dist/interfaces/device/progressor.interface.d.ts +42 -0
- package/dist/interfaces/device/progressor.interface.d.ts.map +1 -1
- package/dist/interfaces/device.interface.d.ts +1 -1
- package/dist/interfaces/download.interface.d.ts +8 -11
- package/dist/interfaces/download.interface.d.ts.map +1 -1
- package/dist/models/device/climbro.model.d.ts.map +1 -1
- package/dist/models/device/climbro.model.js +34 -16
- package/dist/models/device/climbro.model.js.map +1 -1
- package/dist/models/device/entralpi.model.d.ts.map +1 -1
- package/dist/models/device/entralpi.model.js +11 -14
- package/dist/models/device/entralpi.model.js.map +1 -1
- package/dist/models/device/forceboard.model.d.ts.map +1 -1
- package/dist/models/device/forceboard.model.js +12 -17
- package/dist/models/device/forceboard.model.js.map +1 -1
- package/dist/models/device/motherboard.model.d.ts.map +1 -1
- package/dist/models/device/motherboard.model.js +28 -20
- package/dist/models/device/motherboard.model.js.map +1 -1
- package/dist/models/device/pb-700bt.model.d.ts.map +1 -1
- package/dist/models/device/pb-700bt.model.js +11 -14
- package/dist/models/device/pb-700bt.model.js.map +1 -1
- package/dist/models/device/progressor.model.d.ts +47 -5
- package/dist/models/device/progressor.model.d.ts.map +1 -1
- package/dist/models/device/progressor.model.js +184 -49
- package/dist/models/device/progressor.model.js.map +1 -1
- package/dist/models/device/smartboard-pro.model.d.ts.map +1 -1
- package/dist/models/device/smartboard-pro.model.js +11 -16
- package/dist/models/device/smartboard-pro.model.js.map +1 -1
- package/dist/models/device/wh-c06.model.d.ts.map +1 -1
- package/dist/models/device/wh-c06.model.js +11 -14
- package/dist/models/device/wh-c06.model.js.map +1 -1
- package/dist/models/device.model.d.ts +64 -3
- package/dist/models/device.model.d.ts.map +1 -1
- package/dist/models/device.model.js +146 -37
- package/dist/models/device.model.js.map +1 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +29 -6
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +0 -3
- package/src/interfaces/callback.interface.ts +21 -12
- package/src/interfaces/command.interface.ts +11 -6
- package/src/interfaces/device/progressor.interface.ts +50 -0
- package/src/interfaces/device.interface.ts +1 -1
- package/src/interfaces/download.interface.ts +9 -11
- package/src/models/device/climbro.model.ts +38 -20
- package/src/models/device/entralpi.model.ts +15 -17
- package/src/models/device/forceboard.model.ts +15 -19
- package/src/models/device/motherboard.model.ts +38 -26
- package/src/models/device/pb-700bt.model.ts +14 -16
- package/src/models/device/progressor.model.ts +198 -50
- package/src/models/device/smartboard-pro.model.ts +14 -19
- package/src/models/device/wh-c06.model.ts +14 -17
- package/src/models/device.model.ts +181 -41
- package/src/utils.ts +31 -6
|
@@ -164,13 +164,14 @@ export class PB700BT extends Device {
|
|
|
164
164
|
*/
|
|
165
165
|
override handleNotifications = (value: DataView): void => {
|
|
166
166
|
if (value) {
|
|
167
|
-
// Update timestamp
|
|
168
167
|
this.updateTimestamp()
|
|
169
168
|
if (value.buffer) {
|
|
170
169
|
const period = value.getUint32(0, false)
|
|
171
170
|
|
|
172
171
|
if (!Number.isFinite(period) || period === 0) return
|
|
173
172
|
|
|
173
|
+
this.currentSamplesPerPacket = 1
|
|
174
|
+
this.recordPacketReceived()
|
|
174
175
|
const ts = value.getUint32(4, false)
|
|
175
176
|
|
|
176
177
|
const rpmFloat = 60 * (666666 / period)
|
|
@@ -183,31 +184,28 @@ export class PB700BT extends Device {
|
|
|
183
184
|
|
|
184
185
|
const numericData = receivedData - this.applyTare(receivedData)
|
|
185
186
|
|
|
186
|
-
|
|
187
|
-
this.downloadPackets.push({
|
|
188
|
-
received: receivedTime,
|
|
189
|
-
sampleNum: ts,
|
|
190
|
-
battRaw: 0,
|
|
191
|
-
samples: [numericData],
|
|
192
|
-
masses: [numericData],
|
|
193
|
-
})
|
|
187
|
+
const currentMassTotal = Math.max(-1000, numericData)
|
|
194
188
|
|
|
195
|
-
// Update
|
|
189
|
+
// Update session stats before building packet
|
|
196
190
|
this.peak = Math.max(this.peak, numericData)
|
|
197
|
-
|
|
198
|
-
// Update running sum and count
|
|
199
|
-
const currentMassTotal = Math.max(-1000, numericData)
|
|
191
|
+
this.min = Math.min(this.min, Math.max(-1000, numericData))
|
|
200
192
|
this.sum += currentMassTotal
|
|
201
193
|
this.dataPointCount++
|
|
202
|
-
|
|
203
|
-
// Calculate the average dynamically
|
|
204
194
|
this.mean = this.sum / this.dataPointCount
|
|
205
195
|
|
|
196
|
+
// Add data to downloadable Array
|
|
197
|
+
this.downloadPackets.push(
|
|
198
|
+
this.buildDownloadPacket(currentMassTotal, [numericData], {
|
|
199
|
+
timestamp: receivedTime,
|
|
200
|
+
sampleIndex: ts,
|
|
201
|
+
}),
|
|
202
|
+
)
|
|
203
|
+
|
|
206
204
|
// Check if device is being used
|
|
207
205
|
this.activityCheck(numericData)
|
|
208
206
|
|
|
209
207
|
// Notify with weight data
|
|
210
|
-
this.notifyCallback(this.buildForceMeasurement(
|
|
208
|
+
this.notifyCallback(this.buildForceMeasurement(currentMassTotal))
|
|
211
209
|
}
|
|
212
210
|
}
|
|
213
211
|
}
|
|
@@ -40,7 +40,51 @@ enum ProgressorResponses {
|
|
|
40
40
|
* Represents a Tindeq Progressor device.
|
|
41
41
|
* {@link https://tindeq.com}
|
|
42
42
|
*/
|
|
43
|
+
/** One second in microseconds (device timestamp unit). Used for Hz = samples in last 1s. */
|
|
44
|
+
const ONE_SECOND_US = 1_000_000
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Format bytes as hex string.
|
|
48
|
+
* @param payload - Bytes to format
|
|
49
|
+
* @param separator - String between bytes (default " ")
|
|
50
|
+
*/
|
|
51
|
+
function toHex(payload: Uint8Array, separator = " "): string {
|
|
52
|
+
return Array.from(payload)
|
|
53
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
54
|
+
.join(separator)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Parse ProgressorId response: u64 little-endian, device may omit trailing zero bytes.
|
|
59
|
+
* Format as hex string MSB-first to match the official app.
|
|
60
|
+
*/
|
|
61
|
+
function parseProgressorIdPayload(payload: Uint8Array): string {
|
|
62
|
+
if (payload.length === 0) return ""
|
|
63
|
+
const reversed = Uint8Array.from(payload)
|
|
64
|
+
reversed.reverse()
|
|
65
|
+
return toHex(reversed, "").toUpperCase()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Parse calibration curve: 12 opaque bytes (CalibrationCurve = [u8; 12]).
|
|
70
|
+
* Progressor two-point calibration stores two raw ADC readings (zero + reference)
|
|
71
|
+
* and a third value (version/reserved). Exact layout is device-specific.
|
|
72
|
+
*/
|
|
73
|
+
function parseCalibrationCurvePayload(payload: Uint8Array): string {
|
|
74
|
+
const hex = toHex(payload)
|
|
75
|
+
|
|
76
|
+
if (payload.length !== 12) return hex
|
|
77
|
+
|
|
78
|
+
const view = new DataView(payload.buffer, payload.byteOffset, payload.byteLength)
|
|
79
|
+
const [v0, v1, v2] = [0, 4, 8].map((o) => view.getUint32(o, true))
|
|
80
|
+
|
|
81
|
+
return `${hex} — zero-point: ${v1.toLocaleString()} | ref-point: ${v0.toLocaleString()} | version: ${v2} (0x${v2.toString(16)})`
|
|
82
|
+
}
|
|
83
|
+
|
|
43
84
|
export class Progressor extends Device implements IProgressor {
|
|
85
|
+
/** Device timestamps (µs) of recent samples (samples in last 1s device time). */
|
|
86
|
+
private recentSampleTimestamps: number[] = []
|
|
87
|
+
|
|
44
88
|
constructor() {
|
|
45
89
|
super({
|
|
46
90
|
filters: [{ namePrefix: "Progressor" }],
|
|
@@ -75,19 +119,22 @@ export class Progressor extends Device implements IProgressor {
|
|
|
75
119
|
],
|
|
76
120
|
},
|
|
77
121
|
],
|
|
122
|
+
// Tindeq API: opcode = single byte (ASCII char code = decimal 100–114;)
|
|
78
123
|
commands: {
|
|
79
|
-
TARE_SCALE: "d", // 0x64
|
|
80
|
-
START_WEIGHT_MEAS: "e", // 0x65
|
|
81
|
-
STOP_WEIGHT_MEAS: "f", // 0x66
|
|
82
|
-
START_PEAK_RFD_MEAS: "g", //
|
|
83
|
-
START_PEAK_RFD_MEAS_SERIES: "h", //
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
SLEEP: "n", // 0x6e
|
|
90
|
-
|
|
124
|
+
TARE_SCALE: "d", // 100 (0x64)
|
|
125
|
+
START_WEIGHT_MEAS: "e", // 101 (0x65)
|
|
126
|
+
STOP_WEIGHT_MEAS: "f", // 102 (0x66)
|
|
127
|
+
START_PEAK_RFD_MEAS: "g", // 103 (0x67)
|
|
128
|
+
START_PEAK_RFD_MEAS_SERIES: "h", // 104 (0x68)
|
|
129
|
+
ADD_CALIBRATION_POINT: "i", // 105 (0x69)
|
|
130
|
+
SAVE_CALIBRATION: "j", // 106 (0x6a)
|
|
131
|
+
GET_FIRMWARE_VERSION: "k", // 107 (0x6b)
|
|
132
|
+
GET_ERROR_INFORMATION: "l", // 108 (0x6c)
|
|
133
|
+
CLR_ERROR_INFORMATION: "m", // 109 (0x6d)
|
|
134
|
+
SLEEP: "n", // 110 (0x6e)
|
|
135
|
+
GET_BATTERY_VOLTAGE: "o", // 111 (0x6f)
|
|
136
|
+
GET_PROGRESSOR_ID: "p", // 112 (0x70)
|
|
137
|
+
GET_CALIBRATION: "r", // 114 (0x72)
|
|
91
138
|
},
|
|
92
139
|
})
|
|
93
140
|
}
|
|
@@ -98,24 +145,108 @@ export class Progressor extends Device implements IProgressor {
|
|
|
98
145
|
*/
|
|
99
146
|
battery = async (): Promise<string | undefined> => {
|
|
100
147
|
let response: string | undefined = undefined
|
|
101
|
-
await this.write("progressor", "tx", this.commands.
|
|
148
|
+
await this.write("progressor", "tx", this.commands.GET_BATTERY_VOLTAGE, 250, (data) => {
|
|
102
149
|
response = data
|
|
103
150
|
})
|
|
104
151
|
return response
|
|
105
152
|
}
|
|
106
153
|
|
|
107
154
|
/**
|
|
108
|
-
* Retrieves firmware version from the device.
|
|
155
|
+
* Retrieves firmware version from the device (GetAppVersion, opcode 0x6B).
|
|
109
156
|
* @returns {Promise<string>} A Promise that resolves with the firmware version,
|
|
110
157
|
*/
|
|
111
158
|
firmware = async (): Promise<string | undefined> => {
|
|
112
159
|
let response: string | undefined = undefined
|
|
113
|
-
await this.write("progressor", "tx", this.commands.
|
|
160
|
+
await this.write("progressor", "tx", this.commands.GET_FIRMWARE_VERSION, 250, (data) => {
|
|
114
161
|
response = data
|
|
115
162
|
})
|
|
116
163
|
return response
|
|
117
164
|
}
|
|
118
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Retrieves the Progressor ID from the device (opcode 0x70).
|
|
168
|
+
* @returns {Promise<string>} A Promise that resolves with the raw response (hex of payload).
|
|
169
|
+
*/
|
|
170
|
+
progressorId = async (): Promise<string | undefined> => {
|
|
171
|
+
let response: string | undefined = undefined
|
|
172
|
+
await this.write("progressor", "tx", this.commands.GET_PROGRESSOR_ID, 250, (data) => {
|
|
173
|
+
response = data
|
|
174
|
+
})
|
|
175
|
+
return response
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Retrieves calibration values from the device.
|
|
180
|
+
* @returns {Promise<string>} A Promise that resolves with the raw response (hex of payload).
|
|
181
|
+
*/
|
|
182
|
+
calibration = async (): Promise<string | undefined> => {
|
|
183
|
+
let response: string | undefined = undefined
|
|
184
|
+
await this.write("progressor", "tx", this.commands.GET_CALIBRATION, 250, (data) => {
|
|
185
|
+
response = data
|
|
186
|
+
})
|
|
187
|
+
return response
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Saves the current calibration to the device. Call after adding both calibration points.
|
|
192
|
+
* @returns {Promise<void>} A Promise that resolves when the command is sent.
|
|
193
|
+
*/
|
|
194
|
+
saveCalibration = async (): Promise<void> => {
|
|
195
|
+
await this.write("progressor", "tx", this.commands.SAVE_CALIBRATION, 0)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Adds a calibration point: 0x69 + 32-bit float (weight in kg), 5 bytes to the control characteristic.
|
|
200
|
+
* Use for two-point calibration: (1) zero weight — addCalibrationPoint(0); (2) known weight — addCalibrationPoint(knownKg).
|
|
201
|
+
* Order can be either way; writing zero first is recommended (hysteresis). Then call saveCalibration().
|
|
202
|
+
* @param {number} weightKg - Weight in kg (use 0 for zero point, or known reference weight < 150 kg).
|
|
203
|
+
* @returns {Promise<void>} A Promise that resolves when the command is sent.
|
|
204
|
+
*/
|
|
205
|
+
addCalibrationPoint = async (weightKg: number): Promise<void> => {
|
|
206
|
+
const payload = new Uint8Array(5)
|
|
207
|
+
payload[0] = (this.commands.ADD_CALIBRATION_POINT as string).charCodeAt(0) // 0x69
|
|
208
|
+
new DataView(payload.buffer).setFloat32(1, weightKg, true)
|
|
209
|
+
await this.write("progressor", "tx", payload, 0)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Sends the device tare command to zero the scale (hardware tare).
|
|
214
|
+
* For software tare over a duration, use the base tare(duration) method.
|
|
215
|
+
* @returns {Promise<void>} A Promise that resolves when the command is sent.
|
|
216
|
+
*/
|
|
217
|
+
tareScale = async (): Promise<void> => {
|
|
218
|
+
await this.write("progressor", "tx", this.commands.TARE_SCALE, 0)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Puts the device to sleep / shutdown.
|
|
223
|
+
* @returns {Promise<void>} A Promise that resolves when the command is sent.
|
|
224
|
+
*/
|
|
225
|
+
sleep = async (): Promise<void> => {
|
|
226
|
+
const cmd = this.commands.SLEEP
|
|
227
|
+
await this.write("progressor", "tx", typeof cmd === "string" ? cmd : String(cmd), 0)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Retrieves error information from the device.
|
|
232
|
+
* @returns {Promise<string | undefined>} A Promise that resolves with the error info text.
|
|
233
|
+
*/
|
|
234
|
+
errorInfo = async (): Promise<string | undefined> => {
|
|
235
|
+
let response: string | undefined = undefined
|
|
236
|
+
await this.write("progressor", "tx", this.commands.GET_ERROR_INFORMATION, 250, (data) => {
|
|
237
|
+
response = data
|
|
238
|
+
})
|
|
239
|
+
return response
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Clears error information on the device.
|
|
244
|
+
* @returns {Promise<void>} A Promise that resolves when the command is sent.
|
|
245
|
+
*/
|
|
246
|
+
clearErrorInfo = async (): Promise<void> => {
|
|
247
|
+
await this.write("progressor", "tx", this.commands.CLR_ERROR_INFORMATION, 0)
|
|
248
|
+
}
|
|
249
|
+
|
|
119
250
|
/**
|
|
120
251
|
* Handles data received from the device, processes weight measurements,
|
|
121
252
|
* and updates mass data including maximum and average values.
|
|
@@ -133,57 +264,67 @@ export class Progressor extends Device implements IProgressor {
|
|
|
133
264
|
const kind = value.getInt8(0)
|
|
134
265
|
// Check if the message is a weight measurement
|
|
135
266
|
if (kind === ProgressorResponses.WEIGHT_MEASURE) {
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
267
|
+
// Payload = bytes from index 2 (skip byte 0 = message type, byte 1 = reserved/length)
|
|
268
|
+
const payloadLength = value.byteLength - 2
|
|
269
|
+
if (payloadLength % 8 !== 0) return
|
|
270
|
+
const samplesPerPacket = payloadLength / 8
|
|
271
|
+
this.currentSamplesPerPacket = samplesPerPacket
|
|
272
|
+
this.recordPacketReceived()
|
|
273
|
+
|
|
274
|
+
// for (i = 0; i < samplesPerPacket; i++) { offset = i*8; weight = getFloat32(offset); timestampUs = getUint32(offset+4); }
|
|
275
|
+
for (let i = 0; i < samplesPerPacket; i++) {
|
|
276
|
+
const offset = 2 + i * 8
|
|
141
277
|
const weight = value.getFloat32(offset, true)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// Read a 32-bit integer (4 bytes) for the seconds, using little-endian
|
|
145
|
-
const seconds = value.getInt32(offset, true)
|
|
146
|
-
// Move the offset by 4 bytes
|
|
147
|
-
offset += 4
|
|
148
|
-
// Check if both weight and seconds are valid numbers
|
|
149
|
-
if (!isNaN(weight) && !isNaN(seconds)) {
|
|
150
|
-
// Tare correction
|
|
278
|
+
const timestampUs = value.getUint32(offset + 4, true)
|
|
279
|
+
if (!isNaN(weight)) {
|
|
151
280
|
const numericData = weight - this.applyTare(weight)
|
|
152
|
-
// Add data to downloadable Array
|
|
153
|
-
this.downloadPackets.push({
|
|
154
|
-
received: receivedTime,
|
|
155
|
-
sampleNum: seconds,
|
|
156
|
-
battRaw: 0,
|
|
157
|
-
samples: [weight],
|
|
158
|
-
masses: [numericData],
|
|
159
|
-
})
|
|
160
|
-
// Check for max weight
|
|
161
|
-
this.peak = Math.max(this.peak, Number(numericData))
|
|
162
|
-
// Update running sum and count
|
|
163
281
|
const currentMassTotal = Math.max(-1000, Number(numericData))
|
|
282
|
+
|
|
283
|
+
// Update session stats before building packet
|
|
284
|
+
this.peak = Math.max(this.peak, Number(numericData))
|
|
285
|
+
this.min = Math.min(this.min, Math.max(-1000, Number(numericData)))
|
|
164
286
|
this.sum += currentMassTotal
|
|
165
287
|
this.dataPointCount++
|
|
166
|
-
|
|
167
|
-
// Calculate the average dynamically
|
|
168
288
|
this.mean = this.sum / this.dataPointCount
|
|
169
289
|
|
|
170
|
-
|
|
290
|
+
this.downloadPackets.push(
|
|
291
|
+
this.buildDownloadPacket(currentMassTotal, [weight], {
|
|
292
|
+
timestamp: receivedTime,
|
|
293
|
+
sampleIndex: timestampUs,
|
|
294
|
+
}),
|
|
295
|
+
)
|
|
171
296
|
this.activityCheck(numericData)
|
|
172
297
|
|
|
173
|
-
|
|
298
|
+
// Hz from device timestamps: keep only samples in last 1s
|
|
299
|
+
this.recentSampleTimestamps.push(timestampUs)
|
|
300
|
+
const latestUs = this.recentSampleTimestamps[this.recentSampleTimestamps.length - 1] ?? 0
|
|
301
|
+
this.recentSampleTimestamps = this.recentSampleTimestamps.filter((ts) => latestUs - ts <= ONE_SECOND_US)
|
|
302
|
+
const samplingRateHz = this.recentSampleTimestamps.length
|
|
303
|
+
|
|
304
|
+
const payload = this.buildForceMeasurement(currentMassTotal)
|
|
305
|
+
if (payload.performance) payload.performance.samplingRateHz = samplingRateHz
|
|
306
|
+
this.notifyCallback(payload)
|
|
174
307
|
}
|
|
175
308
|
}
|
|
176
309
|
} else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
|
|
177
310
|
if (!this.writeLast) return
|
|
178
311
|
|
|
179
312
|
let output = ""
|
|
313
|
+
const payload = new Uint8Array(value.buffer).slice(2)
|
|
180
314
|
|
|
181
|
-
if (this.writeLast === this.commands.
|
|
315
|
+
if (this.writeLast === this.commands.GET_BATTERY_VOLTAGE) {
|
|
182
316
|
output = new DataView(value.buffer, 2).getUint32(0, true).toString()
|
|
183
|
-
} else if (this.writeLast === this.commands.
|
|
184
|
-
output = new TextDecoder().decode(
|
|
185
|
-
} else if (this.writeLast === this.commands.
|
|
186
|
-
output = new TextDecoder().decode(
|
|
317
|
+
} else if (this.writeLast === this.commands.GET_FIRMWARE_VERSION) {
|
|
318
|
+
output = new TextDecoder().decode(payload)
|
|
319
|
+
} else if (this.writeLast === this.commands.GET_ERROR_INFORMATION) {
|
|
320
|
+
output = new TextDecoder().decode(payload)
|
|
321
|
+
} else if (this.writeLast === this.commands.GET_PROGRESSOR_ID) {
|
|
322
|
+
output = parseProgressorIdPayload(payload)
|
|
323
|
+
} else if (this.writeLast === this.commands.GET_CALIBRATION) {
|
|
324
|
+
output = parseCalibrationCurvePayload(payload)
|
|
325
|
+
} else {
|
|
326
|
+
// Unknown command response: return raw hex
|
|
327
|
+
output = toHex(payload)
|
|
187
328
|
}
|
|
188
329
|
this.writeCallback(output)
|
|
189
330
|
} else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
|
|
@@ -209,8 +350,15 @@ export class Progressor extends Device implements IProgressor {
|
|
|
209
350
|
* @returns {Promise<void>} A promise that resolves when the streaming operation is completed.
|
|
210
351
|
*/
|
|
211
352
|
stream = async (duration = 0): Promise<void> => {
|
|
212
|
-
// Reset download packets
|
|
353
|
+
// Reset download packets and session stats for fresh measurement
|
|
213
354
|
this.downloadPackets.length = 0
|
|
355
|
+
this.peak = Number.NEGATIVE_INFINITY
|
|
356
|
+
this.mean = 0
|
|
357
|
+
this.sum = 0
|
|
358
|
+
this.dataPointCount = 0
|
|
359
|
+
this.min = Number.POSITIVE_INFINITY
|
|
360
|
+
this.resetPacketTracking()
|
|
361
|
+
this.recentSampleTimestamps = []
|
|
214
362
|
// Start streaming data
|
|
215
363
|
await this.write("progressor", "tx", this.commands.START_WEIGHT_MEAS, duration)
|
|
216
364
|
// Stop streaming if duration is set
|
|
@@ -60,7 +60,6 @@ export class SmartBoardPro extends Device implements ISmartBoardPro {
|
|
|
60
60
|
*/
|
|
61
61
|
override handleNotifications = (value: DataView): void => {
|
|
62
62
|
if (value) {
|
|
63
|
-
// Update timestamp
|
|
64
63
|
this.updateTimestamp()
|
|
65
64
|
if (value.buffer) {
|
|
66
65
|
const length = value.byteLength / 2
|
|
@@ -70,49 +69,45 @@ export class SmartBoardPro extends Device implements ISmartBoardPro {
|
|
|
70
69
|
const offset = i * 2
|
|
71
70
|
if (offset + 1 < value.byteLength) {
|
|
72
71
|
const intValue = value.getInt16(offset, true)
|
|
73
|
-
// For debugging purposes
|
|
74
72
|
console.log(intValue)
|
|
75
|
-
|
|
76
73
|
dataArray.push(intValue)
|
|
77
74
|
}
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
if (dataArray.length === 0) return
|
|
81
78
|
|
|
79
|
+
this.currentSamplesPerPacket = dataArray.length
|
|
80
|
+
this.recordPacketReceived()
|
|
82
81
|
const receivedTime = Date.now()
|
|
83
82
|
|
|
84
|
-
// Process each data point
|
|
85
83
|
for (const receivedData of dataArray) {
|
|
86
84
|
// Skip invalid values
|
|
87
85
|
if (!Number.isFinite(receivedData)) continue
|
|
88
86
|
|
|
89
87
|
const numericData = receivedData - this.applyTare(receivedData)
|
|
90
88
|
|
|
91
|
-
|
|
92
|
-
this.downloadPackets.push({
|
|
93
|
-
received: receivedTime,
|
|
94
|
-
sampleNum: this.dataPointCount,
|
|
95
|
-
battRaw: 0,
|
|
96
|
-
samples: [numericData],
|
|
97
|
-
masses: [numericData],
|
|
98
|
-
})
|
|
89
|
+
const currentMassTotal = Math.max(-1000, numericData)
|
|
99
90
|
|
|
100
|
-
// Update
|
|
91
|
+
// Update session stats before building packet
|
|
101
92
|
this.peak = Math.max(this.peak, numericData)
|
|
102
|
-
|
|
103
|
-
// Update running sum and count
|
|
104
|
-
const currentMassTotal = Math.max(-1000, numericData)
|
|
93
|
+
this.min = Math.min(this.min, Math.max(-1000, numericData))
|
|
105
94
|
this.sum += currentMassTotal
|
|
106
95
|
this.dataPointCount++
|
|
107
|
-
|
|
108
|
-
// Calculate the average dynamically
|
|
109
96
|
this.mean = this.sum / this.dataPointCount
|
|
110
97
|
|
|
98
|
+
// Add data to downloadable Array
|
|
99
|
+
this.downloadPackets.push(
|
|
100
|
+
this.buildDownloadPacket(currentMassTotal, [numericData], {
|
|
101
|
+
timestamp: receivedTime,
|
|
102
|
+
sampleIndex: this.dataPointCount,
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
|
|
111
106
|
// Check if device is being used
|
|
112
107
|
this.activityCheck(numericData)
|
|
113
108
|
|
|
114
109
|
// Notify with weight data
|
|
115
|
-
this.notifyCallback(this.buildForceMeasurement(
|
|
110
|
+
this.notifyCallback(this.buildForceMeasurement(currentMassTotal))
|
|
116
111
|
}
|
|
117
112
|
}
|
|
118
113
|
}
|
|
@@ -99,7 +99,8 @@ export class WHC06 extends Device implements IWHC06 {
|
|
|
99
99
|
this.bluetooth.addEventListener("advertisementreceived", (event) => {
|
|
100
100
|
const data = event.manufacturerData.get(WHC06.manufacturerId)
|
|
101
101
|
if (data) {
|
|
102
|
-
|
|
102
|
+
this.currentSamplesPerPacket = 1
|
|
103
|
+
this.recordPacketReceived()
|
|
103
104
|
const weight = (data.getUint8(WHC06.weightOffset) << 8) | data.getUint8(WHC06.weightOffset + 1)
|
|
104
105
|
// const stable = (data.getUint8(STABLE_OFFSET) & 0xf0) >> 4
|
|
105
106
|
// const unit = data.getUint8(STABLE_OFFSET) & 0x0f
|
|
@@ -107,32 +108,28 @@ export class WHC06 extends Device implements IWHC06 {
|
|
|
107
108
|
const receivedData = weight / 100
|
|
108
109
|
|
|
109
110
|
const numericData = receivedData - this.applyTare(receivedData) * -1
|
|
111
|
+
const currentMassTotal = Math.max(-1000, numericData)
|
|
110
112
|
|
|
111
|
-
//
|
|
112
|
-
this.downloadPackets.push({
|
|
113
|
-
received: receivedTime,
|
|
114
|
-
sampleNum: this.dataPointCount,
|
|
115
|
-
battRaw: 0,
|
|
116
|
-
samples: [numericData],
|
|
117
|
-
masses: [numericData],
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
// Update peak
|
|
113
|
+
// Update session stats before building packet
|
|
121
114
|
this.peak = Math.max(this.peak, numericData)
|
|
122
|
-
|
|
123
|
-
// Update running sum and count
|
|
124
|
-
const currentMassTotal = Math.max(-1000, numericData)
|
|
115
|
+
this.min = Math.min(this.min, Math.max(-1000, numericData))
|
|
125
116
|
this.sum += currentMassTotal
|
|
126
117
|
this.dataPointCount++
|
|
127
|
-
|
|
128
|
-
// Calculate the average dynamically
|
|
129
118
|
this.mean = this.sum / this.dataPointCount
|
|
130
119
|
|
|
120
|
+
// Add data to downloadable Array
|
|
121
|
+
this.downloadPackets.push(
|
|
122
|
+
this.buildDownloadPacket(currentMassTotal, [numericData], {
|
|
123
|
+
timestamp: receivedTime,
|
|
124
|
+
sampleIndex: this.dataPointCount,
|
|
125
|
+
}),
|
|
126
|
+
)
|
|
127
|
+
|
|
131
128
|
// Check if device is being used
|
|
132
129
|
this.activityCheck(numericData)
|
|
133
130
|
|
|
134
131
|
// Notify with weight data
|
|
135
|
-
this.notifyCallback(this.buildForceMeasurement(
|
|
132
|
+
this.notifyCallback(this.buildForceMeasurement(currentMassTotal))
|
|
136
133
|
}
|
|
137
134
|
// Reset "still advertising" counter
|
|
138
135
|
this.resetAdvertisementTimeout()
|