UNPKG

@hangtime/grip-connect

Version:

Griptonite Motherboard, Tindeq Progressor, PitchSix Force Board, WHC-06, Entralpi, Climbro, mySmartBoard: Bluetooth API Force-Sensing strength analysis for climbers

216 lines 9.63 kB
import { Device } from "../device.model.js"; /** * Progressor responses */ var ProgressorResponses; (function (ProgressorResponses) { /** * Response received after sending a command to the device. * This could include acknowledgment or specific data related to the command sent. */ ProgressorResponses[ProgressorResponses["COMMAND_RESPONSE"] = 0] = "COMMAND_RESPONSE"; /** * Data representing a weight measurement from the device. * Typically used for tracking load or force applied. */ ProgressorResponses[ProgressorResponses["WEIGHT_MEASURE"] = 1] = "WEIGHT_MEASURE"; /** * Peak rate of force development (RFD) measurement. * This measures how quickly the force is applied over time. */ ProgressorResponses[ProgressorResponses["PEAK_RFD_MEAS"] = 2] = "PEAK_RFD_MEAS"; /** * Series of peak rate of force development (RFD) measurements. * This could be used for analyzing force trends over multiple data points. */ ProgressorResponses[ProgressorResponses["PEAK_RFD_MEAS_SERIES"] = 3] = "PEAK_RFD_MEAS_SERIES"; /** * Low battery warning from the device. * Indicates that the battery level is below a critical threshold. */ ProgressorResponses[ProgressorResponses["LOW_BATTERY_WARNING"] = 4] = "LOW_BATTERY_WARNING"; })(ProgressorResponses || (ProgressorResponses = {})); /** * Represents a Tindeq Progressor device. * {@link https://tindeq.com} */ export class Progressor extends Device { constructor() { super({ filters: [{ namePrefix: "Progressor" }], services: [ { name: "Progressor Service", id: "progressor", uuid: "7e4e1701-1ea6-40c9-9dcc-13d34ffead57", characteristics: [ { name: "Notify", id: "rx", uuid: "7e4e1702-1ea6-40c9-9dcc-13d34ffead57", }, { name: "Write", id: "tx", uuid: "7e4e1703-1ea6-40c9-9dcc-13d34ffead57", }, ], }, { name: "Nordic Device Firmware Update (DFU) Service", id: "dfu", uuid: "0000fe59-0000-1000-8000-00805f9b34fb", characteristics: [ { name: "Buttonless DFU", id: "dfu", uuid: "8ec90003-f315-4f60-9fb8-838830daea50", }, ], }, ], commands: { TARE_SCALE: "d", // 0x64 START_WEIGHT_MEAS: "e", // 0x65 STOP_WEIGHT_MEAS: "f", // 0x66 START_PEAK_RFD_MEAS: "g", // 0x67 START_PEAK_RFD_MEAS_SERIES: "h", // 0x68 ADD_CALIB_POINT: "i", // 0x69 SAVE_CALIB: "j", // 0x6a GET_FW_VERSION: "k", // 0x6b GET_ERR_INFO: "l", // 0x6c CLR_ERR_INFO: "m", // 0x6d SLEEP: "n", // 0x6e GET_BATT_VLTG: "o", // 0x6f }, }); } /** * Retrieves battery or voltage information from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the battery or voltage information, */ battery = async () => { let response = undefined; await this.write("progressor", "tx", this.commands.GET_BATT_VLTG, 250, (data) => { response = data; }); return response; }; /** * Retrieves firmware version from the device. * @returns {Promise<string>} A Promise that resolves with the firmware version, */ firmware = async () => { let response = undefined; await this.write("progressor", "tx", this.commands.GET_FW_VERSION, 250, (data) => { response = data; }); return response; }; /** * Handles data received from the device, processes weight measurements, * and updates mass data including maximum and average values. * It also handles command responses for retrieving device information. * * @param {DataView} value - The notification event. */ handleNotifications = (value) => { if (value) { // Update timestamp this.updateTimestamp(); if (value.buffer) { const receivedTime = Date.now(); // Read the first byte of the buffer to determine the kind of message const kind = value.getInt8(0); // Check if the message is a weight measurement if (kind === ProgressorResponses.WEIGHT_MEASURE) { // Start parsing data from the 3rd byte (index 2) let offset = 2; // Continue parsing while there's data left in the buffer while (offset < value.byteLength) { // Read a 32-bit float (4 bytes) for the weight, using little-endian const weight = value.getFloat32(offset, true); // Move the offset by 4 bytes offset += 4; // Read a 32-bit integer (4 bytes) for the seconds, using little-endian const seconds = value.getInt32(offset, true); // Move the offset by 4 bytes offset += 4; // Check if both weight and seconds are valid numbers if (!isNaN(weight) && !isNaN(seconds)) { // Tare correction const numericData = weight - this.applyTare(weight); // Add data to downloadable Array this.downloadPackets.push({ received: receivedTime, sampleNum: seconds, battRaw: 0, samples: [weight], masses: [numericData], }); // Check for max weight this.massMax = Math.max(Number(this.massMax), Number(numericData)).toFixed(1); // Update running sum and count const currentMassTotal = Math.max(-1000, Number(numericData)); this.massTotalSum += currentMassTotal; this.dataPointCount++; // Calculate the average dynamically this.massAverage = (this.massTotalSum / this.dataPointCount).toFixed(1); // Check if device is being used this.activityCheck(numericData); this.notifyCallback({ massMax: this.massMax, massAverage: this.massAverage, massTotal: Math.max(-1000, numericData).toFixed(1), }); } } } else if (kind === ProgressorResponses.COMMAND_RESPONSE) { if (!this.writeLast) return; let output = ""; if (this.writeLast === this.commands.GET_BATT_VLTG) { output = new DataView(value.buffer, 2).getUint32(0, true).toString(); } else if (this.writeLast === this.commands.GET_FW_VERSION) { output = new TextDecoder().decode(new Uint8Array(value.buffer).slice(2)); } else if (this.writeLast === this.commands.GET_ERR_INFO) { output = new TextDecoder().decode(new Uint8Array(value.buffer.slice(2))); } this.writeCallback(output); } else if (kind === ProgressorResponses.LOW_BATTERY_WARNING) { console.warn("⚠️ Low power detected. Please consider connecting to a power source."); } else { throw new Error(`Unknown message kind detected: ${kind}`); } } } }; /** * Stops the data stream on the specified device. * @returns {Promise<void>} A promise that resolves when the stream is stopped. */ stop = async () => { await this.write("progressor", "tx", this.commands.STOP_WEIGHT_MEAS, 0); }; /** * Starts streaming data from the specified device. * @param {number} [duration=0] - The duration of the stream in milliseconds. If set to 0, stream will continue indefinitely. * @returns {Promise<void>} A promise that resolves when the streaming operation is completed. */ stream = async (duration = 0) => { // Reset download packets this.downloadPackets.length = 0; // Start streaming data await this.write("progressor", "tx", this.commands.START_WEIGHT_MEAS, duration); // Stop streaming if duration is set if (duration !== 0) { await this.stop(); } }; } //# sourceMappingURL=progressor.model.js.map