Add BLE BPL scale native module and BMI integration
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -75,8 +75,6 @@ android/keystores/debug.keystore
|
||||
# Turborepo
|
||||
.turbo/
|
||||
|
||||
# generated by bob
|
||||
lib/
|
||||
|
||||
# React Native Codegen
|
||||
ios/generated
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
</manifest>
|
||||
|
||||
105
android/src/main/java/com/bplscale/BodyFat.kt
Normal file
105
android/src/main/java/com/bplscale/BodyFat.kt
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.bplscale
|
||||
|
||||
/**
|
||||
* BodyFat calculation wrapper
|
||||
* Loads the native libbodyfat.so library
|
||||
*/
|
||||
object BodyFat {
|
||||
init {
|
||||
System.loadLibrary("bodyfat") // libbodyfat.so
|
||||
}
|
||||
|
||||
external fun getVersion(): String
|
||||
external fun getAuthToken(): String
|
||||
|
||||
/**
|
||||
* Calculate body fat percentage
|
||||
* @param sex Boolean - true for female, false for male
|
||||
* @param age Float - user age
|
||||
* @param cm Float - height in cm
|
||||
* @param wtkg Float - weight in kg
|
||||
* @param imp Float - body impedance (Ohm)
|
||||
* @param althlevel Int - athletic level (keep 0)
|
||||
* @return Float - body fat percentage
|
||||
*/
|
||||
external fun getFat(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate total body water (TBW)
|
||||
*/
|
||||
external fun getTbw(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate muscle mass percentage
|
||||
*/
|
||||
external fun getMus(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate bone mass percentage
|
||||
*/
|
||||
external fun getBone(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate basal metabolic rate (BMR) in Kcal
|
||||
*/
|
||||
external fun getKcal(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate visceral fat
|
||||
*/
|
||||
external fun getVfat(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate body age
|
||||
*/
|
||||
external fun getBage(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate best weight
|
||||
*/
|
||||
external fun getBestWeight(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate BMI (Body Mass Index)
|
||||
*/
|
||||
external fun getBMI(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate protein percentage
|
||||
*/
|
||||
external fun getProtein(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate weight without fat (lean body mass)
|
||||
*/
|
||||
external fun getWeightWithoutFat(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate obesity rate
|
||||
* obeserate = (actual weight - best weight) / best weight
|
||||
*
|
||||
* Evaluation:
|
||||
* < 10.0 - Healthy
|
||||
* < 20.0 - Over weight
|
||||
* < 30.0 - Mildly obese
|
||||
* < 50.0 - Moderate obesity
|
||||
* > 50 - Severe obesity
|
||||
*/
|
||||
external fun getObeseRate(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate body score (0-100)
|
||||
*/
|
||||
external fun getScore(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
|
||||
/**
|
||||
* Calculate body shape type
|
||||
*
|
||||
* < 0.5 - Lean type
|
||||
* < 1.5 - Normal type
|
||||
* < 2.5 - Over weight type
|
||||
* < 3.5 - Over fat
|
||||
* > 3.5 - Obese
|
||||
*/
|
||||
external fun getBodyShape(sex: Boolean, age: Float, cm: Float, wtkg: Float, imp: Float, althlevel: Int): Float
|
||||
}
|
||||
@@ -1,23 +1,136 @@
|
||||
package com.bplscale
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothManager
|
||||
import android.bluetooth.le.BluetoothLeScanner
|
||||
import android.bluetooth.le.ScanCallback
|
||||
import android.bluetooth.le.ScanResult
|
||||
import android.bluetooth.le.ScanSettings
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
|
||||
@ReactModule(name = BplScaleModule.NAME)
|
||||
class BplScaleModule(reactContext: ReactApplicationContext) :
|
||||
NativeBplScaleSpec(reactContext) {
|
||||
class BplScaleModule(
|
||||
reactContext: ReactApplicationContext
|
||||
) : ReactContextBaseJavaModule(reactContext) {
|
||||
|
||||
override fun getName(): String {
|
||||
return NAME
|
||||
}
|
||||
private val bluetoothAdapter: BluetoothAdapter? by lazy {
|
||||
val manager =
|
||||
reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||
manager.adapter
|
||||
}
|
||||
|
||||
// Example method
|
||||
// See https://reactnative.dev/docs/native-modules-android
|
||||
override fun multiply(a: Double, b: Double): Double {
|
||||
return a * b
|
||||
}
|
||||
private var scanner: BluetoothLeScanner? = null
|
||||
private var scanCallback: ScanCallback? = null
|
||||
|
||||
companion object {
|
||||
const val NAME = "BplScale"
|
||||
}
|
||||
override fun getName(): String = NAME
|
||||
|
||||
data class ScaleData(val weight: Float, val impedance: Float)
|
||||
|
||||
/**
|
||||
* Scan BLE devices and return weight and impedance
|
||||
*/
|
||||
@ReactMethod
|
||||
fun scanDevices(scanDurationMs: Int, promise: Promise) {
|
||||
val adapter = bluetoothAdapter
|
||||
if (adapter == null || !adapter.isEnabled) {
|
||||
promise.reject("BLE_DISABLED", "Bluetooth is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
scanner = adapter.bluetoothLeScanner
|
||||
if (scanner == null) {
|
||||
promise.reject("BLE_ERROR", "BLE scanner unavailable")
|
||||
return
|
||||
}
|
||||
|
||||
val devicesMap = HashMap<String, WritableMap>()
|
||||
val resultArray = Arguments.createArray()
|
||||
|
||||
val settings = ScanSettings.Builder()
|
||||
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
|
||||
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
|
||||
.build()
|
||||
|
||||
scanCallback = object : ScanCallback() {
|
||||
override fun onScanResult(callbackType: Int, result: ScanResult) {
|
||||
processDevice(result, devicesMap)
|
||||
}
|
||||
|
||||
override fun onBatchScanResults(results: MutableList<ScanResult>) {
|
||||
results.forEach { processDevice(it, devicesMap) }
|
||||
}
|
||||
|
||||
override fun onScanFailed(errorCode: Int) {
|
||||
promise.reject("BLE_SCAN_FAILED", "Scan failed with code $errorCode")
|
||||
}
|
||||
|
||||
private fun processDevice(result: ScanResult, map: HashMap<String, WritableMap>) {
|
||||
val device = result.device ?: return
|
||||
val address = device.address ?: return
|
||||
val scanRecord = result.scanRecord?.bytes ?: return
|
||||
|
||||
val scaleData = parseScaleData(scanRecord) ?: return
|
||||
|
||||
if (!map.containsKey(address)) {
|
||||
val deviceMap = Arguments.createMap()
|
||||
deviceMap.putString("name", device.name ?: "")
|
||||
deviceMap.putString("address", address)
|
||||
deviceMap.putDouble("weight", scaleData.weight.toDouble())
|
||||
deviceMap.putDouble("impedance", scaleData.impedance.toDouble())
|
||||
map[address] = deviceMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scanner?.startScan(null, settings, scanCallback)
|
||||
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
scanner?.stopScan(scanCallback)
|
||||
devicesMap.values.forEach { resultArray.pushMap(it) }
|
||||
promise.resolve(resultArray)
|
||||
}, scanDurationMs.toLong())
|
||||
}
|
||||
|
||||
// ────── Parse weight and impedance from advertising packet ──────
|
||||
private fun parseScaleData(scanRecord: ByteArray): ScaleData? {
|
||||
var i = 0
|
||||
while (i < scanRecord.size - 2) {
|
||||
val len = scanRecord[i].toInt() and 0xFF
|
||||
if (len == 0) break
|
||||
val type = scanRecord[i + 1].toInt() and 0xFF
|
||||
if (type == 0xFF && i + len <= scanRecord.size) {
|
||||
val mfg = scanRecord.copyOfRange(i + 2, i + 1 + len)
|
||||
if (mfg.size >= 15) {
|
||||
val flag = mfg[0].toInt() and 0xFF
|
||||
return when (flag) {
|
||||
0xC0 -> parseV30Protocol(mfg)
|
||||
0xCA -> parseV21Protocol(mfg)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
i += len
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun parseV30Protocol(data: ByteArray): ScaleData {
|
||||
val weight = ((data[2].toInt() and 0xFF) shl 8 or (data[3].toInt() and 0xFF)) / 10f
|
||||
val impedance = ((data[4].toInt() and 0xFF) shl 8 or (data[5].toInt() and 0xFF)) / 10f
|
||||
return ScaleData(weight, impedance)
|
||||
}
|
||||
|
||||
private fun parseV21Protocol(data: ByteArray): ScaleData {
|
||||
val weight = ((data[10].toInt() and 0xFF) shl 8 or (data[11].toInt() and 0xFF)) / 10f
|
||||
val impedance = ((data[12].toInt() and 0xFF) shl 8 or (data[13].toInt() and 0xFF)) / 10f
|
||||
return ScaleData(weight, impedance)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "BplScale"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,19 @@
|
||||
package com.bplscale
|
||||
|
||||
import com.facebook.react.BaseReactPackage
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.module.model.ReactModuleInfo
|
||||
import com.facebook.react.module.model.ReactModuleInfoProvider
|
||||
import java.util.HashMap
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
|
||||
class BplScalePackage : BaseReactPackage() {
|
||||
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
||||
return if (name == BplScaleModule.NAME) {
|
||||
BplScaleModule(reactContext)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
class BplScalePackage : ReactPackage {
|
||||
|
||||
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
||||
return ReactModuleInfoProvider {
|
||||
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
||||
moduleInfos[BplScaleModule.NAME] = ReactModuleInfo(
|
||||
BplScaleModule.NAME,
|
||||
BplScaleModule.NAME,
|
||||
false, // canOverrideExistingModule
|
||||
false, // needsEagerInit
|
||||
false, // isCxxModule
|
||||
true // isTurboModule
|
||||
)
|
||||
moduleInfos
|
||||
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
||||
// Return a list with your BplScaleModule
|
||||
return listOf(BplScaleModule(reactContext))
|
||||
}
|
||||
|
||||
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
||||
// No custom view managers for this module
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
android/src/main/jniLibs/arm64-v8a/libbodyfat.so
Normal file
BIN
android/src/main/jniLibs/arm64-v8a/libbodyfat.so
Normal file
Binary file not shown.
BIN
android/src/main/jniLibs/armeabi-v7a/libbodyfat.so
Normal file
BIN
android/src/main/jniLibs/armeabi-v7a/libbodyfat.so
Normal file
Binary file not shown.
BIN
android/src/main/jniLibs/x86/libbodyfat.so
Normal file
BIN
android/src/main/jniLibs/x86/libbodyfat.so
Normal file
Binary file not shown.
BIN
android/src/main/jniLibs/x86_64/libbodyfat.so
Normal file
BIN
android/src/main/jniLibs/x86_64/libbodyfat.so
Normal file
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
|
||||
pluginManagement { includeBuild("../../node_modules/@react-native/gradle-plugin") }
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
||||
rootProject.name = 'bplscale.example'
|
||||
include ':app'
|
||||
includeBuild('../node_modules/@react-native/gradle-plugin')
|
||||
includeBuild('../../node_modules/@react-native/gradle-plugin')
|
||||
|
||||
8978
package-lock.json
generated
Normal file
8978
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -2,16 +2,8 @@
|
||||
"name": "react-native-bpl-scale",
|
||||
"version": "0.1.0",
|
||||
"description": "React Native Bluetooth BPL weighing scale library",
|
||||
"main": "./lib/module/index.js",
|
||||
"types": "./lib/typescript/src/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"source": "./src/index.tsx",
|
||||
"types": "./lib/typescript/src/index.d.ts",
|
||||
"default": "./lib/module/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "lib/module/index.js",
|
||||
"types": "lib/typescript/src/index.d.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"lib",
|
||||
@@ -32,7 +24,7 @@
|
||||
"!**/.*"
|
||||
],
|
||||
"scripts": {
|
||||
"example": "yarn workspace react-native-bpl-scale-example",
|
||||
"example": "npm --prefix example run android",
|
||||
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
||||
"prepare": "bob build",
|
||||
"typecheck": "tsc"
|
||||
@@ -46,7 +38,7 @@
|
||||
"type": "git",
|
||||
"url": "git+.git"
|
||||
},
|
||||
"author": " <> ()",
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "/issues"
|
||||
@@ -62,7 +54,6 @@
|
||||
"react": "19.2.0",
|
||||
"react-native": "0.83.0",
|
||||
"react-native-builder-bob": "^0.40.13",
|
||||
"turbo": "^2.5.6",
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
27
react-native-bpl-scale/android/build.gradle
Normal file
27
react-native-bpl-scale/android/build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
apply plugin: "com.android.library"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
|
||||
android {
|
||||
namespace "com.bplscale"
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk 24
|
||||
targetSdk 34
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
jniLibs.srcDirs = ['src/main/jniLibs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.facebook.react:react-android"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<manifest package="com.bplscale" />
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.bplscale
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.module.annotations.ReactModule
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule
|
||||
|
||||
@ReactModule(name = BPLScaleModule.NAME)
|
||||
class BPLScaleModule(
|
||||
reactContext: ReactApplicationContext
|
||||
) : NativeBPLScaleSpec(reactContext), TurboModule {
|
||||
|
||||
override fun hello(name: String): String {
|
||||
return "Hello $name from Kotlin TurboModule"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "BPLScale"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.bplscale
|
||||
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.turbomodule.core.TurboReactPackage
|
||||
|
||||
class BPLScalePackage : TurboReactPackage() {
|
||||
|
||||
override fun getModule(
|
||||
name: String,
|
||||
context: ReactApplicationContext
|
||||
): NativeModule? {
|
||||
return if (name == BPLScaleModule.NAME) {
|
||||
BPLScaleModule(context)
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun getReactModuleInfoProvider() =
|
||||
ReactModuleInfoProvider {
|
||||
mapOf(
|
||||
BPLScaleModule.NAME to ReactModuleInfo(
|
||||
BPLScaleModule.NAME,
|
||||
BPLScaleModule.NAME,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
7721
react-native-bpl-scale/package-lock.json
generated
Normal file
7721
react-native-bpl-scale/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
react-native-bpl-scale/package.json
Normal file
21
react-native-bpl-scale/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "react-native-bpl-scale",
|
||||
"version": "1.0.0",
|
||||
"main": "src/index.ts",
|
||||
"codegenConfig": {
|
||||
"name": "BPLScale",
|
||||
"type": "modules",
|
||||
"jsSrcsDir": "src",
|
||||
"android": {
|
||||
"javaPackageName": "com.bplscale"
|
||||
}
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react-native": ">=0.73"
|
||||
},
|
||||
|
||||
"devDependencies": {
|
||||
"react-native": "^0.73.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
8
react-native-bpl-scale/src/NativeBPLScale.ts
Normal file
8
react-native-bpl-scale/src/NativeBPLScale.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import type { TurboModule } from 'react-native';
|
||||
import { TurboModuleRegistry } from 'react-native';
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
hello(name: string): string;
|
||||
}
|
||||
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>('BPLScale');
|
||||
1
react-native-bpl-scale/src/index.ts
Normal file
1
react-native-bpl-scale/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './NativeBPLScale';
|
||||
15
react-native-bpl-scale/tsconfig.json
Normal file
15
react-native-bpl-scale/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2019",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2019"],
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"types": ["react-native"],
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
5
react-native.config.js
Normal file
5
react-native.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
name: 'react-native-bpl-scale',
|
||||
version: '0.1.0',
|
||||
dependencies: {},
|
||||
};
|
||||
@@ -1,7 +1,15 @@
|
||||
import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
||||
import { NativeModules } from 'react-native';
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
multiply(a: number, b: number): number;
|
||||
export interface NativeBplScale {
|
||||
startDiscovery(): void;
|
||||
cancelDiscovery(): void;
|
||||
getBondedDevices(): Promise<any[]>;
|
||||
parseBluetoothScaleData(data: string): any;
|
||||
|
||||
// ✅ ADD THIS
|
||||
scanDevices(scanDurationMs: number): Promise<any[]>;
|
||||
}
|
||||
|
||||
export default TurboModuleRegistry.getEnforcing<Spec>('BplScale');
|
||||
const { BplScale } = NativeModules;
|
||||
|
||||
export default BplScale as NativeBplScale;
|
||||
|
||||
@@ -1,5 +1,37 @@
|
||||
import BplScale from './NativeBplScale';
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
|
||||
export function multiply(a: number, b: number): number {
|
||||
return BplScale.multiply(a, b);
|
||||
}
|
||||
export type BLEDevice = {
|
||||
name: string;
|
||||
address: string;
|
||||
weight: number;
|
||||
impedance: number;
|
||||
};
|
||||
|
||||
type BplScaleModuleType = {
|
||||
scanDevices(scanDurationMs: number): Promise<BLEDevice[]>;
|
||||
};
|
||||
|
||||
const { BplScale } = NativeModules as { BplScale: BplScaleModuleType };
|
||||
|
||||
/**
|
||||
* Scan BLE devices and return weight and impedance.
|
||||
* @param scanDurationMs Duration of scan in milliseconds
|
||||
* @returns Promise resolving to a list of BLE devices with weight & impedance
|
||||
*/
|
||||
export const scanDevices = async (scanDurationMs: number): Promise<BLEDevice[]> => {
|
||||
if (Platform.OS !== 'android') {
|
||||
throw new Error('BplScale module is only supported on Android.');
|
||||
}
|
||||
|
||||
try {
|
||||
const devices = await BplScale.scanDevices(scanDurationMs);
|
||||
return devices;
|
||||
} catch (error) {
|
||||
console.error('BLE Scan Error:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
scanDevices,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user