@hangtime/grip-connect 0.9.0 → 0.10.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.
- package/README.md +24 -15
- package/dist/cjs/index.d.ts +3 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/interfaces/callback.interface.d.ts +37 -31
- package/dist/cjs/interfaces/callback.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device/kilterboard.interface.d.ts +3 -2
- package/dist/cjs/interfaces/device/kilterboard.interface.d.ts.map +1 -1
- package/dist/cjs/interfaces/device.interface.d.ts +4 -2
- package/dist/cjs/interfaces/device.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 +4 -8
- 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 +5 -9
- package/dist/cjs/models/device/entralpi.model.js.map +1 -1
- package/dist/cjs/models/device/forceboard.model.d.ts +1 -0
- package/dist/cjs/models/device/forceboard.model.d.ts.map +1 -1
- package/dist/cjs/models/device/forceboard.model.js +10 -15
- package/dist/cjs/models/device/forceboard.model.js.map +1 -1
- package/dist/cjs/models/device/kilterboard.model.d.ts +5 -3
- package/dist/cjs/models/device/kilterboard.model.d.ts.map +1 -1
- package/dist/cjs/models/device/kilterboard.model.js +52 -16
- package/dist/cjs/models/device/kilterboard.model.js.map +1 -1
- package/dist/cjs/models/device/motherboard.model.d.ts +7 -0
- package/dist/cjs/models/device/motherboard.model.d.ts.map +1 -1
- package/dist/cjs/models/device/motherboard.model.js +28 -15
- package/dist/cjs/models/device/motherboard.model.js.map +1 -1
- package/dist/cjs/models/device/pb-700bt.model.d.ts +63 -0
- package/dist/cjs/models/device/pb-700bt.model.d.ts.map +1 -0
- package/dist/cjs/models/device/pb-700bt.model.js +247 -0
- package/dist/cjs/models/device/pb-700bt.model.js.map +1 -0
- package/dist/cjs/models/device/progressor.model.d.ts.map +1 -1
- package/dist/cjs/models/device/progressor.model.js +4 -8
- package/dist/cjs/models/device/progressor.model.js.map +1 -1
- package/dist/cjs/models/device/smartboard-pro.model.d.ts +8 -0
- package/dist/cjs/models/device/smartboard-pro.model.d.ts.map +1 -1
- package/dist/cjs/models/device/smartboard-pro.model.js +62 -6
- 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 +5 -9
- package/dist/cjs/models/device/wh-c06.model.js.map +1 -1
- package/dist/cjs/models/device.model.d.ts +81 -13
- package/dist/cjs/models/device.model.d.ts.map +1 -1
- package/dist/cjs/models/device.model.js +138 -6
- package/dist/cjs/models/device.model.js.map +1 -1
- package/dist/cjs/models/index.d.ts +2 -1
- package/dist/cjs/models/index.d.ts.map +1 -1
- package/dist/cjs/models/index.js +4 -1
- package/dist/cjs/models/index.js.map +1 -1
- package/dist/cjs/utils.d.ts +16 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +54 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/callback.interface.d.ts +37 -31
- package/dist/interfaces/callback.interface.d.ts.map +1 -1
- package/dist/interfaces/device/kilterboard.interface.d.ts +3 -2
- package/dist/interfaces/device/kilterboard.interface.d.ts.map +1 -1
- package/dist/interfaces/device.interface.d.ts +4 -2
- package/dist/interfaces/device.interface.d.ts.map +1 -1
- package/dist/models/device/climbro.model.d.ts.map +1 -1
- package/dist/models/device/climbro.model.js +4 -8
- 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 +5 -9
- package/dist/models/device/entralpi.model.js.map +1 -1
- package/dist/models/device/forceboard.model.d.ts +1 -0
- package/dist/models/device/forceboard.model.d.ts.map +1 -1
- package/dist/models/device/forceboard.model.js +10 -15
- package/dist/models/device/forceboard.model.js.map +1 -1
- package/dist/models/device/kilterboard.model.d.ts +5 -3
- package/dist/models/device/kilterboard.model.d.ts.map +1 -1
- package/dist/models/device/kilterboard.model.js +52 -16
- package/dist/models/device/kilterboard.model.js.map +1 -1
- package/dist/models/device/motherboard.model.d.ts +7 -0
- package/dist/models/device/motherboard.model.d.ts.map +1 -1
- package/dist/models/device/motherboard.model.js +28 -15
- package/dist/models/device/motherboard.model.js.map +1 -1
- package/dist/models/device/pb-700bt.model.d.ts +63 -0
- package/dist/models/device/pb-700bt.model.d.ts.map +1 -0
- package/dist/models/device/pb-700bt.model.js +243 -0
- package/dist/models/device/pb-700bt.model.js.map +1 -0
- package/dist/models/device/progressor.model.d.ts.map +1 -1
- package/dist/models/device/progressor.model.js +4 -8
- package/dist/models/device/progressor.model.js.map +1 -1
- package/dist/models/device/smartboard-pro.model.d.ts +8 -0
- package/dist/models/device/smartboard-pro.model.d.ts.map +1 -1
- package/dist/models/device/smartboard-pro.model.js +62 -6
- 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 +5 -9
- package/dist/models/device/wh-c06.model.js.map +1 -1
- package/dist/models/device.model.d.ts +81 -13
- package/dist/models/device.model.d.ts.map +1 -1
- package/dist/models/device.model.js +160 -15
- package/dist/models/device.model.js.map +1 -1
- package/dist/models/index.d.ts +2 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +2 -1
- package/dist/models/index.js.map +1 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/utils.d.ts +16 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +50 -0
- package/dist/utils.js.map +1 -0
- package/package.json +4 -1
- package/src/index.ts +13 -0
- package/src/interfaces/callback.interface.ts +41 -31
- package/src/interfaces/device/kilterboard.interface.ts +2 -2
- package/src/interfaces/device.interface.ts +4 -2
- package/src/models/device/climbro.model.ts +4 -8
- package/src/models/device/entralpi.model.ts +5 -9
- package/src/models/device/forceboard.model.ts +11 -16
- package/src/models/device/kilterboard.model.ts +56 -19
- package/src/models/device/motherboard.model.ts +32 -15
- package/src/models/device/pb-700bt.model.ts +263 -0
- package/src/models/device/progressor.model.ts +4 -8
- package/src/models/device/smartboard-pro.model.ts +73 -6
- package/src/models/device/wh-c06.model.ts +5 -9
- package/src/models/device.model.ts +191 -17
- package/src/models/index.ts +3 -1
- package/src/utils.ts +57 -0
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { BaseModel } from "./../models/base.model.js"
|
|
2
2
|
import type { IDevice, Service } from "../interfaces/device.interface.js"
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ActiveCallback,
|
|
5
|
+
ForceMeasurement,
|
|
6
|
+
ForceUnit,
|
|
7
|
+
NotifyCallback,
|
|
8
|
+
WriteCallback,
|
|
9
|
+
} from "../interfaces/callback.interface.js"
|
|
4
10
|
import type { DownloadPacket } from "../interfaces/download.interface.js"
|
|
5
11
|
import type { Commands } from "../interfaces/command.interface.js"
|
|
12
|
+
import { convertForce, convertForceMeasurement } from "../utils.js"
|
|
6
13
|
|
|
7
14
|
export abstract class Device extends BaseModel implements IDevice {
|
|
8
15
|
/**
|
|
@@ -67,26 +74,62 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
77
|
+
* Highest instantaneous force (peak) recorded in the session; may be negative.
|
|
78
|
+
* Initialized to Number.NEGATIVE_INFINITY so the first sample sets the peak.
|
|
79
|
+
* @type {number}
|
|
72
80
|
* @protected
|
|
73
81
|
*/
|
|
74
|
-
protected
|
|
82
|
+
protected peak: number
|
|
75
83
|
|
|
76
84
|
/**
|
|
77
|
-
*
|
|
78
|
-
* @type {
|
|
85
|
+
* Mean (average) force over the session, initialized to 0.
|
|
86
|
+
* @type {number}
|
|
79
87
|
* @protected
|
|
80
88
|
*/
|
|
81
|
-
protected
|
|
89
|
+
protected mean: number
|
|
82
90
|
|
|
83
91
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
92
|
+
* Display unit for force measurements (output unit for notify callbacks).
|
|
93
|
+
* @type {ForceUnit}
|
|
94
|
+
* @protected
|
|
95
|
+
*/
|
|
96
|
+
protected unit: ForceUnit
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Unit of the values streamed by the device (kg for most devices, lbs for ForceBoard).
|
|
100
|
+
* @type {ForceUnit}
|
|
101
|
+
* @protected
|
|
102
|
+
*/
|
|
103
|
+
protected streamUnit: ForceUnit = "kg"
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Optional sampling rate in Hz when known or calculated from notification timestamps.
|
|
107
|
+
* @type {number | undefined}
|
|
108
|
+
* @protected
|
|
109
|
+
*/
|
|
110
|
+
protected samplingRateHz?: number
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Start time of the current rate measurement interval.
|
|
114
|
+
* @type {number}
|
|
115
|
+
* @private
|
|
116
|
+
*/
|
|
117
|
+
private rateIntervalStart = 0
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Number of samples in the current rate measurement interval.
|
|
121
|
+
* @type {number}
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
private rateIntervalSamples = 0
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Running sum of force values for the session.
|
|
128
|
+
* Used to calculate mean (average) force.
|
|
86
129
|
* @type {number}
|
|
87
130
|
* @protected
|
|
88
131
|
*/
|
|
89
|
-
protected
|
|
132
|
+
protected sum: number
|
|
90
133
|
|
|
91
134
|
/**
|
|
92
135
|
* Number of data points received from the device.
|
|
@@ -135,13 +178,13 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
135
178
|
private tareDuration = 5000
|
|
136
179
|
|
|
137
180
|
/**
|
|
138
|
-
* Optional callback for handling
|
|
181
|
+
* Optional callback for handling mass/force data notifications.
|
|
139
182
|
* @callback NotifyCallback
|
|
140
|
-
* @param {
|
|
183
|
+
* @param {ForceMeasurement} data - The force measurement passed to the callback.
|
|
141
184
|
* @type {NotifyCallback | undefined}
|
|
142
185
|
* @protected
|
|
143
186
|
*/
|
|
144
|
-
protected notifyCallback: NotifyCallback = (data:
|
|
187
|
+
protected notifyCallback: NotifyCallback = (data: ForceMeasurement) => console.log(data)
|
|
145
188
|
|
|
146
189
|
/**
|
|
147
190
|
* Optional callback for handling write operations.
|
|
@@ -189,15 +232,121 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
189
232
|
this.bluetooth = device.bluetooth
|
|
190
233
|
}
|
|
191
234
|
|
|
192
|
-
this.
|
|
193
|
-
this.
|
|
194
|
-
this.
|
|
235
|
+
this.peak = Number.NEGATIVE_INFINITY
|
|
236
|
+
this.mean = 0
|
|
237
|
+
this.sum = 0
|
|
195
238
|
this.dataPointCount = 0
|
|
239
|
+
this.unit = "kg"
|
|
240
|
+
|
|
241
|
+
// Reset sampling rate calculation state
|
|
242
|
+
this.rateIntervalStart = 0
|
|
243
|
+
this.rateIntervalSamples = 0
|
|
196
244
|
|
|
197
245
|
this.createdAt = new Date()
|
|
198
246
|
this.updatedAt = new Date()
|
|
199
247
|
}
|
|
200
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Builds a ForceMeasurement for a single zone (e.g. left/center/right).
|
|
251
|
+
* With one argument, current/peak/mean are all set to that value.
|
|
252
|
+
* With three arguments, uses the given current, peak, and mean for the zone.
|
|
253
|
+
* @param valueOrCurrent - Force value, or current force for this zone
|
|
254
|
+
* @param peak - Optional peak for this zone (required if mean is provided)
|
|
255
|
+
* @param mean - Optional mean for this zone
|
|
256
|
+
* @returns ForceMeasurement (no nested distribution)
|
|
257
|
+
* @protected
|
|
258
|
+
*/
|
|
259
|
+
protected buildZoneMeasurement(valueOrCurrent: number, peak?: number, mean?: number): ForceMeasurement {
|
|
260
|
+
const useFullStats = peak !== undefined && mean !== undefined
|
|
261
|
+
const current = valueOrCurrent
|
|
262
|
+
const zonePeak = useFullStats ? (peak === 0 && current < 0 ? current : peak) : valueOrCurrent
|
|
263
|
+
const zoneMean = useFullStats ? mean : valueOrCurrent
|
|
264
|
+
const zone: ForceMeasurement = {
|
|
265
|
+
unit: this.unit,
|
|
266
|
+
timestamp: Date.now(),
|
|
267
|
+
current,
|
|
268
|
+
peak: zonePeak,
|
|
269
|
+
mean: zoneMean,
|
|
270
|
+
}
|
|
271
|
+
if (this.samplingRateHz !== undefined) {
|
|
272
|
+
zone.samplingRateHz = this.samplingRateHz
|
|
273
|
+
}
|
|
274
|
+
return zone
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Interval duration (ms) for sampling rate calculation.
|
|
279
|
+
* @private
|
|
280
|
+
* @readonly
|
|
281
|
+
*/
|
|
282
|
+
private static readonly RATE_INTERVAL_MS = 1000
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Calculates sampling rate: samples per second.
|
|
286
|
+
* Uses fixed intervals to avoid sliding window edge effects.
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
289
|
+
private updateSamplingRate(): void {
|
|
290
|
+
const now = Date.now()
|
|
291
|
+
|
|
292
|
+
if (this.rateIntervalStart === 0) {
|
|
293
|
+
this.rateIntervalStart = now
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.rateIntervalSamples++
|
|
297
|
+
|
|
298
|
+
const elapsed = now - this.rateIntervalStart
|
|
299
|
+
if (elapsed >= Device.RATE_INTERVAL_MS) {
|
|
300
|
+
this.samplingRateHz = Math.round((this.rateIntervalSamples / elapsed) * 1000)
|
|
301
|
+
this.rateIntervalStart = now
|
|
302
|
+
this.rateIntervalSamples = 0
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Builds a ForceMeasurement payload with unit and timestamp for notify callbacks.
|
|
308
|
+
* @param current - Current force at this sample
|
|
309
|
+
* @param distribution - Optional zone distribution: numbers (converted via buildZoneMeasurement) or full ForceMeasurement per zone
|
|
310
|
+
* @returns ForceMeasurement
|
|
311
|
+
* @protected
|
|
312
|
+
*/
|
|
313
|
+
protected buildForceMeasurement(
|
|
314
|
+
current: number,
|
|
315
|
+
distribution?: {
|
|
316
|
+
left?: ForceMeasurement
|
|
317
|
+
center?: ForceMeasurement
|
|
318
|
+
right?: ForceMeasurement
|
|
319
|
+
},
|
|
320
|
+
): ForceMeasurement {
|
|
321
|
+
this.updateSamplingRate()
|
|
322
|
+
const payload: ForceMeasurement = {
|
|
323
|
+
unit: this.unit,
|
|
324
|
+
timestamp: Date.now(),
|
|
325
|
+
current: convertForce(current, this.streamUnit, this.unit),
|
|
326
|
+
peak: convertForce(this.peak, this.streamUnit, this.unit),
|
|
327
|
+
mean: convertForce(this.mean, this.streamUnit, this.unit),
|
|
328
|
+
}
|
|
329
|
+
if (this.samplingRateHz !== undefined) {
|
|
330
|
+
payload.samplingRateHz = this.samplingRateHz
|
|
331
|
+
}
|
|
332
|
+
if (
|
|
333
|
+
distribution !== undefined &&
|
|
334
|
+
(distribution.left !== undefined || distribution.center !== undefined || distribution.right !== undefined)
|
|
335
|
+
) {
|
|
336
|
+
payload.distribution = {}
|
|
337
|
+
if (distribution.left !== undefined) {
|
|
338
|
+
payload.distribution.left = convertForceMeasurement(distribution.left, this.streamUnit, this.unit)
|
|
339
|
+
}
|
|
340
|
+
if (distribution.center !== undefined) {
|
|
341
|
+
payload.distribution.center = convertForceMeasurement(distribution.center, this.streamUnit, this.unit)
|
|
342
|
+
}
|
|
343
|
+
if (distribution.right !== undefined) {
|
|
344
|
+
payload.distribution.right = convertForceMeasurement(distribution.right, this.streamUnit, this.unit)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return payload
|
|
348
|
+
}
|
|
349
|
+
|
|
201
350
|
/**
|
|
202
351
|
* Sets the callback function to be called when the activity status changes,
|
|
203
352
|
* and optionally sets the configuration for threshold and duration.
|
|
@@ -276,6 +425,28 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
276
425
|
|
|
277
426
|
const bluetooth = await this.getBluetooth()
|
|
278
427
|
|
|
428
|
+
// Experiment: Reconnect to known devices, enable these Chrome flags:
|
|
429
|
+
// - chrome://flags/#enable-experimental-web-platform-features → enables getDevices() API
|
|
430
|
+
// - chrome://flags/#enable-web-bluetooth-new-permissions-backend → ensures it returns all permitted devices, not just connected ones
|
|
431
|
+
// let reconnectDevice: BluetoothDevice | undefined
|
|
432
|
+
// if (typeof bluetooth.getDevices === "function") {
|
|
433
|
+
// const devices: BluetoothDevice[] = await bluetooth.getDevices()
|
|
434
|
+
// if (devices.length > 0 && this.filters.length > 0) {
|
|
435
|
+
// reconnectDevice = devices.find((device) => {
|
|
436
|
+
// if (!device.name) return false
|
|
437
|
+
// const d = device
|
|
438
|
+
// return this.filters.some(
|
|
439
|
+
// (f) => (f.name && d.name === f.name) || (f.namePrefix && d.name?.startsWith(f.namePrefix)),
|
|
440
|
+
// )
|
|
441
|
+
// })
|
|
442
|
+
// }
|
|
443
|
+
// if (reconnectDevice) {
|
|
444
|
+
// this.bluetooth = reconnectDevice
|
|
445
|
+
// // It's currently impossible to call this.bluetooth.gatt.connect() here.
|
|
446
|
+
// // After restarting the Browser, it will always give: "Bluetooth Device is no longer in range."
|
|
447
|
+
// }
|
|
448
|
+
// }
|
|
449
|
+
|
|
279
450
|
this.bluetooth = await bluetooth.requestDevice({
|
|
280
451
|
filters: this.filters,
|
|
281
452
|
optionalServices: deviceServices,
|
|
@@ -540,6 +711,7 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
540
711
|
/**
|
|
541
712
|
* Sets the callback function to be called when notifications are received.
|
|
542
713
|
* @param {NotifyCallback} callback - The callback function to be set.
|
|
714
|
+
* @param {ForceUnit} [unit="kg"] - Optional display unit for force values in the callback payload.
|
|
543
715
|
* @returns {void}
|
|
544
716
|
* @public
|
|
545
717
|
*
|
|
@@ -547,8 +719,10 @@ export abstract class Device extends BaseModel implements IDevice {
|
|
|
547
719
|
* device.notify((data) => {
|
|
548
720
|
* console.log('Received notification:', data);
|
|
549
721
|
* });
|
|
722
|
+
* device.notify((data) => { ... }, 'lbs');
|
|
550
723
|
*/
|
|
551
|
-
notify = (callback: NotifyCallback): void => {
|
|
724
|
+
notify = (callback: NotifyCallback, unit?: ForceUnit): void => {
|
|
725
|
+
this.unit = unit ?? "kg"
|
|
552
726
|
this.notifyCallback = callback
|
|
553
727
|
}
|
|
554
728
|
|
package/src/models/index.ts
CHANGED
|
@@ -4,12 +4,14 @@ export { Entralpi } from "./device/entralpi.model.js"
|
|
|
4
4
|
|
|
5
5
|
export { ForceBoard } from "./device/forceboard.model.js"
|
|
6
6
|
|
|
7
|
-
export { KilterBoard } from "./device/kilterboard.model.js"
|
|
7
|
+
export { KilterBoard, KilterBoardPlacementRoles } from "./device/kilterboard.model.js"
|
|
8
8
|
|
|
9
9
|
export { Motherboard } from "./device/motherboard.model.js"
|
|
10
10
|
|
|
11
11
|
export { mySmartBoard } from "./device/mysmartboard.model.js"
|
|
12
12
|
|
|
13
|
+
export { PB700BT } from "./device/pb-700bt.model.js"
|
|
14
|
+
|
|
13
15
|
export { Progressor } from "./device/progressor.model.js"
|
|
14
16
|
|
|
15
17
|
export { SmartBoardPro } from "./device/smartboard-pro.model.js"
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ForceMeasurement, ForceUnit } from "./interfaces/callback.interface.js"
|
|
2
|
+
|
|
3
|
+
/** 1 kg = this many lbs (force-equivalent) */
|
|
4
|
+
const KG_TO_LBS = 2.20462262185
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Converts a force value between kg and lbs.
|
|
8
|
+
* @param value - The numeric force value in the source unit.
|
|
9
|
+
* @param from - The unit of the input value.
|
|
10
|
+
* @param to - The unit for the output value.
|
|
11
|
+
* @returns The force value in the target unit.
|
|
12
|
+
*/
|
|
13
|
+
export function convertForce(value: number, from: ForceUnit, to: ForceUnit): number {
|
|
14
|
+
if (from === to) return value
|
|
15
|
+
return from === "kg" ? value * KG_TO_LBS : value / KG_TO_LBS
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Converts all numeric force fields in a ForceMeasurement from one unit to another.
|
|
20
|
+
* Recurses one level into distribution.left / center / right.
|
|
21
|
+
* Used internally by the device model when building notify payloads.
|
|
22
|
+
*/
|
|
23
|
+
export function convertForceMeasurement(
|
|
24
|
+
measurement: ForceMeasurement,
|
|
25
|
+
from: ForceUnit,
|
|
26
|
+
to: ForceUnit,
|
|
27
|
+
): ForceMeasurement {
|
|
28
|
+
if (from === to) return measurement
|
|
29
|
+
const out: ForceMeasurement = {
|
|
30
|
+
unit: to,
|
|
31
|
+
timestamp: measurement.timestamp,
|
|
32
|
+
current: convertForce(measurement.current, from, to),
|
|
33
|
+
peak: convertForce(measurement.peak, from, to),
|
|
34
|
+
mean: convertForce(measurement.mean, from, to),
|
|
35
|
+
}
|
|
36
|
+
if (measurement.samplingRateHz !== undefined) {
|
|
37
|
+
out.samplingRateHz = measurement.samplingRateHz
|
|
38
|
+
}
|
|
39
|
+
if (
|
|
40
|
+
measurement.distribution &&
|
|
41
|
+
(measurement.distribution.left !== undefined ||
|
|
42
|
+
measurement.distribution.center !== undefined ||
|
|
43
|
+
measurement.distribution.right !== undefined)
|
|
44
|
+
) {
|
|
45
|
+
out.distribution = {}
|
|
46
|
+
if (measurement.distribution.left !== undefined) {
|
|
47
|
+
out.distribution.left = convertForceMeasurement(measurement.distribution.left, from, to)
|
|
48
|
+
}
|
|
49
|
+
if (measurement.distribution.center !== undefined) {
|
|
50
|
+
out.distribution.center = convertForceMeasurement(measurement.distribution.center, from, to)
|
|
51
|
+
}
|
|
52
|
+
if (measurement.distribution.right !== undefined) {
|
|
53
|
+
out.distribution.right = convertForceMeasurement(measurement.distribution.right, from, to)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return out
|
|
57
|
+
}
|