@hangtime/grip-connect 0.4.2 → 0.5.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 +49 -40
- package/dist/characteristic.d.ts +1 -1
- package/dist/commands/forceboard.d.ts +6 -0
- package/dist/commands/forceboard.js +5 -0
- package/dist/commands/index.d.ts +3 -2
- package/dist/commands/index.js +3 -2
- package/dist/index.d.ts +1 -13
- package/dist/index.js +3 -23
- package/dist/interfaces/base.interface.d.ts +5 -0
- package/dist/interfaces/device/climbro.interface.d.ts +3 -0
- package/dist/interfaces/device/climbro.interface.js +1 -0
- package/dist/interfaces/device/entralpi.interface.d.ts +3 -0
- package/dist/interfaces/device/entralpi.interface.js +1 -0
- package/dist/interfaces/device/forceboard.interface.d.ts +3 -0
- package/dist/interfaces/device/forceboard.interface.js +1 -0
- package/dist/interfaces/device/kilterboard.interface.d.ts +3 -0
- package/dist/interfaces/device/kilterboard.interface.js +1 -0
- package/dist/interfaces/device/motherboard.interface.d.ts +10 -0
- package/dist/interfaces/device/motherboard.interface.js +1 -0
- package/dist/interfaces/device/mysmartboard.interface.d.ts +3 -0
- package/dist/interfaces/device/mysmartboard.interface.js +1 -0
- package/dist/interfaces/device/progressor.interface.d.ts +3 -0
- package/dist/interfaces/device/progressor.interface.js +1 -0
- package/dist/interfaces/device/wh-c06.interface.d.ts +3 -0
- package/dist/interfaces/device/wh-c06.interface.js +1 -0
- package/dist/interfaces/device.interface.d.ts +76 -0
- package/dist/interfaces/device.interface.js +1 -0
- package/dist/is-device.d.ts +19 -13
- package/dist/is-device.js +19 -13
- package/dist/models/base.model.d.ts +7 -0
- package/dist/models/base.model.js +10 -0
- package/dist/models/device/climbro.model.d.ts +9 -0
- package/dist/models/device/climbro.model.js +13 -0
- package/dist/models/device/entralpi.model.d.ts +43 -0
- package/dist/models/device/entralpi.model.js +246 -0
- package/dist/models/device/forceboard.model.d.ts +23 -0
- package/dist/models/device/forceboard.model.js +201 -0
- package/dist/models/device/kilterboard.model.d.ts +85 -0
- package/dist/models/device/kilterboard.model.js +213 -0
- package/dist/models/device/motherboard.model.d.ts +76 -0
- package/dist/models/device/motherboard.model.js +391 -0
- package/dist/models/device/mysmartboard.model.d.ts +9 -0
- package/dist/models/device/mysmartboard.model.js +13 -0
- package/dist/models/device/progressor.model.d.ts +32 -0
- package/dist/models/device/progressor.model.js +177 -0
- package/dist/models/device/wh-c06.model.d.ts +15 -0
- package/dist/models/device/wh-c06.model.js +97 -0
- package/dist/models/device.model.d.ts +45 -0
- package/dist/models/device.model.js +140 -0
- package/dist/models/index.d.ts +8 -0
- package/dist/models/index.js +8 -0
- package/dist/notify.js +1 -1
- package/dist/read.d.ts +1 -1
- package/dist/read.js +1 -2
- package/dist/stop.d.ts +1 -1
- package/dist/stop.js +1 -2
- package/dist/write.d.ts +1 -1
- package/dist/write.js +1 -2
- package/package.json +4 -2
- package/src/characteristic.ts +1 -1
- package/src/commands/forceboard.ts +6 -0
- package/src/commands/index.ts +4 -2
- package/src/index.ts +14 -27
- package/src/interfaces/base.interface.ts +5 -0
- package/src/interfaces/device/climbro.interface.ts +4 -0
- package/src/interfaces/device/entralpi.interface.ts +4 -0
- package/src/interfaces/device/forceboard.interface.ts +4 -0
- package/src/interfaces/device/kilterboard.interface.ts +4 -0
- package/src/interfaces/device/motherboard.interface.ts +11 -0
- package/src/interfaces/device/mysmartboard.interface.ts +4 -0
- package/src/interfaces/device/progressor.interface.ts +4 -0
- package/src/interfaces/device/wh-c06.interface.ts +4 -0
- package/src/interfaces/device.interface.ts +85 -0
- package/src/is-device.ts +23 -16
- package/src/models/base.model.ts +16 -0
- package/src/models/device/climbro.model.ts +15 -0
- package/src/models/device/entralpi.model.ts +264 -0
- package/src/models/device/forceboard.model.ts +206 -0
- package/src/models/device/kilterboard.model.ts +229 -0
- package/src/models/device/motherboard.model.ts +430 -0
- package/src/models/device/mysmartboard.model.ts +15 -0
- package/src/models/device/progressor.model.ts +184 -0
- package/src/models/device/wh-c06.model.ts +118 -0
- package/src/models/device.model.ts +159 -0
- package/src/models/index.ts +15 -0
- package/src/notify.ts +1 -1
- package/src/read.ts +2 -3
- package/src/stop.ts +2 -3
- package/src/write.ts +2 -3
- package/dist/battery.d.ts +0 -10
- package/dist/battery.js +0 -34
- package/dist/calibration.d.ts +0 -7
- package/dist/calibration.js +0 -21
- package/dist/connect.d.ts +0 -8
- package/dist/connect.js +0 -163
- package/dist/data/entralpi.d.ts +0 -5
- package/dist/data/entralpi.js +0 -33
- package/dist/data/index.d.ts +0 -4
- package/dist/data/index.js +0 -4
- package/dist/data/motherboard.d.ts +0 -10
- package/dist/data/motherboard.js +0 -141
- package/dist/data/progressor.d.ts +0 -9
- package/dist/data/progressor.js +0 -78
- package/dist/data/wh-c06.d.ts +0 -5
- package/dist/data/wh-c06.js +0 -38
- package/dist/devices/climbro.d.ts +0 -6
- package/dist/devices/climbro.js +0 -8
- package/dist/devices/entralpi.d.ts +0 -5
- package/dist/devices/entralpi.js +0 -59
- package/dist/devices/index.d.ts +0 -7
- package/dist/devices/index.js +0 -7
- package/dist/devices/kilterboard.d.ts +0 -10
- package/dist/devices/kilterboard.js +0 -34
- package/dist/devices/motherboard.d.ts +0 -5
- package/dist/devices/motherboard.js +0 -81
- package/dist/devices/mysmartboard.d.ts +0 -6
- package/dist/devices/mysmartboard.js +0 -8
- package/dist/devices/progressor.d.ts +0 -5
- package/dist/devices/progressor.js +0 -37
- package/dist/devices/wh-c06.d.ts +0 -6
- package/dist/devices/wh-c06.js +0 -17
- package/dist/disconnect.d.ts +0 -8
- package/dist/disconnect.js +0 -14
- package/dist/firmware.d.ts +0 -10
- package/dist/firmware.js +0 -34
- package/dist/hardware.d.ts +0 -9
- package/dist/hardware.js +0 -22
- package/dist/is-connected.d.ts +0 -7
- package/dist/is-connected.js +0 -13
- package/dist/led.d.ts +0 -24
- package/dist/led.js +0 -195
- package/dist/manufacturer.d.ts +0 -9
- package/dist/manufacturer.js +0 -22
- package/dist/serial.d.ts +0 -9
- package/dist/serial.js +0 -27
- package/dist/stream.d.ts +0 -8
- package/dist/stream.js +0 -41
- package/dist/text.d.ts +0 -12
- package/dist/text.js +0 -30
- package/dist/types/devices.d.ts +0 -38
- package/src/battery.ts +0 -36
- package/src/calibration.ts +0 -23
- package/src/connect.ts +0 -187
- package/src/data/entralpi.ts +0 -41
- package/src/data/index.ts +0 -7
- package/src/data/motherboard.ts +0 -163
- package/src/data/progressor.ts +0 -79
- package/src/data/wh-c06.ts +0 -47
- package/src/devices/climbro.ts +0 -10
- package/src/devices/entralpi.ts +0 -61
- package/src/devices/index.ts +0 -13
- package/src/devices/kilterboard.ts +0 -37
- package/src/devices/motherboard.ts +0 -83
- package/src/devices/mysmartboard.ts +0 -10
- package/src/devices/progressor.ts +0 -38
- package/src/devices/wh-c06.ts +0 -19
- package/src/disconnect.ts +0 -16
- package/src/firmware.ts +0 -36
- package/src/hardware.ts +0 -24
- package/src/is-connected.ts +0 -15
- package/src/led.ts +0 -210
- package/src/manufacturer.ts +0 -24
- package/src/serial.ts +0 -29
- package/src/stream.ts +0 -43
- package/src/text.ts +0 -32
- package/src/types/devices.ts +0 -39
- /package/dist/{types/devices.js → interfaces/base.interface.js} +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Device } from "../device.model";
|
|
2
|
+
import type { ImySmartBoard } from "../../interfaces/device/mysmartboard.interface";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a mySmartBoard device
|
|
5
|
+
* TODO: Add services, do you own a mySmartBoard? Help us!
|
|
6
|
+
*/
|
|
7
|
+
export declare class mySmartBoard extends Device implements ImySmartBoard {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Device } from "../device.model";
|
|
2
|
+
/**
|
|
3
|
+
* Represents a mySmartBoard device
|
|
4
|
+
* TODO: Add services, do you own a mySmartBoard? Help us!
|
|
5
|
+
*/
|
|
6
|
+
export class mySmartBoard extends Device {
|
|
7
|
+
constructor() {
|
|
8
|
+
super({
|
|
9
|
+
filters: [{ name: "mySmartBoard" }],
|
|
10
|
+
services: [],
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Device } from "../device.model";
|
|
2
|
+
import type { IProgressor } from "../../interfaces/device/progressor.interface";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a Tindeq Progressor device
|
|
5
|
+
*/
|
|
6
|
+
export declare class Progressor extends Device implements IProgressor {
|
|
7
|
+
constructor();
|
|
8
|
+
/**
|
|
9
|
+
* Retrieves battery or voltage information from the device.
|
|
10
|
+
* @returns {Promise<string | undefined>} A Promise that resolves with the battery or voltage information,
|
|
11
|
+
*/
|
|
12
|
+
battery: () => Promise<string | undefined>;
|
|
13
|
+
/**
|
|
14
|
+
* Retrieves firmware version from the device.
|
|
15
|
+
* @returns {Promise<string>} A Promise that resolves with the firmware version,
|
|
16
|
+
*/
|
|
17
|
+
firmware: () => Promise<string | undefined>;
|
|
18
|
+
/**
|
|
19
|
+
* Handles data received from the Progressor device, processes weight measurements,
|
|
20
|
+
* and updates mass data including maximum and average values.
|
|
21
|
+
* It also handles command responses for retrieving device information.
|
|
22
|
+
*
|
|
23
|
+
* @param {Event} event - The notification event.
|
|
24
|
+
*/
|
|
25
|
+
handleNotifications: (event: Event) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Starts streaming data from the specified device.
|
|
28
|
+
* @param {number} [duration=0] - The duration of the stream in milliseconds. If set to 0, stream will continue indefinitely.
|
|
29
|
+
* @returns {Promise<void>} A promise that resolves when the streaming operation is completed.
|
|
30
|
+
*/
|
|
31
|
+
stream: (duration?: number) => Promise<void>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { Device } from "../device.model";
|
|
2
|
+
import { notifyCallback } from "../../notify";
|
|
3
|
+
import { applyTare } from "../../tare";
|
|
4
|
+
import { checkActivity } from "../../is-active";
|
|
5
|
+
import { ProgressorCommands, ProgressorResponses } from "../../commands/progressor";
|
|
6
|
+
import struct from "../../struct";
|
|
7
|
+
import { DownloadPackets, emptyDownloadPackets } from "../../download";
|
|
8
|
+
import { lastWrite, write, writeCallback } from "../../write";
|
|
9
|
+
import { stop } from "../../stop";
|
|
10
|
+
// Constants
|
|
11
|
+
let MASS_MAX = "0";
|
|
12
|
+
let MASS_AVERAGE = "0";
|
|
13
|
+
let MASS_TOTAL_SUM = 0;
|
|
14
|
+
let DATAPOINT_COUNT = 0;
|
|
15
|
+
/**
|
|
16
|
+
* Represents a Tindeq Progressor device
|
|
17
|
+
*/
|
|
18
|
+
export class Progressor extends Device {
|
|
19
|
+
constructor() {
|
|
20
|
+
super({
|
|
21
|
+
filters: [{ namePrefix: "Progressor" }],
|
|
22
|
+
services: [
|
|
23
|
+
{
|
|
24
|
+
name: "Progressor Service",
|
|
25
|
+
id: "progressor",
|
|
26
|
+
uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57",
|
|
27
|
+
characteristics: [
|
|
28
|
+
{
|
|
29
|
+
name: "Notify",
|
|
30
|
+
id: "rx",
|
|
31
|
+
uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "Write",
|
|
35
|
+
id: "tx",
|
|
36
|
+
uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "Nordic Device Firmware Update (DFU) Service",
|
|
42
|
+
id: "dfu",
|
|
43
|
+
uuid: "0000fe59-0000-1000-8000-00805f9b34fb",
|
|
44
|
+
characteristics: [
|
|
45
|
+
{
|
|
46
|
+
name: "Buttonless DFU",
|
|
47
|
+
id: "dfu",
|
|
48
|
+
uuid: "8ec90003-f315-4f60-9fb8-838830daea50",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Retrieves battery or voltage information from the device.
|
|
57
|
+
* @returns {Promise<string | undefined>} A Promise that resolves with the battery or voltage information,
|
|
58
|
+
*/
|
|
59
|
+
battery = async () => {
|
|
60
|
+
if (this.isConnected()) {
|
|
61
|
+
let response = undefined;
|
|
62
|
+
await write(this, "progressor", "tx", ProgressorCommands.GET_BATT_VLTG, 250, (data) => {
|
|
63
|
+
response = data;
|
|
64
|
+
});
|
|
65
|
+
return response;
|
|
66
|
+
}
|
|
67
|
+
// If device is not found, return undefined
|
|
68
|
+
return undefined;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Retrieves firmware version from the device.
|
|
72
|
+
* @returns {Promise<string>} A Promise that resolves with the firmware version,
|
|
73
|
+
*/
|
|
74
|
+
firmware = async () => {
|
|
75
|
+
// Check if the device is connected
|
|
76
|
+
if (this.isConnected()) {
|
|
77
|
+
// Read firmware version from the device
|
|
78
|
+
let response = undefined;
|
|
79
|
+
await write(this, "progressor", "tx", ProgressorCommands.GET_FW_VERSION, 250, (data) => {
|
|
80
|
+
response = data;
|
|
81
|
+
});
|
|
82
|
+
return response;
|
|
83
|
+
}
|
|
84
|
+
// If device is not found, return undefined
|
|
85
|
+
return undefined;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Handles data received from the Progressor device, processes weight measurements,
|
|
89
|
+
* and updates mass data including maximum and average values.
|
|
90
|
+
* It also handles command responses for retrieving device information.
|
|
91
|
+
*
|
|
92
|
+
* @param {Event} event - The notification event.
|
|
93
|
+
*/
|
|
94
|
+
handleNotifications = (event) => {
|
|
95
|
+
const characteristic = event.target;
|
|
96
|
+
const value = characteristic.value;
|
|
97
|
+
if (value) {
|
|
98
|
+
if (value.buffer) {
|
|
99
|
+
const buffer = value.buffer;
|
|
100
|
+
const data = new DataView(buffer);
|
|
101
|
+
const receivedTime = Date.now();
|
|
102
|
+
const [kind] = struct("<bb").unpack(data.buffer.slice(0, 2));
|
|
103
|
+
if (kind === ProgressorResponses.WEIGHT_MEASURE) {
|
|
104
|
+
const iterable = struct("<fi").iter_unpack(data.buffer.slice(2));
|
|
105
|
+
// eslint-disable-next-line prefer-const
|
|
106
|
+
for (let [weight, seconds] of iterable) {
|
|
107
|
+
if (typeof weight === "number" && !isNaN(weight) && typeof seconds === "number" && !isNaN(seconds)) {
|
|
108
|
+
// Add data to downloadable Array: sample and mass are the same
|
|
109
|
+
DownloadPackets.push({
|
|
110
|
+
received: receivedTime,
|
|
111
|
+
sampleNum: seconds,
|
|
112
|
+
battRaw: 0,
|
|
113
|
+
samples: [weight],
|
|
114
|
+
masses: [weight],
|
|
115
|
+
});
|
|
116
|
+
// Tare correction
|
|
117
|
+
weight -= applyTare(weight);
|
|
118
|
+
// Check for max weight
|
|
119
|
+
MASS_MAX = Math.max(Number(MASS_MAX), Number(weight)).toFixed(1);
|
|
120
|
+
// Update running sum and count
|
|
121
|
+
const currentMassTotal = Math.max(-1000, Number(weight));
|
|
122
|
+
MASS_TOTAL_SUM += currentMassTotal;
|
|
123
|
+
DATAPOINT_COUNT++;
|
|
124
|
+
// Calculate the average dynamically
|
|
125
|
+
MASS_AVERAGE = (MASS_TOTAL_SUM / DATAPOINT_COUNT).toFixed(1);
|
|
126
|
+
// Check if device is being used
|
|
127
|
+
checkActivity(weight);
|
|
128
|
+
notifyCallback({
|
|
129
|
+
massMax: MASS_MAX,
|
|
130
|
+
massAverage: MASS_AVERAGE,
|
|
131
|
+
massTotal: Math.max(-1000, weight).toFixed(1),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
|
|
137
|
+
if (!lastWrite)
|
|
138
|
+
return;
|
|
139
|
+
let value = "";
|
|
140
|
+
if (lastWrite === ProgressorCommands.GET_BATT_VLTG) {
|
|
141
|
+
value = new DataView(data.buffer, 2).getUint32(0, true).toString();
|
|
142
|
+
}
|
|
143
|
+
else if (lastWrite === ProgressorCommands.GET_FW_VERSION) {
|
|
144
|
+
value = new TextDecoder().decode(data.buffer.slice(2));
|
|
145
|
+
}
|
|
146
|
+
else if (lastWrite === ProgressorCommands.GET_ERR_INFO) {
|
|
147
|
+
value = new TextDecoder().decode(data.buffer.slice(2));
|
|
148
|
+
}
|
|
149
|
+
writeCallback(value);
|
|
150
|
+
}
|
|
151
|
+
else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
|
|
152
|
+
console.warn("⚠️ Low power detected. Please consider connecting to a power source.");
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw new Error(`Unknown message kind detected: ${kind}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* Starts streaming data from the specified device.
|
|
162
|
+
* @param {number} [duration=0] - The duration of the stream in milliseconds. If set to 0, stream will continue indefinitely.
|
|
163
|
+
* @returns {Promise<void>} A promise that resolves when the streaming operation is completed.
|
|
164
|
+
*/
|
|
165
|
+
stream = async (duration = 0) => {
|
|
166
|
+
if (this.isConnected()) {
|
|
167
|
+
// Reset download packets
|
|
168
|
+
emptyDownloadPackets();
|
|
169
|
+
// Start streaming data
|
|
170
|
+
await write(this, "progressor", "tx", ProgressorCommands.START_WEIGHT_MEAS, duration);
|
|
171
|
+
// Stop streaming if duration is set
|
|
172
|
+
if (duration !== 0) {
|
|
173
|
+
await stop(this);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Device } from "../device.model";
|
|
2
|
+
import type { IWHC06 } from "../../interfaces/device/wh-c06.interface";
|
|
3
|
+
/**
|
|
4
|
+
* Represents a Weiheng - WH-C06 (or MAT Muscle Meter) device
|
|
5
|
+
* Enable 'Experimental Web Platform features' Chrome Flags.
|
|
6
|
+
*/
|
|
7
|
+
export declare class WHC06 extends Device implements IWHC06 {
|
|
8
|
+
constructor();
|
|
9
|
+
/**
|
|
10
|
+
* Connects to a Bluetooth device.
|
|
11
|
+
* @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
|
|
12
|
+
* @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
|
|
13
|
+
*/
|
|
14
|
+
connect: (onSuccess?: () => void, onError?: (error: Error) => void) => Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Device } from "../device.model";
|
|
2
|
+
import { applyTare } from "../../tare";
|
|
3
|
+
import { checkActivity } from "../../is-active";
|
|
4
|
+
import { notifyCallback } from "../../notify";
|
|
5
|
+
// Constants
|
|
6
|
+
let MASS_MAX = "0";
|
|
7
|
+
let MASS_AVERAGE = "0";
|
|
8
|
+
let MASS_TOTAL_SUM = 0;
|
|
9
|
+
let DATAPOINT_COUNT = 0;
|
|
10
|
+
const WEIGHT_OFFSET = 10;
|
|
11
|
+
// const STABLE_OFFSET = 14
|
|
12
|
+
/**
|
|
13
|
+
* Represents a Weiheng - WH-C06 (or MAT Muscle Meter) device
|
|
14
|
+
* Enable 'Experimental Web Platform features' Chrome Flags.
|
|
15
|
+
*/
|
|
16
|
+
export class WHC06 extends Device {
|
|
17
|
+
constructor() {
|
|
18
|
+
super({
|
|
19
|
+
filters: [
|
|
20
|
+
{
|
|
21
|
+
// namePrefix: "IF_B7",
|
|
22
|
+
manufacturerData: [
|
|
23
|
+
{
|
|
24
|
+
companyIdentifier: 0x0100, // 256
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
services: [],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Connects to a Bluetooth device.
|
|
34
|
+
* @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
|
|
35
|
+
* @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
|
|
36
|
+
*/
|
|
37
|
+
connect = async (onSuccess = () => console.log("Connected successfully"), onError = (error) => console.error(error)) => {
|
|
38
|
+
try {
|
|
39
|
+
// Only data matching the optionalManufacturerData parameter to requestDevice is included in the advertisement event: https://github.com/WebBluetoothCG/web-bluetooth/issues/598
|
|
40
|
+
const optionalManufacturerData = this.filters.flatMap((filter) => filter.manufacturerData?.map((data) => data.companyIdentifier) || []);
|
|
41
|
+
this.bluetooth = await navigator.bluetooth.requestDevice({
|
|
42
|
+
filters: this.filters,
|
|
43
|
+
optionalManufacturerData,
|
|
44
|
+
});
|
|
45
|
+
if (!this.bluetooth.gatt) {
|
|
46
|
+
throw new Error("GATT is not available on this device");
|
|
47
|
+
}
|
|
48
|
+
// WH-C06
|
|
49
|
+
const MANUFACTURER_ID = 256; // 0x0100
|
|
50
|
+
this.bluetooth.addEventListener("advertisementreceived", (event) => {
|
|
51
|
+
const data = event.manufacturerData.get(MANUFACTURER_ID);
|
|
52
|
+
if (data) {
|
|
53
|
+
// Device has no services / characteristics
|
|
54
|
+
onSuccess();
|
|
55
|
+
// Handle recieved data
|
|
56
|
+
const weight = (data.getUint8(WEIGHT_OFFSET) << 8) | data.getUint8(WEIGHT_OFFSET + 1);
|
|
57
|
+
// const stable = (data.getUint8(STABLE_OFFSET) & 0xf0) >> 4
|
|
58
|
+
// const unit = data.getUint8(STABLE_OFFSET) & 0x0f
|
|
59
|
+
let numericData = weight / 100;
|
|
60
|
+
// Tare correction
|
|
61
|
+
numericData -= applyTare(numericData);
|
|
62
|
+
// Update MASS_MAX
|
|
63
|
+
MASS_MAX = Math.max(Number(MASS_MAX), numericData).toFixed(1);
|
|
64
|
+
// Update running sum and count
|
|
65
|
+
const currentMassTotal = Math.max(-1000, numericData);
|
|
66
|
+
MASS_TOTAL_SUM += currentMassTotal;
|
|
67
|
+
DATAPOINT_COUNT++;
|
|
68
|
+
// Calculate the average dynamically
|
|
69
|
+
MASS_AVERAGE = (MASS_TOTAL_SUM / DATAPOINT_COUNT).toFixed(1);
|
|
70
|
+
// Check if device is being used
|
|
71
|
+
checkActivity(numericData);
|
|
72
|
+
// Notify with weight data
|
|
73
|
+
notifyCallback({
|
|
74
|
+
massMax: MASS_MAX,
|
|
75
|
+
massAverage: MASS_AVERAGE,
|
|
76
|
+
massTotal: Math.max(-1000, numericData).toFixed(1),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// When the companyIdentifier is provided we want to get manufacturerData using watchAdvertisements.
|
|
81
|
+
if (optionalManufacturerData.length) {
|
|
82
|
+
// Receive events when the system receives an advertisement packet from a watched device.
|
|
83
|
+
// To use this function in Chrome: chrome://flags/#enable-experimental-web-platform-features has to be enabled.
|
|
84
|
+
// More info: https://chromestatus.com/feature/5180688812736512
|
|
85
|
+
if (typeof this.bluetooth.watchAdvertisements === "function") {
|
|
86
|
+
await this.bluetooth.watchAdvertisements();
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
throw new Error("watchAdvertisements isn't supported. For Chrome, enable it at chrome://flags/#enable-experimental-web-platform-features.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
onError(error);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { BaseModel } from "./../models/base.model";
|
|
2
|
+
import type { IDevice, Service } from "../interfaces/device.interface";
|
|
3
|
+
export declare class Device extends BaseModel implements IDevice {
|
|
4
|
+
filters: BluetoothLEScanFilter[];
|
|
5
|
+
services: Service[];
|
|
6
|
+
bluetooth?: BluetoothDevice | undefined;
|
|
7
|
+
constructor(device: Partial<IDevice>);
|
|
8
|
+
/**
|
|
9
|
+
* Handles the 'disconnected' event.
|
|
10
|
+
* @param {Event} event - The 'disconnected' event.
|
|
11
|
+
*/
|
|
12
|
+
onDisconnected: (event: Event) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Handles notifications received from a characteristic.
|
|
15
|
+
* @param {Event} event - The notification event.
|
|
16
|
+
*/
|
|
17
|
+
handleNotifications: (event: Event) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Handles the 'connected' event.
|
|
20
|
+
* @param {Function} onSuccess - Callback function to execute on successful connection.
|
|
21
|
+
*/
|
|
22
|
+
onConnected: (onSuccess: () => void) => Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Returns UUIDs of all services associated with the device.
|
|
25
|
+
* @returns {string[]} Array of service UUIDs.
|
|
26
|
+
*/
|
|
27
|
+
getAllServiceUUIDs: () => string[];
|
|
28
|
+
/**
|
|
29
|
+
* Connects to a Bluetooth device.
|
|
30
|
+
* @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
|
|
31
|
+
* @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
|
|
32
|
+
*/
|
|
33
|
+
connect: (onSuccess?: () => void, onError?: (error: Error) => void) => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Checks if a Bluetooth device is connected.
|
|
36
|
+
* @returns {boolean} A boolean indicating whether the device is connected.
|
|
37
|
+
*/
|
|
38
|
+
isConnected: () => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Disconnects the device if it is currently connected.
|
|
41
|
+
* - Checks if the device is connected via it's GATT server.
|
|
42
|
+
* - If the device is connected, it attempts to gracefully disconnect.
|
|
43
|
+
*/
|
|
44
|
+
disconnect: () => void;
|
|
45
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { BaseModel } from "./../models/base.model";
|
|
2
|
+
let server;
|
|
3
|
+
export class Device extends BaseModel {
|
|
4
|
+
filters;
|
|
5
|
+
services;
|
|
6
|
+
bluetooth;
|
|
7
|
+
constructor(device) {
|
|
8
|
+
super(device);
|
|
9
|
+
this.filters = device.filters || [];
|
|
10
|
+
this.services = device.services || [];
|
|
11
|
+
this.bluetooth = device.bluetooth;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Handles the 'disconnected' event.
|
|
15
|
+
* @param {Event} event - The 'disconnected' event.
|
|
16
|
+
*/
|
|
17
|
+
onDisconnected = (event) => {
|
|
18
|
+
this.bluetooth = undefined;
|
|
19
|
+
const device = event.target;
|
|
20
|
+
throw new Error(`Device ${device.name} is disconnected.`);
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Handles notifications received from a characteristic.
|
|
24
|
+
* @param {Event} event - The notification event.
|
|
25
|
+
*/
|
|
26
|
+
handleNotifications = (event) => {
|
|
27
|
+
const characteristic = event.target;
|
|
28
|
+
const value = characteristic.value;
|
|
29
|
+
if (value) {
|
|
30
|
+
if (value.buffer) {
|
|
31
|
+
const buffer = value.buffer;
|
|
32
|
+
console.log(new Uint8Array(buffer));
|
|
33
|
+
const rawData = new DataView(buffer);
|
|
34
|
+
console.log(rawData);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log(value);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Handles the 'connected' event.
|
|
43
|
+
* @param {Function} onSuccess - Callback function to execute on successful connection.
|
|
44
|
+
*/
|
|
45
|
+
onConnected = async (onSuccess) => {
|
|
46
|
+
// Connect to GATT server and set up characteristics
|
|
47
|
+
const services = await server.getPrimaryServices();
|
|
48
|
+
if (!services || services.length === 0) {
|
|
49
|
+
throw new Error("No services found");
|
|
50
|
+
}
|
|
51
|
+
for (const service of services) {
|
|
52
|
+
const matchingService = this.services.find((boardService) => boardService.uuid === service.uuid);
|
|
53
|
+
if (matchingService) {
|
|
54
|
+
// Android bug: Introduce a delay before getting characteristics
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
56
|
+
const characteristics = await service.getCharacteristics();
|
|
57
|
+
for (const characteristic of matchingService.characteristics) {
|
|
58
|
+
const matchingCharacteristic = characteristics.find((char) => char.uuid === characteristic.uuid);
|
|
59
|
+
if (matchingCharacteristic) {
|
|
60
|
+
const element = matchingService.characteristics.find((char) => char.uuid === matchingCharacteristic.uuid);
|
|
61
|
+
if (element) {
|
|
62
|
+
element.characteristic = matchingCharacteristic;
|
|
63
|
+
// notify
|
|
64
|
+
if (element.id === "rx") {
|
|
65
|
+
matchingCharacteristic.startNotifications();
|
|
66
|
+
matchingCharacteristic.addEventListener("characteristicvaluechanged", (event) => {
|
|
67
|
+
this.handleNotifications(event);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw new Error(`Characteristic ${characteristic.uuid} not found in service ${service.uuid}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Call the onSuccess callback after successful connection and setup
|
|
79
|
+
onSuccess();
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Returns UUIDs of all services associated with the device.
|
|
83
|
+
* @returns {string[]} Array of service UUIDs.
|
|
84
|
+
*/
|
|
85
|
+
getAllServiceUUIDs = () => {
|
|
86
|
+
return this.services.map((service) => service.uuid);
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Connects to a Bluetooth device.
|
|
90
|
+
* @param {Function} [onSuccess] - Optional callback function to execute on successful connection. Default logs success.
|
|
91
|
+
* @param {Function} [onError] - Optional callback function to execute on error. Default logs the error.
|
|
92
|
+
*/
|
|
93
|
+
connect = async (onSuccess = () => console.log("Connected successfully"), onError = (error) => console.error(error)) => {
|
|
94
|
+
try {
|
|
95
|
+
// Request device and set up connection
|
|
96
|
+
const deviceServices = this.getAllServiceUUIDs();
|
|
97
|
+
this.bluetooth = await navigator.bluetooth.requestDevice({
|
|
98
|
+
filters: this.filters,
|
|
99
|
+
optionalServices: deviceServices,
|
|
100
|
+
});
|
|
101
|
+
if (!this.bluetooth.gatt) {
|
|
102
|
+
throw new Error("GATT is not available on this device");
|
|
103
|
+
}
|
|
104
|
+
this.bluetooth.addEventListener("gattserverdisconnected", (event) => {
|
|
105
|
+
this.onDisconnected(event);
|
|
106
|
+
});
|
|
107
|
+
server = await this.bluetooth.gatt.connect();
|
|
108
|
+
if (server.connected) {
|
|
109
|
+
await this.onConnected(onSuccess);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
onError(error);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Checks if a Bluetooth device is connected.
|
|
118
|
+
* @returns {boolean} A boolean indicating whether the device is connected.
|
|
119
|
+
*/
|
|
120
|
+
isConnected = () => {
|
|
121
|
+
// Check if the device is defined and available
|
|
122
|
+
if (!this?.bluetooth) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
// Check if the device is connected
|
|
126
|
+
return !!this.bluetooth.gatt?.connected;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Disconnects the device if it is currently connected.
|
|
130
|
+
* - Checks if the device is connected via it's GATT server.
|
|
131
|
+
* - If the device is connected, it attempts to gracefully disconnect.
|
|
132
|
+
*/
|
|
133
|
+
disconnect = () => {
|
|
134
|
+
// Verify that the device is connected using the provided helper function
|
|
135
|
+
if (this.isConnected()) {
|
|
136
|
+
// Safely attempt to disconnect the device's GATT server, if available
|
|
137
|
+
this.bluetooth?.gatt?.disconnect();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Climbro } from "./device/climbro.model";
|
|
2
|
+
export { Entralpi } from "./device/entralpi.model";
|
|
3
|
+
export { ForceBoard } from "./device/forceboard.model";
|
|
4
|
+
export { KilterBoard } from "./device/kilterboard.model";
|
|
5
|
+
export { Motherboard } from "./device/motherboard.model";
|
|
6
|
+
export { mySmartBoard } from "./device/mysmartboard.model";
|
|
7
|
+
export { Progressor } from "./device/progressor.model";
|
|
8
|
+
export { WHC06 } from "./device/wh-c06.model";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Climbro } from "./device/climbro.model";
|
|
2
|
+
export { Entralpi } from "./device/entralpi.model";
|
|
3
|
+
export { ForceBoard } from "./device/forceboard.model";
|
|
4
|
+
export { KilterBoard } from "./device/kilterboard.model";
|
|
5
|
+
export { Motherboard } from "./device/motherboard.model";
|
|
6
|
+
export { mySmartBoard } from "./device/mysmartboard.model";
|
|
7
|
+
export { Progressor } from "./device/progressor.model";
|
|
8
|
+
export { WHC06 } from "./device/wh-c06.model";
|
package/dist/notify.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @callback NotifyCallback
|
|
4
4
|
* @param {massObject} data - The data passed to the callback.
|
|
5
5
|
*/
|
|
6
|
-
export let notifyCallback;
|
|
6
|
+
export let notifyCallback = (data) => console.log(data);
|
|
7
7
|
/**
|
|
8
8
|
* Sets the callback function to be called when notifications are received.
|
|
9
9
|
* @param {NotifyCallback} callback - The callback function to be set.
|
package/dist/read.d.ts
CHANGED
package/dist/read.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCharacteristic } from "./characteristic";
|
|
2
|
-
import { isConnected } from "./is-connected";
|
|
3
2
|
/**
|
|
4
3
|
* Reads the value of the specified characteristic from the device.
|
|
5
4
|
* @param {Device} board - The device to read from.
|
|
@@ -10,7 +9,7 @@ import { isConnected } from "./is-connected";
|
|
|
10
9
|
*/
|
|
11
10
|
export const read = (board, serviceId, characteristicId, duration = 0) => {
|
|
12
11
|
return new Promise((resolve, reject) => {
|
|
13
|
-
if (isConnected(
|
|
12
|
+
if (board.isConnected()) {
|
|
14
13
|
const characteristic = getCharacteristic(board, serviceId, characteristicId);
|
|
15
14
|
if (characteristic) {
|
|
16
15
|
characteristic
|
package/dist/stop.d.ts
CHANGED
package/dist/stop.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { write } from "./write";
|
|
2
|
-
import { isConnected } from "./is-connected";
|
|
3
2
|
import { MotherboardCommands, ProgressorCommands } from "./commands";
|
|
4
3
|
import { isMotherboard, isProgressor } from "./is-device";
|
|
5
4
|
/**
|
|
@@ -8,7 +7,7 @@ import { isMotherboard, isProgressor } from "./is-device";
|
|
|
8
7
|
* @returns {Promise<void>} A promise that resolves when the stream is stopped.
|
|
9
8
|
*/
|
|
10
9
|
export const stop = async (board) => {
|
|
11
|
-
if (isConnected(
|
|
10
|
+
if (board.isConnected()) {
|
|
12
11
|
if (isMotherboard(board)) {
|
|
13
12
|
// Stop stream on Motherboard
|
|
14
13
|
await write(board, "uart", "tx", MotherboardCommands.STOP_WEIGHT_MEAS, 0);
|
package/dist/write.d.ts
CHANGED
package/dist/write.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { isConnected } from "./is-connected";
|
|
2
1
|
import { getCharacteristic } from "./characteristic";
|
|
3
2
|
/**
|
|
4
3
|
* The last message written to the device.
|
|
@@ -32,7 +31,7 @@ export let writeCallback = (data) => {
|
|
|
32
31
|
* });
|
|
33
32
|
*/
|
|
34
33
|
export const write = async (board, serviceId, characteristicId, message, duration = 0, callback = writeCallback) => {
|
|
35
|
-
if (isConnected(
|
|
34
|
+
if (board.isConnected()) {
|
|
36
35
|
// Check if message is provided
|
|
37
36
|
if (message === undefined) {
|
|
38
37
|
// If not provided, return without performing write operation
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Griptonite Motherboard, Tindeq Progressor, PitchSix Force Board, WHC-06, Entralpi, Climbro, mySmartBoard: Web Bluetooth API Force-Sensing strength analysis for climbers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"motherboard",
|
|
24
24
|
"tindeq",
|
|
25
25
|
"progressor",
|
|
26
|
+
"pitchsix",
|
|
27
|
+
"forceboard",
|
|
26
28
|
"entralpi",
|
|
27
29
|
"wh-c06",
|
|
28
30
|
"kilterboard"
|
package/src/characteristic.ts
CHANGED