@hangtime/grip-connect-react-native
Version:
React Native module for Grip Connect devices
119 lines (101 loc) • 3.87 kB
text/typescript
import { BleManager, Device } from "react-native-ble-plx"
import { WHC06 as WHC06Base } from "@hangtime/grip-connect"
import { Buffer } from "buffer"
/**
* Represents a Weiheng - WH-C06 (or MAT Muscle Meter) device.
* To use this device enable: `chrome://flags/#enable-experimental-web-platform-features`.
* {@link https://googlechrome.github.io/samples/web-bluetooth/scan.html| Web Bluetooth}
* {@link https://weihengmanufacturer.com}
*/
export class WHC06 extends WHC06Base {
manager: BleManager
device?: Device
constructor() {
super()
this.manager = new BleManager()
}
private base64ToHex(base64: string): string {
const binary = Buffer.from(base64, "base64").toString("binary")
return Array.from(binary)
.map((char) => char.charCodeAt(0).toString(16).padStart(2, "0"))
.join("")
}
private parseWeightData(manufacturerData: string | null): number {
if (!manufacturerData) return 0
try {
const hexData = this.base64ToHex(manufacturerData)
const weightHex = hexData.substring(24, 28)
return parseInt(weightHex, 16) / 100
} catch (error) {
console.error("Error parsing weight data:", error)
return 0
}
}
override connect = async (
onSuccess: () => void = () => console.log("Connected successfully"),
onError: (error: Error) => void = (error) => console.error(error),
): Promise<void> => {
try {
let hasReportedSuccess = false
this.manager.startDeviceScan(
null,
{
allowDuplicates: true, // By allowing duplicates scanning records are received more frequently [iOS only]
scanMode: 2, // Scan mode for Bluetooth LE scan [Android only]
callbackType: 1, // Scan callback type for Bluetooth LE scan [Android only]
},
(error, scannedDevice) => {
if (error) {
onError(error)
return
}
if (!scannedDevice || (scannedDevice.localName !== "IF_B7" && scannedDevice.name !== "IF_B7")) {
return
}
// Check if we received data
const manufacturerData = scannedDevice.manufacturerData
if (!manufacturerData) {
return
}
// Update timestamp
this.updateTimestamp()
// Device has no services / characteristics, so we directly call onSuccess
if (!hasReportedSuccess) {
hasReportedSuccess = true
onSuccess()
}
// Handle received data
this.currentSamplesPerPacket = 1
this.recordPacketReceived()
const receivedTime: number = Date.now()
const receivedData = this.parseWeightData(manufacturerData)
// Tare correction
const numericData = receivedData - this.applyTare(receivedData) * -1
const currentMassTotal = Math.max(-1000, numericData)
// Update session stats before building packet
this.peak = Math.max(this.peak, numericData)
this.min = Math.min(this.min, Math.max(-1000, numericData))
this.sum += currentMassTotal
this.dataPointCount++
this.mean = this.sum / this.dataPointCount
// Add data to downloadable Array
this.downloadPackets.push(
this.buildDownloadPacket(currentMassTotal, [numericData], {
timestamp: receivedTime,
sampleIndex: this.dataPointCount,
}),
)
// Check if device is being used
this.activityCheck(numericData)
// Notify with weight data
this.notifyCallback(this.buildForceMeasurement(currentMassTotal))
},
)
} catch (error) {
onError(error as Error)
}
}
override download = async (): Promise<void> => {
throw new Error("Download is not supported on React Native")
}
}