@hangtime/grip-connect 0.3.2 → 0.3.4
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 +25 -7
- package/package.json +1 -1
- package/src/battery.ts +2 -2
- package/src/calibration.ts +1 -1
- package/src/connect.ts +5 -21
- package/src/devices/climbro.ts +1 -1
- package/src/devices/entralpi.ts +5 -1
- package/src/devices/index.ts +2 -0
- package/src/devices/kilterboard.ts +32 -0
- package/src/devices/motherboard.ts +1 -2
- package/src/devices/musclemeter.ts +1 -1
- package/src/devices/mysmartboard.ts +1 -1
- package/src/devices/progressor.ts +1 -1
- package/src/index.ts +1 -1
- package/src/info.ts +2 -2
- package/src/stop.ts +2 -2
- package/src/stream.ts +2 -2
- package/src/types/devices.ts +2 -4
- package/src/write.ts +0 -3
package/README.md
CHANGED
|
@@ -3,18 +3,31 @@
|
|
|
3
3
|
**Force-Sensing Climbing Training**
|
|
4
4
|
|
|
5
5
|
The objective of this project is to create a Web Bluetooth API client that can establish connections with various
|
|
6
|
-
Force-Sensing Hangboards / Plates used by climbers for strength measurement. Examples of such
|
|
7
|
-
[Griptonite Motherboard](https://griptonite.io/shop/motherboard/),
|
|
8
|
-
[
|
|
9
|
-
[
|
|
6
|
+
Force-Sensing Hangboards / Plates / LED system boards used by climbers for strength measurement. Examples of such
|
|
7
|
+
hangboards include the [Griptonite Motherboard](https://griptonite.io/shop/motherboard/),
|
|
8
|
+
[Climbro](https://climbro.com/), [mySmartBoard](https://www.smartboard-climbing.com/),
|
|
9
|
+
[Entralpi](https://entralpi.com/), [Tindeq Progressor](https://tindeq.com/) or
|
|
10
|
+
[MAT Muscle Meter](https://www.matassessment.com/musclemeter)
|
|
11
|
+
|
|
12
|
+
And LED system boards from [Aurora Climbing](https://auroraclimbing.com/) like the
|
|
13
|
+
[Kilter Board](https://settercloset.com/pages/the-kilter-board),
|
|
14
|
+
[Tension Board](https://tensionclimbing.com/product/tension-board-2/),
|
|
15
|
+
[Grasshopper Board](https://grasshopperclimbing.com/products/),
|
|
16
|
+
[Decoy Board](https://decoy-holds.com/pages/decoy-board), [Touchstone Board](https://touchstoneboardapp.com/) and
|
|
17
|
+
[So iLL Board](https://apps.apple.com/us/app/so-ill-board/id1358056082).
|
|
10
18
|
|
|
11
19
|
Learn more: [Docs](https://stevie-ray.github.io/hangtime-grip-connect/) -
|
|
12
20
|
[Browser Support](https://caniuse.com/web-bluetooth)
|
|
13
21
|
|
|
22
|
+
> This project is provided "as-is" without any express or implied warranties. By using this software, you assume all
|
|
23
|
+
> risks associated with its use, including but not limited to hardware damage, data loss, or any other issues that may
|
|
24
|
+
> arise. The developers and contributors are not responsible for any harm or loss incurred. Use this software at your
|
|
25
|
+
> own discretion and responsibility.
|
|
26
|
+
|
|
14
27
|
## Try it out
|
|
15
28
|
|
|
16
29
|
[Chart](https://grip-connect.vercel.app/) - [Flappy Bird](https://grip-connect-flappy-bird.vercel.app/) -
|
|
17
|
-
[
|
|
30
|
+
[Kilter Board](https://grip-connect-kilter-board.vercel.app/)
|
|
18
31
|
|
|
19
32
|
## Install
|
|
20
33
|
|
|
@@ -77,7 +90,8 @@ available services with us.
|
|
|
77
90
|
|
|
78
91
|
- ✅ Griptonite Motherboard
|
|
79
92
|
- ✅ Tindeq Progressor
|
|
80
|
-
-
|
|
93
|
+
- ⏳ Entralpi (not verified)
|
|
94
|
+
- ⏳ Kilterboard (write only, see example)
|
|
81
95
|
- ➡️ Climbro
|
|
82
96
|
- ➡️ mySmartBoard
|
|
83
97
|
- ➡️ MAT Muscle Meter
|
|
@@ -113,9 +127,13 @@ A special thank you to:
|
|
|
113
127
|
- [@donaldharvey](https://github.com/donaldharvey) for a valuable example on connecting to the Motherboard.
|
|
114
128
|
- [@ecstrema](https://github.com/ecstrema) for providing [examples](https://github.com/ecstrema/entralpi-games) on how
|
|
115
129
|
to play games with the Entralpi.
|
|
116
|
-
- [Tindeq](https://tindeq.com/) for providing an open [Progressor API](https://tindeq.com/progressor_api/)
|
|
130
|
+
- [Tindeq](https://tindeq.com/) for providing an open [Progressor API](https://tindeq.com/progressor_api/).
|
|
117
131
|
- [@StuartLittlefair](https://github.com/StuartLittlefair) for his
|
|
118
132
|
[PyTindeq](https://github.com/StuartLittlefair/PyTindeq) implementation.
|
|
133
|
+
- [@Phil9l](https://github.com/phil9l) for his research and providing a [blog](https://bazun.me/blog/kiterboard/) on how
|
|
134
|
+
to connect with the Kilter Board.
|
|
135
|
+
- [@1-max-1](https://github.com/1-max-1) for the docs on his Kilter Board
|
|
136
|
+
[simulator](https://github.com/1-max-1/fake_kilter_board).
|
|
119
137
|
|
|
120
138
|
## Disclaimer
|
|
121
139
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hangtime/grip-connect",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "A client that can establish connections with various Force-Sensing Hangboards/Plates used by climbers for strength measurement. Examples of such hangboards include the Griptonite Motherboard, Climbro, SmartBoard, Entralpi or Tindeq Progressor",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"scripts": {
|
package/src/battery.ts
CHANGED
|
@@ -14,12 +14,12 @@ export const battery = async (board: Device): Promise<void> => {
|
|
|
14
14
|
// Check if the device is connected
|
|
15
15
|
if (isConnected(board)) {
|
|
16
16
|
// If the device is connected and it is a Motherboard device
|
|
17
|
-
if (board.name === "Motherboard") {
|
|
17
|
+
if (board.filters.some((filter) => filter.name === "Motherboard")) {
|
|
18
18
|
// Read battery level information from the Motherboard
|
|
19
19
|
await read(Motherboard, "battery", "level", 250)
|
|
20
20
|
}
|
|
21
21
|
// If the device is connected and its name starts with "Progressor"
|
|
22
|
-
if (board.
|
|
22
|
+
if (board.filters.some((filter) => filter.namePrefix === "Progressor")) {
|
|
23
23
|
// Write command to get battery voltage information to the Progressor
|
|
24
24
|
await write(Progressor, "progressor", "tx", ProgressorCommands.GET_BATT_VLTG, 250)
|
|
25
25
|
}
|
package/src/calibration.ts
CHANGED
|
@@ -13,7 +13,7 @@ export const calibration = async (board: Device): Promise<void> => {
|
|
|
13
13
|
// Check if the device is connected
|
|
14
14
|
if (isConnected(board)) {
|
|
15
15
|
// If the device is connected, and it is a Motherboard device
|
|
16
|
-
if (board.name === "Motherboard") {
|
|
16
|
+
if (board.filters.some((filter) => filter.name === "Motherboard")) {
|
|
17
17
|
// Write the command to get calibration data to the device
|
|
18
18
|
await write(Motherboard, "uart", "tx", MotherboardCommands.GET_CALIBRATION, 2500)
|
|
19
19
|
}
|
package/src/connect.ts
CHANGED
|
@@ -24,7 +24,8 @@ const handleNotifications = (event: Event, board: Device): void => {
|
|
|
24
24
|
const value: DataView | undefined = characteristic.value
|
|
25
25
|
|
|
26
26
|
if (value) {
|
|
27
|
-
|
|
27
|
+
// If the device is connected and it is a Motherboard device
|
|
28
|
+
if (board.filters.some((filter) => filter.name === "Motherboard")) {
|
|
28
29
|
for (let i: number = 0; i < value.byteLength; i++) {
|
|
29
30
|
receiveBuffer.push(value.getUint8(i))
|
|
30
31
|
}
|
|
@@ -37,14 +38,14 @@ const handleNotifications = (event: Event, board: Device): void => {
|
|
|
37
38
|
const receivedData: string = decoder.decode(new Uint8Array(line))
|
|
38
39
|
handleMotherboardData(receivedData)
|
|
39
40
|
}
|
|
40
|
-
} else if (board.name === "ENTRALPI") {
|
|
41
|
+
} else if (board.filters.some((filter) => filter.name === "ENTRALPI")) {
|
|
41
42
|
if (value.buffer) {
|
|
42
43
|
const buffer: ArrayBuffer = value.buffer
|
|
43
44
|
const rawData: DataView = new DataView(buffer)
|
|
44
45
|
const receivedData: string = (rawData.getUint16(0) / 100).toFixed(1)
|
|
45
46
|
handleEntralpiData(receivedData)
|
|
46
47
|
}
|
|
47
|
-
} else if (board.
|
|
48
|
+
} else if (board.filters.some((filter) => filter.namePrefix === "Progressor")) {
|
|
48
49
|
if (value.buffer) {
|
|
49
50
|
const buffer: ArrayBuffer = value.buffer
|
|
50
51
|
const rawData: DataView = new DataView(buffer)
|
|
@@ -126,25 +127,8 @@ export const connect = async (board: Device, onSuccess: () => void): Promise<voi
|
|
|
126
127
|
// Request device and set up connection
|
|
127
128
|
const deviceServices = getAllServiceUUIDs(board)
|
|
128
129
|
|
|
129
|
-
// setup filter list
|
|
130
|
-
const filters = []
|
|
131
|
-
|
|
132
|
-
if (board.name) {
|
|
133
|
-
const filterName = board.name === "Progressor" ? { namePrefix: board.name } : { name: board.name }
|
|
134
|
-
filters.push(filterName)
|
|
135
|
-
}
|
|
136
|
-
if (board.companyId) {
|
|
137
|
-
filters.push({
|
|
138
|
-
manufacturerData: [
|
|
139
|
-
{
|
|
140
|
-
companyIdentifier: board.companyId,
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
})
|
|
144
|
-
}
|
|
145
|
-
|
|
146
130
|
const device = await navigator.bluetooth.requestDevice({
|
|
147
|
-
filters: filters,
|
|
131
|
+
filters: board.filters,
|
|
148
132
|
optionalServices: deviceServices,
|
|
149
133
|
})
|
|
150
134
|
|
package/src/devices/climbro.ts
CHANGED
package/src/devices/entralpi.ts
CHANGED
package/src/devices/index.ts
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Device } from "../types/devices"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents a Aurora Climbing device
|
|
5
|
+
* Kilter Board, Tension Board, Decoy Board, Touchstone Board, Grasshopper Board, Aurora Board, So iLL Board
|
|
6
|
+
*/
|
|
7
|
+
export const KilterBoard: Device = {
|
|
8
|
+
filters: [
|
|
9
|
+
{
|
|
10
|
+
services: ["4488b571-7806-4df6-bcff-a2897e4953ff"], // Aurora Climbing Advertising service
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
services: [
|
|
14
|
+
{
|
|
15
|
+
name: "UART Nordic Service",
|
|
16
|
+
id: "uart",
|
|
17
|
+
uuid: "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
|
|
18
|
+
characteristics: [
|
|
19
|
+
{
|
|
20
|
+
name: "TX",
|
|
21
|
+
id: "tx",
|
|
22
|
+
uuid: "6e400002-b5a3-f393-e0a9-e50e24dcca9e",
|
|
23
|
+
},
|
|
24
|
+
// {
|
|
25
|
+
// name: "RX",
|
|
26
|
+
// id: "rx",
|
|
27
|
+
// uuid: "6e400003-b5a3-f393-e0a9-e50e24dcca9e",
|
|
28
|
+
// },
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}
|
|
@@ -4,8 +4,7 @@ import type { Device } from "../types/devices"
|
|
|
4
4
|
* Represents a Griptonite Motherboard device
|
|
5
5
|
*/
|
|
6
6
|
export const Motherboard: Device = {
|
|
7
|
-
name: "Motherboard",
|
|
8
|
-
companyId: 0x2a29,
|
|
7
|
+
filters: [{ name: "Motherboard" }],
|
|
9
8
|
services: [
|
|
10
9
|
{
|
|
11
10
|
name: "Device Information",
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Export device types
|
|
2
|
-
export { Climbro, Entralpi, Motherboard, mySmartBoard, MuscleMeter, Progressor } from "./devices/index"
|
|
2
|
+
export { Climbro, Entralpi, KilterBoard, Motherboard, mySmartBoard, MuscleMeter, Progressor } from "./devices/index"
|
|
3
3
|
|
|
4
4
|
// Export battery related functions
|
|
5
5
|
export { battery } from "./battery"
|
package/src/info.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { MotherboardCommands, ProgressorCommands } from "./commands"
|
|
|
12
12
|
*/
|
|
13
13
|
export const info = async (board: Device): Promise<void> => {
|
|
14
14
|
if (isConnected(board)) {
|
|
15
|
-
if (board.name === "Motherboard") {
|
|
15
|
+
if (board.filters.some((filter) => filter.name === "Motherboard")) {
|
|
16
16
|
// Read manufacturer information
|
|
17
17
|
await read(Motherboard, "device", "manufacturer", 250)
|
|
18
18
|
// Read hardware version
|
|
@@ -24,7 +24,7 @@ export const info = async (board: Device): Promise<void> => {
|
|
|
24
24
|
// Get serial number from Motherboard
|
|
25
25
|
await write(Motherboard, "uart", "tx", MotherboardCommands.GET_SERIAL, 250)
|
|
26
26
|
}
|
|
27
|
-
if (board.
|
|
27
|
+
if (board.filters.some((filter) => filter.namePrefix === "Progressor")) {
|
|
28
28
|
// Get firmware version from Progressor
|
|
29
29
|
await write(Progressor, "progressor", "tx", ProgressorCommands.GET_FW_VERSION, 250)
|
|
30
30
|
}
|
package/src/stop.ts
CHANGED
|
@@ -11,11 +11,11 @@ import { MotherboardCommands, ProgressorCommands } from "./commands"
|
|
|
11
11
|
*/
|
|
12
12
|
export const stop = async (board: Device): Promise<void> => {
|
|
13
13
|
if (isConnected(board)) {
|
|
14
|
-
if (board.name === "Motherboard") {
|
|
14
|
+
if (board.filters.some((filter) => filter.name === "Motherboard")) {
|
|
15
15
|
// Stop stream on Motherboard
|
|
16
16
|
await write(Motherboard, "uart", "tx", MotherboardCommands.STOP_WEIGHT_MEAS, 0)
|
|
17
17
|
}
|
|
18
|
-
if (board.
|
|
18
|
+
if (board.filters.some((filter) => filter.namePrefix === "Progressor")) {
|
|
19
19
|
// Stop stream on Progressor
|
|
20
20
|
await write(Progressor, "progressor", "tx", ProgressorCommands.STOP_WEIGHT_MEAS, 0)
|
|
21
21
|
}
|
package/src/stream.ts
CHANGED
|
@@ -19,7 +19,7 @@ export const stream = async (board: Device, duration: number = 0): Promise<void>
|
|
|
19
19
|
// Reset download packets
|
|
20
20
|
emptyDownloadPackets()
|
|
21
21
|
// Device specific logic
|
|
22
|
-
if (board.name === "Motherboard") {
|
|
22
|
+
if (board.filters.some((filter) => filter.name === "Motherboard")) {
|
|
23
23
|
// Read calibration data if not already available
|
|
24
24
|
if (!CALIBRATION[0].length) {
|
|
25
25
|
await calibration(Motherboard)
|
|
@@ -31,7 +31,7 @@ export const stream = async (board: Device, duration: number = 0): Promise<void>
|
|
|
31
31
|
await stop(Motherboard)
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
if (board.
|
|
34
|
+
if (board.filters.some((filter) => filter.namePrefix === "Progressor")) {
|
|
35
35
|
// Start streaming data
|
|
36
36
|
await write(Progressor, "progressor", "tx", ProgressorCommands.START_WEIGHT_MEAS, duration)
|
|
37
37
|
// Stop streaming if duration is set
|
package/src/types/devices.ts
CHANGED
|
@@ -30,10 +30,8 @@ interface Service {
|
|
|
30
30
|
* Represents a Bluetooth device.
|
|
31
31
|
*/
|
|
32
32
|
export interface Device {
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
/** Optional company identifier of the device */
|
|
36
|
-
companyId?: number
|
|
33
|
+
/** Filters to indentify the device */
|
|
34
|
+
filters: BluetoothLEScanFilter[]
|
|
37
35
|
/** Array of services provided by the device */
|
|
38
36
|
services: Service[]
|
|
39
37
|
/** Reference to the BluetoothDevice object representing this device */
|
package/src/write.ts
CHANGED