@hangtime/grip-connect 0.2.0 → 0.2.3

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 CHANGED
@@ -4,47 +4,16 @@
4
4
 
5
5
  The objective of this project is to create a Web Bluetooth API client that can establish connections with various
6
6
  Force-Sensing Hangboards / Plates used by climbers for strength measurement. Examples of such hangboards include the
7
- [Motherboard](https://griptonite.io/shop/motherboard/), [Climbro](https://climbro.com/),
7
+ [Griptonite Motherboard](https://griptonite.io/shop/motherboard/), [Climbro](https://climbro.com/),
8
8
  [SmartBoard](https://www.smartboard-climbing.com/), [Entralpi](https://entralpi.com/) or
9
9
  [Tindeq Progressor](https://tindeq.com/)
10
10
 
11
- [Try it out](https://grip-connect.vercel.app/) - [Docs](https://stevie-ray.github.io/hangtime-grip-connect/) -
11
+ Learn more: [Docs](https://stevie-ray.github.io/hangtime-grip-connect/) -
12
12
  [Browser Support](https://caniuse.com/web-bluetooth)
13
13
 
14
- ## Roadmap
15
-
16
- **Help wanted:** Do you own any of these devices? Use Google Chrome's Bluetooth Internals
17
- `chrome://bluetooth-internals/#devices` and press `Start Scan` to look for your device, click on `Inspect` and share all
18
- available services with us.
19
-
20
- - ✅ Griptonite Motherboard
21
- - ✅️ Connect with devices
22
- - ✅️ Read / Write / Notify using Bluetooth
23
- - ✅️ Output weight/force stream
24
- - ✅ Tindeq Progressor
25
- - ✅️ Connect with devices
26
- - ✅️ Read / Write / Notify using Bluetooth
27
- - ✅️ Output weight/force stream
28
- - ✅ Entralpi
29
- - ✅️ Connect with devices
30
- - ✅️ Read / Write / Notify using Bluetooth
31
- - ✅️️ Output weight/force stream
32
- - ➡️ Climbro
33
- - ➡️ Connect with devices
34
- - ➡️ Read / Write / Notify using Bluetooth
35
- - ➡️ Output weight/force stream
36
- - ➡️ SmartBoard
37
- - ➡️ Connect with devices
38
- - ➡️ Read / Write / Notify using Bluetooth
39
- - ➡️ Output weight/force stream
40
-
41
- ## Development
14
+ ## Try it out
42
15
 
43
- ```bash
44
- git clone https://github.com/Stevie-Ray/hangtime-grip-connect
45
- cd hangtime-grip-connect
46
- npm install
47
- ```
16
+ [Chart](https://grip-connect.vercel.app/) - [Flappy Bird](https://grip-connect-flappy-bird.vercel.app/)
48
17
 
49
18
  ## Install
50
19
 
@@ -89,18 +58,59 @@ motherboardButton.addEventListener("click", () => {
89
58
  })
90
59
  ```
91
60
 
61
+ ## Roadmap
62
+
63
+ **Help wanted:** Do you own any of these devices? Use Google Chrome's Bluetooth Internals
64
+ `chrome://bluetooth-internals/#devices` and press `Start Scan` to look for your device, click on `Inspect` and share all
65
+ available services with us.
66
+
67
+ ### Device support
68
+
69
+ - ✅ Griptonite Motherboard
70
+ - ✅ Tindeq Progressor
71
+ - ✅ Entralpi
72
+ - ➡️ Climbro
73
+ - ➡️ SmartBoard
74
+
75
+ ### Features
76
+
77
+ - ✅ Connect
78
+ - ✅ Disconnect
79
+ - ✅ Start data stream (live data)
80
+ - ✅ Stop data stream
81
+ - ✅ Battery status
82
+ - ✅ Read calibration
83
+ - ✅ Device info (firmware / serial etc.)
84
+ - ✅ Check if device is connected
85
+ - ➡️ Peak load
86
+ - ➡️ Endurance
87
+ - ➡️ Rate of Force Development (RFD)
88
+ - ➡️ Repeaters
89
+ - ➡️ Critical Force
90
+
91
+ ## Development
92
+
93
+ ```bash
94
+ git clone https://github.com/Stevie-Ray/hangtime-grip-connect
95
+ cd hangtime-grip-connect
96
+ npm install
97
+ ```
98
+
92
99
  ## Credits
93
100
 
94
101
  A special thank you to:
95
102
 
96
103
  - [@CassimLadha](https://github.com/CassimLadha) for sharing insights on reading the Motherboards data.
97
- - [@donaldharvey](https://github.com/donaldharvey) for a valuable example on connecting to the motherboard.
98
- - [@ecstrema](https://github.com/ecstrema) for providing an example on how to play games with the entralpi.
104
+ - [@donaldharvey](https://github.com/donaldharvey) for a valuable example on connecting to the Motherboard.
105
+ - [@ecstrema](https://github.com/ecstrema) for providing [examples](https://github.com/ecstrema/entralpi-games) on how
106
+ to play games with the Entralpi.
107
+ - [@StuartLittlefair](https://github.com/StuartLittlefair) for his
108
+ [PyTindeq](https://github.com/StuartLittlefair/PyTindeq) implementation.
99
109
 
100
- ## Disclamer
110
+ ## Disclaimer
101
111
 
102
- THIS SOFTWARE IS NOT OFFICIALY SUPPORTED, SUPPLIED OR MAINTAINED BY THE DEVICE MANUFACTURER. BY USING THE SOFTWARE YOU
103
- ARE ACKNOWLEDGEING THIS AND UNDERSTAND THAT USING THIS SOFTWARE WILL INVALIDATE THE MANUFACTURERS WARRANTY.
112
+ THIS SOFTWARE IS NOT OFFICIALLY SUPPORTED, SUPPLIED OR MAINTAINED BY THE DEVICE MANUFACTURER. BY USING THE SOFTWARE YOU
113
+ ARE ACKNOWLEDGING THIS AND UNDERSTAND THAT USING THIS SOFTWARE WILL INVALIDATE THE MANUFACTURERS WARRANTY.
104
114
 
105
115
  ## License
106
116
 
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@hangtime/grip-connect",
3
- "version": "0.2.0",
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 Motherboard, Climbro, SmartBoard, Entralpi or Tindeq Progressor",
3
+ "version": "0.2.3",
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": {
7
7
  "build": "tsc --build"
8
8
  },
9
9
  "author": "Stevie-Ray Hartog <mail@stevie-ray.nl>",
10
10
  "license": "BSD-2-Clause",
11
- "devDependencies": {},
12
11
  "repository": {
13
12
  "type": "git",
14
13
  "url": "git+https://github.com/Stevie-Ray/hangtime-grip-connect.git"
@@ -25,5 +24,8 @@
25
24
  "bugs": {
26
25
  "url": "https://github.com/Stevie-Ray/hangtime-grip-connect/issues"
27
26
  },
28
- "homepage": "https://stevie-ray.github.io/hangtime-grip-connect/"
27
+ "homepage": "https://stevie-ray.github.io/hangtime-grip-connect/",
28
+ "dependencies": {
29
+ "@aksel/structjs": "^1.0.0"
30
+ }
29
31
  }
package/src/battery.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { write } from "./write";
2
2
  import { read } from "./read";
3
3
  import { isConnected } from "./is-connected";
4
- import { Motherboard, Tindeq } from "./devices";
5
- import { TindeqCommands } from "./commands";
4
+ import { Motherboard, Progressor } from "./devices";
5
+ import { ProgressorCommands } from "./commands";
6
6
  /**
7
7
  * Get Battery / Voltage information
8
8
  * @param board
@@ -12,8 +12,8 @@ export const battery = async (board) => {
12
12
  if (board.name === "Motherboard") {
13
13
  await read(Motherboard, "battery", "level", 250);
14
14
  }
15
- if (board.name === "Tindeq") {
16
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_BATT_VLTG), 250);
15
+ if (board.name && board.name.startsWith("Progressor")) {
16
+ await write(Progressor, "progressor", "tx", ProgressorCommands.GET_BATT_VLTG, 250);
17
17
  }
18
18
  }
19
19
  };
package/src/battery.ts CHANGED
@@ -2,8 +2,8 @@ import { Device } from "./devices/types"
2
2
  import { write } from "./write"
3
3
  import { read } from "./read"
4
4
  import { isConnected } from "./is-connected"
5
- import { Motherboard, Tindeq } from "./devices"
6
- import { TindeqCommands } from "./commands"
5
+ import { Motherboard, Progressor } from "./devices"
6
+ import { ProgressorCommands } from "./commands"
7
7
 
8
8
  /**
9
9
  * Get Battery / Voltage information
@@ -14,8 +14,8 @@ export const battery = async (board: Device): Promise<void> => {
14
14
  if (board.name === "Motherboard") {
15
15
  await read(Motherboard, "battery", "level", 250)
16
16
  }
17
- if (board.name === "Tindeq") {
18
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_BATT_VLTG), 250)
17
+ if (board.name && board.name.startsWith("Progressor")) {
18
+ await write(Progressor, "progressor", "tx", ProgressorCommands.GET_BATT_VLTG, 250)
19
19
  }
20
20
  }
21
21
  }
@@ -9,7 +9,7 @@ import { MotherboardCommands } from "./commands";
9
9
  export const calibration = async (board) => {
10
10
  if (isConnected(board)) {
11
11
  if (board.name === "Motherboard") {
12
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500);
12
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_CALIBRATION, 2500);
13
13
  }
14
14
  }
15
15
  };
@@ -11,7 +11,7 @@ import { MotherboardCommands } from "./commands"
11
11
  export const calibration = async (board: Device): Promise<void> => {
12
12
  if (isConnected(board)) {
13
13
  if (board.name === "Motherboard") {
14
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_CALIBRATION), 2500)
14
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_CALIBRATION, 2500)
15
15
  }
16
16
  }
17
17
  }
@@ -1,5 +1,5 @@
1
1
  export { ClimbroCommands } from "./climbro";
2
2
  export { EntralpiCommands } from "./entralpi";
3
3
  export { MotherboardCommands } from "./motherboard";
4
+ export { ProgressorCommands } from "./progressor";
4
5
  export { SmartBoardCommands } from "./smartboard";
5
- export { TindeqCommands } from "./tindeq";
@@ -1,5 +1,5 @@
1
1
  export { ClimbroCommands } from "./climbro";
2
2
  export { EntralpiCommands } from "./entralpi";
3
3
  export { MotherboardCommands } from "./motherboard";
4
+ export { ProgressorCommands } from "./progressor";
4
5
  export { SmartBoardCommands } from "./smartboard";
5
- export { TindeqCommands } from "./tindeq";
@@ -4,6 +4,6 @@ export { EntralpiCommands } from "./entralpi"
4
4
 
5
5
  export { MotherboardCommands } from "./motherboard"
6
6
 
7
- export { SmartBoardCommands } from "./smartboard"
7
+ export { ProgressorCommands } from "./progressor"
8
8
 
9
- export { TindeqCommands } from "./tindeq"
9
+ export { SmartBoardCommands } from "./smartboard"
@@ -3,8 +3,12 @@ import { Commands } from "../commands/types";
3
3
  * Warning:
4
4
  * Using other commands can seriously harm your device
5
5
  */
6
- export declare const TindeqCommands: Commands;
7
- export declare const NotificationTypes: {
6
+ export declare const ProgressorCommands: Commands;
7
+ /**
8
+ * The Progressor returns a Uint8Array.
9
+ * The first item [0] is the type of response it returns
10
+ */
11
+ export declare const ProgressorResponses: {
8
12
  COMMAND_RESPONSE: number;
9
13
  WEIGHT_MEASURE: number;
10
14
  LOW_BATTERY_WARNING: number;
@@ -2,7 +2,7 @@
2
2
  * Warning:
3
3
  * Using other commands can seriously harm your device
4
4
  */
5
- export const TindeqCommands = {
5
+ export const ProgressorCommands = {
6
6
  TARE_SCALE: "d", // 0x64,
7
7
  START_WEIGHT_MEAS: "e", // 0x65,
8
8
  STOP_WEIGHT_MEAS: "f", // 0x66,
@@ -10,13 +10,17 @@ export const TindeqCommands = {
10
10
  START_PEAK_RFD_MEAS_SERIES: "h", // 0x68,
11
11
  ADD_CALIB_POINT: "i", // 0x69,
12
12
  SAVE_CALIB: "j", // 0x6a,
13
- GET_APP_VERSION: "k", // 0x6b,
13
+ GET_FW_VERSION: "k", // 0x6b,
14
14
  GET_ERR_INFO: "l", // 0x6c,
15
15
  CLR_ERR_INFO: "m", // 0x6d,
16
16
  SLEEP: "n", // 0x6e,
17
17
  GET_BATT_VLTG: "o", // 0x6f,
18
18
  };
19
- export const NotificationTypes = {
19
+ /**
20
+ * The Progressor returns a Uint8Array.
21
+ * The first item [0] is the type of response it returns
22
+ */
23
+ export const ProgressorResponses = {
20
24
  COMMAND_RESPONSE: 0,
21
25
  WEIGHT_MEASURE: 1,
22
26
  LOW_BATTERY_WARNING: 2,
@@ -3,7 +3,7 @@ import { Commands } from "../commands/types"
3
3
  * Warning:
4
4
  * Using other commands can seriously harm your device
5
5
  */
6
- export const TindeqCommands: Commands = {
6
+ export const ProgressorCommands: Commands = {
7
7
  TARE_SCALE: "d", // 0x64,
8
8
  START_WEIGHT_MEAS: "e", // 0x65,
9
9
  STOP_WEIGHT_MEAS: "f", // 0x66,
@@ -11,14 +11,18 @@ export const TindeqCommands: Commands = {
11
11
  START_PEAK_RFD_MEAS_SERIES: "h", // 0x68,
12
12
  ADD_CALIB_POINT: "i", // 0x69,
13
13
  SAVE_CALIB: "j", // 0x6a,
14
- GET_APP_VERSION: "k", // 0x6b,
14
+ GET_FW_VERSION: "k", // 0x6b,
15
15
  GET_ERR_INFO: "l", // 0x6c,
16
16
  CLR_ERR_INFO: "m", // 0x6d,
17
17
  SLEEP: "n", // 0x6e,
18
18
  GET_BATT_VLTG: "o", // 0x6f,
19
19
  }
20
20
 
21
- export const NotificationTypes = {
21
+ /**
22
+ * The Progressor returns a Uint8Array.
23
+ * The first item [0] is the type of response it returns
24
+ */
25
+ export const ProgressorResponses = {
22
26
  COMMAND_RESPONSE: 0,
23
27
  WEIGHT_MEASURE: 1,
24
28
  LOW_BATTERY_WARNING: 2,
@@ -2,8 +2,8 @@ export interface Commands {
2
2
  START_WEIGHT_MEAS?: string;
3
3
  STOP_WEIGHT_MEAS?: string;
4
4
  SLEEP?: number | string;
5
- GET_TEXT?: string;
6
5
  GET_SERIAL?: string;
6
+ GET_TEXT?: string;
7
7
  DEBUG_STREAM?: string;
8
8
  GET_CALIBRATION?: string;
9
9
  TARE_SCALE?: string;
@@ -11,7 +11,7 @@ export interface Commands {
11
11
  START_PEAK_RFD_MEAS_SERIES?: string;
12
12
  ADD_CALIB_POINT?: string;
13
13
  SAVE_CALIB?: string;
14
- GET_APP_VERSION?: string;
14
+ GET_FW_VERSION?: string;
15
15
  GET_ERR_INFO?: string;
16
16
  CLR_ERR_INFO?: string;
17
17
  GET_BATT_VLTG?: string;
@@ -1,20 +1,20 @@
1
1
  export interface Commands {
2
- // Motherboard, Tindeq
2
+ // Motherboard, Progressor
3
3
  START_WEIGHT_MEAS?: string
4
4
  STOP_WEIGHT_MEAS?: string
5
5
  SLEEP?: number | string
6
- // Motherboard
7
- GET_TEXT?: string
8
6
  GET_SERIAL?: string
7
+ // Griptonite Motherboard
8
+ GET_TEXT?: string
9
9
  DEBUG_STREAM?: string
10
10
  GET_CALIBRATION?: string
11
- // Tindeq
11
+ // Tindeq Progressor
12
12
  TARE_SCALE?: string
13
13
  START_PEAK_RFD_MEAS?: string
14
14
  START_PEAK_RFD_MEAS_SERIES?: string
15
15
  ADD_CALIB_POINT?: string
16
16
  SAVE_CALIB?: string
17
- GET_APP_VERSION?: string
17
+ GET_FW_VERSION?: string
18
18
  GET_ERR_INFO?: string
19
19
  CLR_ERR_INFO?: string
20
20
  GET_BATT_VLTG?: string
package/src/connect.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { notifyCallback } from "./notify";
2
- import { handleMotherboardData } from "./data";
2
+ import { handleMotherboardData, handleProgressorData } from "./data";
3
3
  let server;
4
4
  const receiveBuffer = [];
5
5
  /**
@@ -50,8 +50,12 @@ const handleNotifications = (event, board) => {
50
50
  }
51
51
  }
52
52
  }
53
- else if (board.name === "Tindeq") {
54
- // TODO: handle Tindeq notify
53
+ else if (board.name && board.name.startsWith("Progressor")) {
54
+ if (value.buffer) {
55
+ const buffer = value.buffer;
56
+ const rawData = new DataView(buffer);
57
+ handleProgressorData(characteristic.uuid, rawData);
58
+ }
55
59
  }
56
60
  else {
57
61
  if (notifyCallback) {
@@ -122,9 +126,8 @@ export const connect = async (board, onSuccess) => {
122
126
  // setup filter list
123
127
  const filters = [];
124
128
  if (board.name) {
125
- filters.push({
126
- name: board.name,
127
- });
129
+ const filterName = board.name === "Progressor" ? { namePrefix: board.name } : { name: board.name };
130
+ filters.push(filterName);
128
131
  }
129
132
  if (board.companyId) {
130
133
  filters.push({
package/src/connect.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Device } from "./devices/types"
2
2
  import { notifyCallback } from "./notify"
3
- import { handleMotherboardData } from "./data"
3
+ import { handleMotherboardData, handleProgressorData } from "./data"
4
4
 
5
5
  let server: BluetoothRemoteGATTServer
6
6
  const receiveBuffer: number[] = []
@@ -23,6 +23,7 @@ const onDisconnected = (event: Event, board: Device): void => {
23
23
  const handleNotifications = (event: Event, board: Device): void => {
24
24
  const characteristic: BluetoothRemoteGATTCharacteristic = event.target as BluetoothRemoteGATTCharacteristic
25
25
  const value: DataView | undefined = characteristic.value
26
+
26
27
  if (value) {
27
28
  if (board.name === "Motherboard") {
28
29
  for (let i: number = 0; i < value.byteLength; i++) {
@@ -51,8 +52,12 @@ const handleNotifications = (event: Event, board: Device): void => {
51
52
  })
52
53
  }
53
54
  }
54
- } else if (board.name === "Tindeq") {
55
- // TODO: handle Tindeq notify
55
+ } else if (board.name && board.name.startsWith("Progressor")) {
56
+ if (value.buffer) {
57
+ const buffer: ArrayBuffer = value.buffer
58
+ const rawData: DataView = new DataView(buffer)
59
+ handleProgressorData(characteristic.uuid, rawData)
60
+ }
56
61
  } else {
57
62
  if (notifyCallback) {
58
63
  notifyCallback({ uuid: characteristic.uuid, value: value })
@@ -132,9 +137,8 @@ export const connect = async (board: Device, onSuccess: () => void): Promise<voi
132
137
  const filters = []
133
138
 
134
139
  if (board.name) {
135
- filters.push({
136
- name: board.name,
137
- })
140
+ const filterName = board.name === "Progressor" ? { namePrefix: board.name } : { name: board.name }
141
+ filters.push(filterName)
138
142
  }
139
143
  if (board.companyId) {
140
144
  filters.push({
package/src/data.d.ts CHANGED
@@ -6,3 +6,4 @@ export declare const CALIBRATION: never[][];
6
6
  * @param receivedData - Received data string
7
7
  */
8
8
  export declare const handleMotherboardData: (uuid: string, receivedData: string) => void;
9
+ export declare const handleProgressorData: (uuid: string, data: DataView) => void;
package/src/data.js CHANGED
@@ -1,4 +1,7 @@
1
1
  import { notifyCallback } from "./notify";
2
+ import { ProgressorCommands, ProgressorResponses } from "./commands/progressor";
3
+ import { lastWrite } from "./write";
4
+ import struct from "@aksel/structjs";
2
5
  const PACKET_LENGTH = 32;
3
6
  const NUM_SAMPLES = 3;
4
7
  export const CALIBRATION = [[], [], [], []];
@@ -107,3 +110,48 @@ export const handleMotherboardData = (uuid, receivedData) => {
107
110
  console.log(receivedData);
108
111
  }
109
112
  };
113
+ export const handleProgressorData = (uuid, data) => {
114
+ const tare = 0; // todo: add tare
115
+ const [kind] = struct("<bb").unpack(data.buffer.slice(0, 2));
116
+ if (kind === ProgressorResponses.WEIGHT_MEASURE) {
117
+ const iterable = struct("<fi").iter_unpack(data.buffer.slice(2));
118
+ for (const [weight] of iterable) {
119
+ if (notifyCallback) {
120
+ notifyCallback({
121
+ uuid: uuid,
122
+ value: {
123
+ massTotal: Math.max(-1000, weight - tare).toFixed(1),
124
+ },
125
+ });
126
+ }
127
+ }
128
+ }
129
+ else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
130
+ if (!lastWrite)
131
+ return;
132
+ let value = "";
133
+ if (lastWrite === ProgressorCommands.GET_BATT_VLTG) {
134
+ const vdd = new DataView(data.buffer, 2).getUint32(0, true);
135
+ value = `Battery level = ${vdd} [mV]`;
136
+ }
137
+ else if (lastWrite === ProgressorCommands.GET_FW_VERSION) {
138
+ value = new TextDecoder().decode(data.buffer.slice(2));
139
+ }
140
+ else if (lastWrite === ProgressorCommands.GET_ERR_INFO) {
141
+ value = new TextDecoder().decode(data.buffer.slice(2));
142
+ }
143
+ if (notifyCallback) {
144
+ notifyCallback({ uuid: uuid, value: value });
145
+ }
146
+ }
147
+ else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
148
+ if (notifyCallback) {
149
+ notifyCallback({ uuid: uuid, value: "low power warning" });
150
+ }
151
+ }
152
+ else {
153
+ if (notifyCallback) {
154
+ notifyCallback({ uuid: uuid, value: `unknown message kind ${kind}` });
155
+ }
156
+ }
157
+ };
package/src/data.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { notifyCallback } from "./notify"
2
+ import { ProgressorCommands, ProgressorResponses } from "./commands/progressor"
3
+ import { lastWrite } from "./write"
4
+ import struct from "@aksel/structjs"
2
5
 
3
6
  const PACKET_LENGTH: number = 32
4
7
  const NUM_SAMPLES: number = 3
@@ -131,3 +134,45 @@ export const handleMotherboardData = (uuid: string, receivedData: string): void
131
134
  console.log(receivedData)
132
135
  }
133
136
  }
137
+
138
+ export const handleProgressorData = (uuid: string, data: DataView): void => {
139
+ const tare: number = 0 // todo: add tare
140
+ const [kind] = struct("<bb").unpack(data.buffer.slice(0, 2))
141
+ if (kind === ProgressorResponses.WEIGHT_MEASURE) {
142
+ const iterable = struct("<fi").iter_unpack(data.buffer.slice(2))
143
+ for (const [weight] of iterable) {
144
+ if (notifyCallback) {
145
+ notifyCallback({
146
+ uuid: uuid,
147
+ value: {
148
+ massTotal: Math.max(-1000, weight - tare).toFixed(1),
149
+ },
150
+ })
151
+ }
152
+ }
153
+ } else if (kind === ProgressorResponses.COMMAND_RESPONSE) {
154
+ if (!lastWrite) return
155
+
156
+ let value: string = ""
157
+
158
+ if (lastWrite === ProgressorCommands.GET_BATT_VLTG) {
159
+ const vdd = new DataView(data.buffer, 2).getUint32(0, true)
160
+ value = `Battery level = ${vdd} [mV]`
161
+ } else if (lastWrite === ProgressorCommands.GET_FW_VERSION) {
162
+ value = new TextDecoder().decode(data.buffer.slice(2))
163
+ } else if (lastWrite === ProgressorCommands.GET_ERR_INFO) {
164
+ value = new TextDecoder().decode(data.buffer.slice(2))
165
+ }
166
+ if (notifyCallback) {
167
+ notifyCallback({ uuid: uuid, value: value })
168
+ }
169
+ } else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) {
170
+ if (notifyCallback) {
171
+ notifyCallback({ uuid: uuid, value: "low power warning" })
172
+ }
173
+ } else {
174
+ if (notifyCallback) {
175
+ notifyCallback({ uuid: uuid, value: `unknown message kind ${kind}` })
176
+ }
177
+ }
178
+ }
@@ -0,0 +1 @@
1
+ declare module "@aksel/structjs"
@@ -2,4 +2,4 @@ export { Climbro } from "./climbro";
2
2
  export { Entralpi } from "./entralpi";
3
3
  export { Motherboard } from "./motherboard";
4
4
  export { SmartBoard } from "./smartboard";
5
- export { Tindeq } from "./tindeq";
5
+ export { Progressor } from "./progressor";
@@ -2,4 +2,4 @@ export { Climbro } from "./climbro";
2
2
  export { Entralpi } from "./entralpi";
3
3
  export { Motherboard } from "./motherboard";
4
4
  export { SmartBoard } from "./smartboard";
5
- export { Tindeq } from "./tindeq";
5
+ export { Progressor } from "./progressor";
@@ -6,4 +6,4 @@ export { Motherboard } from "./motherboard"
6
6
 
7
7
  export { SmartBoard } from "./smartboard"
8
8
 
9
- export { Tindeq } from "./tindeq"
9
+ export { Progressor } from "./progressor"
@@ -0,0 +1,2 @@
1
+ import { Device } from "./types";
2
+ export declare const Progressor: Device;
@@ -1,20 +1,32 @@
1
- export const Tindeq = {
2
- name: "Tindeq",
1
+ export const Progressor = {
2
+ name: "Progressor",
3
3
  services: [
4
4
  {
5
5
  name: "Progressor Service",
6
6
  id: "progressor",
7
7
  uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57",
8
8
  characteristics: [
9
+ {
10
+ name: "Notify",
11
+ id: "rx",
12
+ uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
13
+ },
9
14
  {
10
15
  name: "Write",
11
16
  id: "tx",
12
17
  uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57",
13
18
  },
19
+ ],
20
+ },
21
+ {
22
+ name: "Secure DFU Service",
23
+ id: "dfu",
24
+ uuid: "0000fe59-0000-1000-8000-00805f9b34fb",
25
+ characteristics: [
14
26
  {
15
- name: "Notify",
16
- id: "rx",
17
- uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
27
+ name: "Buttonless DFU",
28
+ id: "dfu",
29
+ uuid: "8ec90003-f315-4f60-9fb8-838830daea50",
18
30
  },
19
31
  ],
20
32
  },
@@ -1,22 +1,34 @@
1
1
  import { Device } from "./types"
2
2
 
3
- export const Tindeq: Device = {
4
- name: "Tindeq",
3
+ export const Progressor: Device = {
4
+ name: "Progressor",
5
5
  services: [
6
6
  {
7
7
  name: "Progressor Service",
8
8
  id: "progressor",
9
9
  uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57",
10
10
  characteristics: [
11
+ {
12
+ name: "Notify",
13
+ id: "rx",
14
+ uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
15
+ },
11
16
  {
12
17
  name: "Write",
13
18
  id: "tx",
14
19
  uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57",
15
20
  },
21
+ ],
22
+ },
23
+ {
24
+ name: "Secure DFU Service",
25
+ id: "dfu",
26
+ uuid: "0000fe59-0000-1000-8000-00805f9b34fb",
27
+ characteristics: [
16
28
  {
17
- name: "Notify",
18
- id: "rx",
19
- uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57",
29
+ name: "Buttonless DFU",
30
+ id: "dfu",
31
+ uuid: "8ec90003-f315-4f60-9fb8-838830daea50",
20
32
  },
21
33
  ],
22
34
  },
package/src/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Climbro, Entralpi, Motherboard, SmartBoard, Tindeq } from "./devices/index";
1
+ export { Climbro, Entralpi, Motherboard, SmartBoard, Progressor } from "./devices/index";
2
2
  export { battery } from "./battery";
3
3
  export { calibration } from "./calibration";
4
4
  export { connect } from "./connect";
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Climbro, Entralpi, Motherboard, SmartBoard, Tindeq } from "./devices/index";
1
+ export { Climbro, Entralpi, Motherboard, SmartBoard, Progressor } from "./devices/index";
2
2
  export { battery } from "./battery";
3
3
  export { calibration } from "./calibration";
4
4
  export { connect } from "./connect";
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Climbro, Entralpi, Motherboard, SmartBoard, Tindeq } from "./devices/index"
1
+ export { Climbro, Entralpi, Motherboard, SmartBoard, Progressor } from "./devices/index"
2
2
 
3
3
  export { battery } from "./battery"
4
4
 
package/src/info.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { write } from "./write";
2
2
  import { read } from "./read";
3
3
  import { isConnected } from "./is-connected";
4
- import { Motherboard, Tindeq } from "./devices";
5
- import { MotherboardCommands, TindeqCommands } from "./commands";
4
+ import { Motherboard, Progressor } from "./devices";
5
+ import { MotherboardCommands, ProgressorCommands } from "./commands";
6
6
  /**
7
7
  * Get device information
8
8
  * @param board
@@ -13,11 +13,11 @@ export const info = async (board) => {
13
13
  await read(Motherboard, "device", "manufacturer", 250);
14
14
  await read(Motherboard, "device", "hardware", 250);
15
15
  await read(Motherboard, "device", "firmware", 250);
16
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_TEXT), 250);
17
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_SERIAL), 250);
16
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_TEXT, 250);
17
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_SERIAL, 250);
18
18
  }
19
- if (board.name === "Tindeq") {
20
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_APP_VERSION), 250);
19
+ if (board.name && board.name.startsWith("Progressor")) {
20
+ await write(Progressor, "progressor", "tx", ProgressorCommands.GET_FW_VERSION, 250);
21
21
  }
22
22
  }
23
23
  };
package/src/info.ts CHANGED
@@ -2,8 +2,8 @@ import { Device } from "./devices/types"
2
2
  import { write } from "./write"
3
3
  import { read } from "./read"
4
4
  import { isConnected } from "./is-connected"
5
- import { Motherboard, Tindeq } from "./devices"
6
- import { MotherboardCommands, TindeqCommands } from "./commands"
5
+ import { Motherboard, Progressor } from "./devices"
6
+ import { MotherboardCommands, ProgressorCommands } from "./commands"
7
7
 
8
8
  /**
9
9
  * Get device information
@@ -15,11 +15,11 @@ export const info = async (board: Device): Promise<void> => {
15
15
  await read(Motherboard, "device", "manufacturer", 250)
16
16
  await read(Motherboard, "device", "hardware", 250)
17
17
  await read(Motherboard, "device", "firmware", 250)
18
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_TEXT), 250)
19
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.GET_SERIAL), 250)
18
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_TEXT, 250)
19
+ await write(Motherboard, "uart", "tx", MotherboardCommands.GET_SERIAL, 250)
20
20
  }
21
- if (board.name === "Tindeq") {
22
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.GET_APP_VERSION), 250)
21
+ if (board.name && board.name.startsWith("Progressor")) {
22
+ await write(Progressor, "progressor", "tx", ProgressorCommands.GET_FW_VERSION, 250)
23
23
  }
24
24
  }
25
25
  }
package/src/stop.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { write } from "./write";
2
2
  import { isConnected } from "./is-connected";
3
- import { Motherboard, Tindeq } from "./devices";
4
- import { MotherboardCommands, TindeqCommands } from "./commands";
3
+ import { Motherboard, Progressor } from "./devices";
4
+ import { MotherboardCommands, ProgressorCommands } from "./commands";
5
5
  /**
6
6
  * read calibration
7
7
  * @param board
@@ -9,10 +9,10 @@ import { MotherboardCommands, TindeqCommands } from "./commands";
9
9
  export const stop = async (board) => {
10
10
  if (isConnected(board)) {
11
11
  if (board.name === "Motherboard") {
12
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.STOP_WEIGHT_MEAS), 0);
12
+ await write(Motherboard, "uart", "tx", MotherboardCommands.STOP_WEIGHT_MEAS, 0);
13
13
  }
14
- if (board.name === "Tindeq") {
15
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.STOP_WEIGHT_MEAS), 0);
14
+ if (board.name && board.name.startsWith("Progressor")) {
15
+ await write(Progressor, "progressor", "tx", ProgressorCommands.STOP_WEIGHT_MEAS, 0);
16
16
  }
17
17
  }
18
18
  };
package/src/stop.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Device } from "./devices/types"
2
2
  import { write } from "./write"
3
3
  import { isConnected } from "./is-connected"
4
- import { Motherboard, Tindeq } from "./devices"
5
- import { MotherboardCommands, TindeqCommands } from "./commands"
4
+ import { Motherboard, Progressor } from "./devices"
5
+ import { MotherboardCommands, ProgressorCommands } from "./commands"
6
6
 
7
7
  /**
8
8
  * read calibration
@@ -11,10 +11,10 @@ import { MotherboardCommands, TindeqCommands } from "./commands"
11
11
  export const stop = async (board: Device): Promise<void> => {
12
12
  if (isConnected(board)) {
13
13
  if (board.name === "Motherboard") {
14
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.STOP_WEIGHT_MEAS), 0)
14
+ await write(Motherboard, "uart", "tx", MotherboardCommands.STOP_WEIGHT_MEAS, 0)
15
15
  }
16
- if (board.name === "Tindeq") {
17
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.STOP_WEIGHT_MEAS), 0)
16
+ if (board.name && board.name.startsWith("Progressor")) {
17
+ await write(Progressor, "progressor", "tx", ProgressorCommands.STOP_WEIGHT_MEAS, 0)
18
18
  }
19
19
  }
20
20
  }
package/src/stream.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { isConnected } from "./is-connected";
2
2
  import { write } from "./write";
3
3
  import { stop } from "./stop";
4
- import { Motherboard, Tindeq } from "./devices";
5
- import { MotherboardCommands, TindeqCommands } from "./commands";
4
+ import { Motherboard, Progressor } from "./devices";
5
+ import { MotherboardCommands, ProgressorCommands } from "./commands";
6
6
  import { CALIBRATION } from "./data";
7
7
  import { calibration } from "./calibration";
8
8
  /**
@@ -17,18 +17,18 @@ export const stream = async (board, duration = 0) => {
17
17
  await calibration(Motherboard);
18
18
  }
19
19
  // start stream
20
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.START_WEIGHT_MEAS), duration);
20
+ await write(Motherboard, "uart", "tx", MotherboardCommands.START_WEIGHT_MEAS, duration);
21
21
  // end stream if duration is set
22
22
  if (duration !== 0) {
23
23
  await stop(Motherboard);
24
24
  }
25
25
  }
26
- if (board.name === "Tindeq") {
26
+ if (board.name && board.name.startsWith("Progressor")) {
27
27
  // start stream
28
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.START_WEIGHT_MEAS), duration);
28
+ await write(Progressor, "progressor", "tx", ProgressorCommands.START_WEIGHT_MEAS, duration);
29
29
  // end stream if duration is set
30
30
  if (duration !== 0) {
31
- await stop(Tindeq);
31
+ await stop(Progressor);
32
32
  }
33
33
  }
34
34
  }
package/src/stream.ts CHANGED
@@ -2,8 +2,8 @@ import { Device } from "./devices/types"
2
2
  import { isConnected } from "./is-connected"
3
3
  import { write } from "./write"
4
4
  import { stop } from "./stop"
5
- import { Motherboard, Tindeq } from "./devices"
6
- import { MotherboardCommands, TindeqCommands } from "./commands"
5
+ import { Motherboard, Progressor } from "./devices"
6
+ import { MotherboardCommands, ProgressorCommands } from "./commands"
7
7
  import { CALIBRATION } from "./data"
8
8
  import { calibration } from "./calibration"
9
9
 
@@ -19,18 +19,18 @@ export const stream = async (board: Device, duration: number = 0): Promise<void>
19
19
  await calibration(Motherboard)
20
20
  }
21
21
  // start stream
22
- await write(Motherboard, "uart", "tx", String(MotherboardCommands.START_WEIGHT_MEAS), duration)
22
+ await write(Motherboard, "uart", "tx", MotherboardCommands.START_WEIGHT_MEAS, duration)
23
23
  // end stream if duration is set
24
24
  if (duration !== 0) {
25
25
  await stop(Motherboard)
26
26
  }
27
27
  }
28
- if (board.name === "Tindeq") {
28
+ if (board.name && board.name.startsWith("Progressor")) {
29
29
  // start stream
30
- await write(Tindeq, "progressor", "tx", String(TindeqCommands.START_WEIGHT_MEAS), duration)
30
+ await write(Progressor, "progressor", "tx", ProgressorCommands.START_WEIGHT_MEAS, duration)
31
31
  // end stream if duration is set
32
32
  if (duration !== 0) {
33
- await stop(Tindeq)
33
+ await stop(Progressor)
34
34
  }
35
35
  }
36
36
  }
package/src/write.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Device } from "./devices/types";
2
+ export declare let lastWrite: string | null;
2
3
  /**
3
4
  * write
4
5
  * @param characteristic
5
6
  * @param message
6
7
  */
7
- export declare const write: (board: Device, serviceId: string, characteristicId: string, message: string, duration?: number) => Promise<void>;
8
+ export declare const write: (board: Device, serviceId: string, characteristicId: string, message: string | undefined, duration?: number) => Promise<void>;
package/src/write.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { isConnected } from "./is-connected";
2
2
  import { getCharacteristic } from "./characteristic";
3
+ export let lastWrite = null;
3
4
  /**
4
5
  * write
5
6
  * @param characteristic
@@ -8,12 +9,17 @@ import { getCharacteristic } from "./characteristic";
8
9
  export const write = (board, serviceId, characteristicId, message, duration = 0) => {
9
10
  return new Promise((resolve, reject) => {
10
11
  if (isConnected(board)) {
11
- const encoder = new TextEncoder();
12
+ if (!message)
13
+ return;
12
14
  const characteristic = getCharacteristic(board, serviceId, characteristicId);
13
15
  if (characteristic) {
16
+ const encoder = new TextEncoder();
14
17
  characteristic
15
18
  .writeValue(encoder.encode(message))
16
19
  .then(() => {
20
+ // update last written message
21
+ lastWrite = message;
22
+ // handle timeout
17
23
  if (duration !== 0) {
18
24
  setTimeout(() => {
19
25
  resolve();
package/src/write.ts CHANGED
@@ -2,6 +2,7 @@ import { Device } from "./devices/types"
2
2
  import { isConnected } from "./is-connected"
3
3
  import { getCharacteristic } from "./characteristic"
4
4
 
5
+ export let lastWrite: string | null = null
5
6
  /**
6
7
  * write
7
8
  * @param characteristic
@@ -11,19 +12,21 @@ export const write = (
11
12
  board: Device,
12
13
  serviceId: string,
13
14
  characteristicId: string,
14
- message: string,
15
+ message: string | undefined,
15
16
  duration: number = 0,
16
17
  ): Promise<void> => {
17
18
  return new Promise((resolve, reject) => {
18
19
  if (isConnected(board)) {
19
- const encoder = new TextEncoder()
20
-
20
+ if (!message) return
21
21
  const characteristic = getCharacteristic(board, serviceId, characteristicId)
22
-
23
22
  if (characteristic) {
23
+ const encoder = new TextEncoder()
24
24
  characteristic
25
25
  .writeValue(encoder.encode(message))
26
26
  .then(() => {
27
+ // update last written message
28
+ lastWrite = message
29
+ // handle timeout
27
30
  if (duration !== 0) {
28
31
  setTimeout(() => {
29
32
  resolve()
@@ -1,2 +0,0 @@
1
- import { Device } from "./types";
2
- export declare const Tindeq: Device;