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

249 lines 10.4 kB
import { Device } from "../device.model.js"; /** * Climbro protocol constants */ var ClimbroResponses; (function (ClimbroResponses) { /** * 240 - Battery data marker */ ClimbroResponses[ClimbroResponses["BAT_DAT"] = 240] = "BAT_DAT"; /** * 245 - Sensor data marker */ ClimbroResponses[ClimbroResponses["SENS_DAT"] = 245] = "SENS_DAT"; /** * 246 - 36kg value */ ClimbroResponses[ClimbroResponses["DAT_36KG"] = 246] = "DAT_36KG"; })(ClimbroResponses || (ClimbroResponses = {})); /** * Represents a Climbro device. * {@link https://climbro.com/} */ export class Climbro extends Device { /** * Battery constants */ static minBatteryDisc = 112; static maxBatteryDisc = 230; static batRangeDisc = this.maxBatteryDisc - this.minBatteryDisc; static batLevelCoef = 100 / this.batRangeDisc; /** * Synchronization flag used to track the current data type being processed. * Set to BAT_DAT when processing battery data, SENS_DAT when processing sensor data. * @type {number} * @private */ flagSynchro = 0; /** * Current battery level percentage calculated from the device's battery voltage. * @type {number} * @private */ batteryLevel = 0; constructor() { super({ filters: [{ namePrefix: "Climbro" }], services: [ { name: "UART Transparent Service", id: "uart", uuid: "49535343-fe7d-4ae5-8fa9-9fafd205e455", characteristics: [ { name: "UART Tramsmit (we set as rx)", id: "rx", uuid: "49535343-1e4d-4bd9-ba61-23c647249616", }, { name: "UART Receive (we set as tx)", id: "tx", uuid: "49535343-8841-43f4-a8d4-ecbe34729bb3", }, { name: "Transparent Control Point", id: "tcp", uuid: "49535343-4c8a-39b3-2f49-511cff073b7e", }, ], }, { name: "Device Information", id: "device", uuid: "0000180a-0000-1000-8000-00805f9b34fb", characteristics: [ { name: "System ID", id: "system", uuid: "00002a23-0000-1000-8000-00805f9b34fb", }, { name: "Model Number String", id: "model", // RN487x uuid: "00002a24-0000-1000-8000-00805f9b34fb", }, // { // name: "Serial Number String (Blocked)", // id: "serial", // uuid: "00002a25-0000-1000-8000-00805f9b34fb", // }, { name: "Firmware Revision String", id: "firmware", uuid: "00002a26-0000-1000-8000-00805f9b34fb", }, { name: "Hardware Revision String", id: "hardware", // 5505 102_BLDK3 uuid: "00002a27-0000-1000-8000-00805f9b34fb", }, { name: "Software Revision String", id: "software", // 1.30 uuid: "00002a28-0000-1000-8000-00805f9b34fb", }, { name: "Manufacturer Name String", id: "manufacturer", // Microchip uuid: "00002a29-0000-1000-8000-00805f9b34fb", }, { name: "IEEE 11073-20601 Regulatory Certification Data List", id: "certification", uuid: "00002a2a-0000-1000-8000-00805f9b34fb", }, ], }, ], }); } /** * Retrieves battery level from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the battery level. */ battery = async () => { // Battery level is continuously updated via notifications return this.batteryLevel.toString(); }; /** * Retrieves hardware version from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the hardware version. */ hardware = async () => { return await this.read("device", "hardware", 250); }; /** * Retrieves manufacturer information from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the manufacturer information. */ manufacturer = async () => { return await this.read("device", "manufacturer", 250); }; /** * Retrieves model number from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the model number. */ model = async () => { return await this.read("device", "model", 250); }; /** * Retrieves software version from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the software version. */ software = async () => { return await this.read("device", "software", 250); }; /** * Retrieves system id from the device. * @returns {Promise<string | undefined>} A Promise that resolves with the system id. */ system = async () => { return await this.read("device", "system", 250); }; /** * Handles data received from the device, processes force measurements and battery data * according to the Climbro protocol. * * @param {DataView} value - The notification event. */ handleNotifications = (value) => { if (value) { this.updateTimestamp(); if (value.buffer) { const buffer = new Uint8Array(value.buffer); const byteCount = buffer.length; let flagSynchro = this.flagSynchro; let forceCount = 0; for (let i = 0; i < byteCount; i++) { const b = buffer[i]; if (b === ClimbroResponses.BAT_DAT) { flagSynchro = ClimbroResponses.BAT_DAT; continue; } if (b === ClimbroResponses.SENS_DAT) { flagSynchro = ClimbroResponses.SENS_DAT; continue; } if (b === ClimbroResponses.DAT_36KG) { // 36kg sentinel: fall through to sync/force handling } if (flagSynchro === ClimbroResponses.BAT_DAT) continue; if (flagSynchro === ClimbroResponses.SENS_DAT) { forceCount++; } } this.currentSamplesPerPacket = forceCount; this.recordPacketReceived(); const receivedTime = Date.now(); for (let i = 0; i < byteCount; i++) { let signalValue = buffer[i]; // Check for battery data marker if (signalValue === ClimbroResponses.BAT_DAT) { this.flagSynchro = ClimbroResponses.BAT_DAT; continue; } // Check for sensor data marker if (signalValue === ClimbroResponses.SENS_DAT) { this.flagSynchro = ClimbroResponses.SENS_DAT; continue; } // Check if signal is the reserved word for 36kg and convert it if (signalValue === ClimbroResponses.DAT_36KG) { signalValue = 36; } // Process battery level signal if (this.flagSynchro === ClimbroResponses.BAT_DAT) { this.batteryLevel = Climbro.batLevelCoef * (signalValue - Climbro.minBatteryDisc); continue; } // Process force signal if (this.flagSynchro === ClimbroResponses.SENS_DAT) { // Process force data inline const forceValue = signalValue; const numericData = forceValue - this.applyTare(forceValue); const currentMassTotal = Math.max(-1000, Number(numericData)); // Update session stats before building packet (so packet reflects this sample) this.peak = Math.max(this.peak, Number(numericData)); this.min = Math.min(this.min, Math.max(-1000, Number(numericData))); this.sum += currentMassTotal; this.dataPointCount++; this.mean = this.sum / this.dataPointCount; // Add data to downloadable array this.downloadPackets.push(this.buildDownloadPacket(currentMassTotal, [forceValue], { timestamp: receivedTime, battRaw: this.batteryLevel, sampleIndex: this.dataPointCount, })); // Check if device is being used this.activityCheck(numericData); this.notifyCallback(this.buildForceMeasurement(currentMassTotal)); continue; } } } } }; } //# sourceMappingURL=climbro.model.js.map