@hangtime/grip-connect 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/README.md +9 -9
  2. package/dist/cjs/index.d.ts +2 -2
  3. package/dist/cjs/index.d.ts.map +1 -1
  4. package/dist/cjs/index.js +2 -3
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/interfaces/device/aurora.interface.d.ts +17 -0
  7. package/dist/cjs/interfaces/device/aurora.interface.d.ts.map +1 -0
  8. package/dist/cjs/interfaces/device/{kilterboard.interface.js → aurora.interface.js} +1 -1
  9. package/dist/cjs/interfaces/device/aurora.interface.js.map +1 -0
  10. package/dist/cjs/interfaces/device/motherboard.interface.d.ts +1 -1
  11. package/dist/cjs/interfaces/device/pb-700bt.interface.d.ts +53 -0
  12. package/dist/cjs/interfaces/device/pb-700bt.interface.d.ts.map +1 -0
  13. package/dist/cjs/interfaces/device/pb-700bt.interface.js +3 -0
  14. package/dist/cjs/interfaces/device/pb-700bt.interface.js.map +1 -0
  15. package/dist/cjs/interfaces/index.d.ts +2 -1
  16. package/dist/cjs/interfaces/index.d.ts.map +1 -1
  17. package/dist/cjs/models/device/{kilterboard.model.d.ts → aurora.model.d.ts} +82 -40
  18. package/dist/cjs/models/device/aurora.model.d.ts.map +1 -0
  19. package/dist/cjs/models/device/aurora.model.js +407 -0
  20. package/dist/cjs/models/device/aurora.model.js.map +1 -0
  21. package/dist/cjs/models/device/climbro.model.js +1 -1
  22. package/dist/cjs/models/device/climbro.model.js.map +1 -1
  23. package/dist/cjs/models/device/cts500.model.d.ts.map +1 -1
  24. package/dist/cjs/models/device/cts500.model.js +12 -4
  25. package/dist/cjs/models/device/cts500.model.js.map +1 -1
  26. package/dist/cjs/models/device/forceboard.model.d.ts.map +1 -1
  27. package/dist/cjs/models/device/forceboard.model.js +6 -2
  28. package/dist/cjs/models/device/forceboard.model.js.map +1 -1
  29. package/dist/cjs/models/device/motherboard.model.d.ts +4 -1
  30. package/dist/cjs/models/device/motherboard.model.d.ts.map +1 -1
  31. package/dist/cjs/models/device/motherboard.model.js +26 -10
  32. package/dist/cjs/models/device/motherboard.model.js.map +1 -1
  33. package/dist/cjs/models/device/pb-700bt.model.d.ts +2 -1
  34. package/dist/cjs/models/device/pb-700bt.model.d.ts.map +1 -1
  35. package/dist/cjs/models/device/pb-700bt.model.js.map +1 -1
  36. package/dist/cjs/models/device/progressor.model.d.ts.map +1 -1
  37. package/dist/cjs/models/device/progressor.model.js +1 -6
  38. package/dist/cjs/models/device/progressor.model.js.map +1 -1
  39. package/dist/cjs/models/device/wh-c06.model.d.ts +2 -0
  40. package/dist/cjs/models/device/wh-c06.model.d.ts.map +1 -1
  41. package/dist/cjs/models/device/wh-c06.model.js +45 -34
  42. package/dist/cjs/models/device/wh-c06.model.js.map +1 -1
  43. package/dist/cjs/models/device.model.d.ts +18 -5
  44. package/dist/cjs/models/device.model.d.ts.map +1 -1
  45. package/dist/cjs/models/device.model.js +71 -16
  46. package/dist/cjs/models/device.model.js.map +1 -1
  47. package/dist/cjs/models/index.d.ts +1 -1
  48. package/dist/cjs/models/index.d.ts.map +1 -1
  49. package/dist/cjs/models/index.js +3 -4
  50. package/dist/cjs/models/index.js.map +1 -1
  51. package/dist/cjs/package.json +3 -0
  52. package/dist/index.d.ts +2 -2
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -1
  55. package/dist/index.js.map +1 -1
  56. package/dist/interfaces/device/aurora.interface.d.ts +17 -0
  57. package/dist/interfaces/device/aurora.interface.d.ts.map +1 -0
  58. package/dist/interfaces/device/aurora.interface.js +2 -0
  59. package/dist/interfaces/device/aurora.interface.js.map +1 -0
  60. package/dist/interfaces/device/motherboard.interface.d.ts +1 -1
  61. package/dist/interfaces/device/pb-700bt.interface.d.ts +53 -0
  62. package/dist/interfaces/device/pb-700bt.interface.d.ts.map +1 -0
  63. package/dist/interfaces/device/pb-700bt.interface.js +2 -0
  64. package/dist/interfaces/device/pb-700bt.interface.js.map +1 -0
  65. package/dist/interfaces/index.d.ts +2 -1
  66. package/dist/interfaces/index.d.ts.map +1 -1
  67. package/dist/models/device/{kilterboard.model.d.ts → aurora.model.d.ts} +82 -40
  68. package/dist/models/device/aurora.model.d.ts.map +1 -0
  69. package/dist/models/device/aurora.model.js +401 -0
  70. package/dist/models/device/aurora.model.js.map +1 -0
  71. package/dist/models/device/climbro.model.js +1 -1
  72. package/dist/models/device/climbro.model.js.map +1 -1
  73. package/dist/models/device/cts500.model.d.ts.map +1 -1
  74. package/dist/models/device/cts500.model.js +12 -4
  75. package/dist/models/device/cts500.model.js.map +1 -1
  76. package/dist/models/device/forceboard.model.d.ts.map +1 -1
  77. package/dist/models/device/forceboard.model.js +6 -2
  78. package/dist/models/device/forceboard.model.js.map +1 -1
  79. package/dist/models/device/motherboard.model.d.ts +4 -1
  80. package/dist/models/device/motherboard.model.d.ts.map +1 -1
  81. package/dist/models/device/motherboard.model.js +26 -10
  82. package/dist/models/device/motherboard.model.js.map +1 -1
  83. package/dist/models/device/pb-700bt.model.d.ts +2 -1
  84. package/dist/models/device/pb-700bt.model.d.ts.map +1 -1
  85. package/dist/models/device/pb-700bt.model.js.map +1 -1
  86. package/dist/models/device/progressor.model.d.ts.map +1 -1
  87. package/dist/models/device/progressor.model.js +1 -6
  88. package/dist/models/device/progressor.model.js.map +1 -1
  89. package/dist/models/device/wh-c06.model.d.ts +2 -0
  90. package/dist/models/device/wh-c06.model.d.ts.map +1 -1
  91. package/dist/models/device/wh-c06.model.js +44 -34
  92. package/dist/models/device/wh-c06.model.js.map +1 -1
  93. package/dist/models/device.model.d.ts +18 -5
  94. package/dist/models/device.model.d.ts.map +1 -1
  95. package/dist/models/device.model.js +71 -16
  96. package/dist/models/device.model.js.map +1 -1
  97. package/dist/models/index.d.ts +1 -1
  98. package/dist/models/index.d.ts.map +1 -1
  99. package/dist/models/index.js +1 -1
  100. package/dist/models/index.js.map +1 -1
  101. package/package.json +45 -42
  102. package/src/index.ts +4 -3
  103. package/src/interfaces/device/aurora.interface.ts +18 -0
  104. package/src/interfaces/device/motherboard.interface.ts +1 -1
  105. package/src/interfaces/device/pb-700bt.interface.ts +61 -0
  106. package/src/interfaces/index.ts +4 -2
  107. package/src/models/device/aurora.model.ts +497 -0
  108. package/src/models/device/climbro.model.ts +1 -1
  109. package/src/models/device/cts500.model.ts +14 -7
  110. package/src/models/device/forceboard.model.ts +6 -2
  111. package/src/models/device/motherboard.model.ts +51 -9
  112. package/src/models/device/pb-700bt.model.ts +2 -1
  113. package/src/models/device/progressor.model.ts +1 -6
  114. package/src/models/device/wh-c06.model.ts +54 -42
  115. package/src/models/device.model.ts +82 -16
  116. package/src/models/index.ts +2 -2
  117. package/dist/cjs/interfaces/device/kilterboard.interface.d.ts +0 -17
  118. package/dist/cjs/interfaces/device/kilterboard.interface.d.ts.map +0 -1
  119. package/dist/cjs/interfaces/device/kilterboard.interface.js.map +0 -1
  120. package/dist/cjs/models/device/kilterboard.model.d.ts.map +0 -1
  121. package/dist/cjs/models/device/kilterboard.model.js +0 -327
  122. package/dist/cjs/models/device/kilterboard.model.js.map +0 -1
  123. package/dist/interfaces/device/kilterboard.interface.d.ts +0 -17
  124. package/dist/interfaces/device/kilterboard.interface.d.ts.map +0 -1
  125. package/dist/interfaces/device/kilterboard.interface.js +0 -2
  126. package/dist/interfaces/device/kilterboard.interface.js.map +0 -1
  127. package/dist/models/device/kilterboard.model.d.ts.map +0 -1
  128. package/dist/models/device/kilterboard.model.js +0 -323
  129. package/dist/models/device/kilterboard.model.js.map +0 -1
  130. package/dist/tsconfig.cjs.tsbuildinfo +0 -1
  131. package/src/interfaces/device/kilterboard.interface.ts +0 -12
  132. package/src/models/device/kilterboard.model.ts +0 -347
@@ -1,347 +0,0 @@
1
- import { Device } from "../device.model.js"
2
- import type { IKilterBoard } from "../../interfaces/device/kilterboard.interface.js"
3
-
4
- /**
5
- * For API level 2 and API level 3.
6
- * The first byte in the data is dependent on where the packet is in the message as a whole.
7
- * More details: https://github.com/1-max-1/fake_kilter_board
8
- */
9
- export enum KilterBoardPacket {
10
- /** If this packet is in the middle, the byte gets set to 77 (M). */
11
- V2_MIDDLE = 77,
12
- /** If this packet is the first packet in the message, then this byte gets set to 78 (N). */
13
- V2_FIRST,
14
- /** If this is the last packet in the message, this byte gets set to 79 (0). */
15
- V2_LAST,
16
- /** If this packet is the only packet in the message, the byte gets set to 80 (P). Note that this takes priority over the other conditions. */
17
- V2_ONLY,
18
- /** If this packet is in the middle, the byte gets set to 81 (Q). */
19
- V3_MIDDLE,
20
- /** If this packet is the first packet in the message, then this byte gets set to 82 (R). */
21
- V3_FIRST,
22
- /** If this is the last packet in the message, this byte gets set to 83 (S). */
23
- V3_LAST,
24
- /** If this packet is the only packet in the message, the byte gets set to 84 (T). Note that this takes priority over the other conditions. */
25
- V3_ONLY,
26
- }
27
- /**
28
- * Extracted from placement_roles database table.
29
- */
30
- export const KilterBoardPlacementRoles = [
31
- {
32
- id: 12,
33
- product_id: 1,
34
- position: 1,
35
- name: "start",
36
- full_name: "Start",
37
- led_color: "00FF00",
38
- screen_color: "00DD00",
39
- },
40
- {
41
- id: 13,
42
- product_id: 1,
43
- position: 2,
44
- name: "middle",
45
- full_name: "Middle",
46
- led_color: "00FFFF",
47
- screen_color: "00FFFF",
48
- },
49
- {
50
- id: 14,
51
- product_id: 1,
52
- position: 3,
53
- name: "finish",
54
- full_name: "Finish",
55
- led_color: "FF00FF",
56
- screen_color: "FF00FF",
57
- },
58
- {
59
- id: 15,
60
- product_id: 1,
61
- position: 4,
62
- name: "foot",
63
- full_name: "Foot Only",
64
- led_color: "FFB600",
65
- screen_color: "FFA500",
66
- },
67
- ]
68
-
69
- /**
70
- * Represents a Aurora Climbing device.
71
- * Kilter Board, Tension Board, Decoy Board, Touchstone Board, Grasshopper Board, Aurora Board, So iLL Board
72
- * {@link https://auroraclimbing.com}
73
- */
74
- export class KilterBoard extends Device implements IKilterBoard {
75
- /**
76
- * UUID for the Aurora Climbing Advertising service.
77
- * This constant is used to identify the specific Bluetooth service for Kilter Boards.
78
- * @type {string}
79
- * @static
80
- * @readonly
81
- * @constant
82
- */
83
- static readonly AuroraUUID: string = "4488b571-7806-4df6-bcff-a2897e4953ff"
84
-
85
- /**
86
- * Maximum length of the message body for byte wrapping.
87
- * This value defines the limit for the size of messages that can be sent or received
88
- * to ensure proper byte wrapping in communication.
89
- * @type {number}
90
- * @private
91
- * @readonly
92
- * @constant
93
- */
94
- private static readonly messageBodyMaxLength: number = 255
95
-
96
- /**
97
- * Maximum length of the Bluetooth message chunk.
98
- * This value sets the upper limit for the size of individual Bluetooth messages
99
- * sent to and from the device to comply with Bluetooth protocol constraints.
100
- * @type {number}
101
- * @private
102
- * @readonly
103
- * @constant
104
- */
105
- private static readonly maxBluetoothMessageSize: number = 20
106
-
107
- constructor() {
108
- super({
109
- filters: [
110
- {
111
- services: [KilterBoard.AuroraUUID],
112
- },
113
- ],
114
- services: [
115
- {
116
- name: "UART Nordic Service",
117
- id: "uart",
118
- uuid: "6e400001-b5a3-f393-e0a9-e50e24dcca9e",
119
- characteristics: [
120
- {
121
- name: "TX",
122
- id: "tx",
123
- uuid: "6e400002-b5a3-f393-e0a9-e50e24dcca9e",
124
- },
125
- // {
126
- // name: "RX",
127
- // id: "rx",
128
- // uuid: "6e400003-b5a3-f393-e0a9-e50e24dcca9e",
129
- // },
130
- ],
131
- },
132
- ],
133
- })
134
- }
135
-
136
- /**
137
- * Calculates the checksum for a byte array by summing up all bytes ot hre packet in a single-byte variable.
138
- * @param data - The array of bytes to calculate the checksum for.
139
- * @returns {number} The calculated checksum value.
140
- */
141
- private checksum(data: number[]): number {
142
- let i = 0
143
- for (const value of data) {
144
- i = (i + value) & 255
145
- }
146
- return ~i & 255
147
- }
148
-
149
- /**
150
- * Wraps a byte array with header and footer bytes for transmission.
151
- * @param data - The array of bytes to wrap.
152
- * @returns {number[]} The wrapped byte array.
153
- */
154
- private wrapBytes(data: number[]): number[] {
155
- if (data.length > KilterBoard.messageBodyMaxLength) {
156
- return []
157
- }
158
- /**
159
- - 0x1
160
- - len(packets)
161
- - checksum(packets)
162
- - 0x2
163
- - *packets
164
- - 0x3
165
-
166
- First byte is always 1, the second is a number of packets, then checksum, then 2, packets themselves, and finally 3.
167
- */
168
- return [1, data.length, this.checksum(data), 2, ...data, 3]
169
- }
170
-
171
- /**
172
- * Encodes a position into a byte array.
173
- * The lowest 8 bits of the position get put in the first byte of the group.
174
- * The highest 8 bits of the position get put in the second byte of the group.
175
- * @param position - The position to encode.
176
- * @returns {number[]} The encoded byte array representing the position.
177
- */
178
- private encodePosition(position: number): number[] {
179
- const position1 = position & 255
180
- const position2 = (position & 65280) >> 8
181
-
182
- return [position1, position2]
183
- }
184
-
185
- /**
186
- * Encodes a color string into a numeric representation.
187
- * The rgb color, 3 bits for the R and G components, 2 bits for the B component, with the 3 R bits occupying the high end of the byte and the 2 B bits in the low end (hence 3 G bits in the middle).
188
- * Format: 0bRRRGGGBB where RRR is 3 bits for red, GGG is 3 bits for green, BB is 2 bits for blue.
189
- * @param color - The color string in hexadecimal format (e.g., 'FFFFFF').
190
- * @returns The encoded /compressed color value.
191
- */
192
- private encodeColor(color: string): number {
193
- const r = parseInt(color.substring(0, 2), 16)
194
- const g = parseInt(color.substring(2, 4), 16)
195
- const b = parseInt(color.substring(4, 6), 16)
196
-
197
- // Integer division: R and G divided by 32, B divided by 64
198
- // Then pack into 0bRRRGGGBB format
199
- const rBits = Math.floor(r / 32) // 0-7 (3 bits)
200
- const gBits = Math.floor(g / 32) // 0-7 (3 bits)
201
- const bBits = Math.floor(b / 64) // 0-3 (2 bits)
202
-
203
- // Pack: RRR in bits 7-5, GGG in bits 4-2, BB in bits 1-0
204
- return (rBits << 5) | (gBits << 2) | bBits
205
- }
206
-
207
- /**
208
- * Encodes a placement (requires a 16-bit position and a 24-bit rgb color. ) into a byte array.
209
- * @param position - The position to encode.
210
- * @param ledColor - The color of the LED in hexadecimal format (e.g., 'FFFFFF').
211
- * @returns The encoded byte array representing the placement.
212
- */
213
- private encodePlacement(position: number, ledColor: string): number[] {
214
- return [...this.encodePosition(position), this.encodeColor(ledColor)]
215
- }
216
-
217
- /**
218
- * Prepares byte arrays for transmission based on a list of climb placements.
219
- * @param {{ position: number; role_id?: number; color?: string }[]} climbPlacementList - The list of climb placements containing position and either role ID or color.
220
- * @returns {number[]} The final byte array ready for transmission.
221
- */
222
- private prepBytesV3(climbPlacementList: { position: number; role_id?: number; color?: string }[]): number[] {
223
- const resultArray: number[][] = []
224
- let tempArray: number[] = [KilterBoardPacket.V3_MIDDLE]
225
-
226
- // Filter out any invalid placements and clean up objects before processing
227
- const validPlacements = climbPlacementList
228
- .filter((p) => {
229
- // Explicitly check for color
230
- const hasColor = p.color != null && typeof p.color === "string" && p.color.trim() !== ""
231
- // Explicitly check for role_id - must be a number, not undefined or null
232
- const hasRoleId = typeof p.role_id === "number" && !isNaN(p.role_id)
233
-
234
- return hasColor || hasRoleId
235
- })
236
- .map((p) => {
237
- // Create clean objects without undefined properties
238
- if (p.color != null && typeof p.color === "string" && p.color.trim() !== "") {
239
- return { position: p.position, color: p.color.trim() } as { position: number; color: string }
240
- } else if (typeof p.role_id === "number" && !isNaN(p.role_id)) {
241
- return { position: p.position, role_id: p.role_id } as { position: number; role_id: number }
242
- }
243
- // This should never happen due to the filter above, but TypeScript needs this
244
- throw new Error("Invalid placement after filter")
245
- })
246
-
247
- for (const climbPlacement of validPlacements) {
248
- if (tempArray.length + 3 > KilterBoard.messageBodyMaxLength) {
249
- resultArray.push(tempArray)
250
- tempArray = [KilterBoardPacket.V3_MIDDLE]
251
- }
252
-
253
- let colorHex: string
254
-
255
- // Type guard: check if this placement has a color property
256
- if ("color" in climbPlacement && climbPlacement.color) {
257
- // Use direct color if provided
258
- colorHex = climbPlacement.color.replace("#", "").toUpperCase()
259
- } else if ("role_id" in climbPlacement && typeof climbPlacement.role_id === "number") {
260
- // Fall back to role_id if no color provided
261
- const role = KilterBoardPlacementRoles.find((placement) => placement.id === climbPlacement.role_id)
262
- if (!role) {
263
- throw new Error(`Role with id ${climbPlacement.role_id} not found in placement_roles`)
264
- }
265
- colorHex = role.led_color
266
- } else {
267
- // This should never happen due to the filter above, but just in case
268
- throw new Error(`Either role_id or color must be provided for position ${climbPlacement.position}`)
269
- }
270
-
271
- const encodedPlacement = this.encodePlacement(climbPlacement.position, colorHex)
272
- tempArray.push(...encodedPlacement)
273
- }
274
-
275
- resultArray.push(tempArray)
276
-
277
- if (resultArray.length === 1) {
278
- resultArray[0][0] = KilterBoardPacket.V3_ONLY
279
- } else if (resultArray.length > 1) {
280
- resultArray[0][0] = KilterBoardPacket.V3_FIRST
281
- resultArray[resultArray.length - 1][0] = KilterBoardPacket.V3_LAST
282
- }
283
-
284
- const finalResultArray: number[] = []
285
- for (const currentArray of resultArray) {
286
- finalResultArray.push(...this.wrapBytes(currentArray))
287
- }
288
-
289
- return finalResultArray
290
- }
291
-
292
- /**
293
- * Splits a collection into slices of the specified length.
294
- * https://github.com/ramda/ramda/blob/master/source/splitEvery
295
- * @param {Number} n
296
- * @param {Array} list
297
- * @return {Array<number[]>}
298
- */
299
- private splitEvery(n: number, list: number[]): number[][] {
300
- if (n <= 0) {
301
- throw new Error("First argument to splitEvery must be a positive integer")
302
- }
303
- const result = []
304
- let idx = 0
305
- while (idx < list.length) {
306
- result.push(list.slice(idx, (idx += n)))
307
- }
308
- return result
309
- }
310
-
311
- /**
312
- * The kilter board only supports messages of 20 bytes
313
- * at a time. This method splits a full message into parts
314
- * of 20 bytes
315
- *
316
- * @param buffer
317
- */
318
- private splitMessages = (buffer: number[]) =>
319
- this.splitEvery(KilterBoard.maxBluetoothMessageSize, buffer).map((arr) => new Uint8Array(arr))
320
-
321
- /**
322
- * Sends a series of messages to a device.
323
- */
324
- private async writeMessageSeries(messages: Uint8Array[]) {
325
- for (const message of messages) {
326
- await this.write("uart", "tx", message)
327
- }
328
- }
329
-
330
- /**
331
- * Configures the LEDs based on an array of climb placements.
332
- * @param {{ position: number; role_id?: number; color?: string }[]} config - Array of climb placements for the LEDs. Either role_id or color (hex string) must be provided.
333
- * @returns {Promise<number[] | undefined>} A promise that resolves with the payload array for the Kilter Board if LED settings were applied, or `undefined` if no action was taken or for the Motherboard.
334
- */
335
- led = async (config: { position: number; role_id?: number; color?: string }[]): Promise<number[] | undefined> => {
336
- // Handle Kilterboard logic: process placements and send payload if connected
337
- if (Array.isArray(config)) {
338
- // Prepares byte arrays for transmission based on a list of climb placements.
339
- const payload = this.prepBytesV3(config)
340
- if (this.isConnected()) {
341
- await this.writeMessageSeries(this.splitMessages(payload))
342
- }
343
- return payload
344
- }
345
- return undefined
346
- }
347
- }