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
|
# Turborepo
|
||||||
.turbo/
|
.turbo/
|
||||||
|
|
||||||
# generated by bob
|
|
||||||
lib/
|
|
||||||
|
|
||||||
# React Native Codegen
|
# React Native Codegen
|
||||||
ios/generated
|
ios/generated
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<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>
|
</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,20 +1,133 @@
|
|||||||
package com.bplscale
|
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
|
import com.facebook.react.module.annotations.ReactModule
|
||||||
|
|
||||||
@ReactModule(name = BplScaleModule.NAME)
|
@ReactModule(name = BplScaleModule.NAME)
|
||||||
class BplScaleModule(reactContext: ReactApplicationContext) :
|
class BplScaleModule(
|
||||||
NativeBplScaleSpec(reactContext) {
|
reactContext: ReactApplicationContext
|
||||||
|
) : ReactContextBaseJavaModule(reactContext) {
|
||||||
|
|
||||||
override fun getName(): String {
|
private val bluetoothAdapter: BluetoothAdapter? by lazy {
|
||||||
return NAME
|
val manager =
|
||||||
|
reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
|
||||||
|
manager.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example method
|
private var scanner: BluetoothLeScanner? = null
|
||||||
// See https://reactnative.dev/docs/native-modules-android
|
private var scanCallback: ScanCallback? = null
|
||||||
override fun multiply(a: Double, b: Double): Double {
|
|
||||||
return a * b
|
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 {
|
companion object {
|
||||||
|
|||||||
@@ -1,33 +1,19 @@
|
|||||||
package com.bplscale
|
package com.bplscale
|
||||||
|
|
||||||
import com.facebook.react.BaseReactPackage
|
import com.facebook.react.ReactPackage
|
||||||
import com.facebook.react.bridge.NativeModule
|
import com.facebook.react.bridge.NativeModule
|
||||||
import com.facebook.react.bridge.ReactApplicationContext
|
import com.facebook.react.bridge.ReactApplicationContext
|
||||||
import com.facebook.react.module.model.ReactModuleInfo
|
import com.facebook.react.uimanager.ViewManager
|
||||||
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
||||||
import java.util.HashMap
|
|
||||||
|
|
||||||
class BplScalePackage : BaseReactPackage() {
|
class BplScalePackage : ReactPackage {
|
||||||
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
||||||
return if (name == BplScaleModule.NAME) {
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
||||||
BplScaleModule(reactContext)
|
// Return a list with your BplScaleModule
|
||||||
} else {
|
return listOf(BplScaleModule(reactContext))
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
||||||
return ReactModuleInfoProvider {
|
// No custom view managers for this module
|
||||||
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
return emptyList()
|
||||||
moduleInfos[BplScaleModule.NAME] = ReactModuleInfo(
|
|
||||||
BplScaleModule.NAME,
|
|
||||||
BplScaleModule.NAME,
|
|
||||||
false, // canOverrideExistingModule
|
|
||||||
false, // needsEagerInit
|
|
||||||
false, // isCxxModule
|
|
||||||
true // isTurboModule
|
|
||||||
)
|
|
||||||
moduleInfos
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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") }
|
plugins { id("com.facebook.react.settings") }
|
||||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
||||||
rootProject.name = 'bplscale.example'
|
rootProject.name = 'bplscale.example'
|
||||||
include ':app'
|
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",
|
"name": "react-native-bpl-scale",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "React Native Bluetooth BPL weighing scale library",
|
"description": "React Native Bluetooth BPL weighing scale library",
|
||||||
"main": "./lib/module/index.js",
|
"main": "lib/module/index.js",
|
||||||
"types": "./lib/typescript/src/index.d.ts",
|
"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"
|
|
||||||
},
|
|
||||||
"files": [
|
"files": [
|
||||||
"src",
|
"src",
|
||||||
"lib",
|
"lib",
|
||||||
@@ -32,7 +24,7 @@
|
|||||||
"!**/.*"
|
"!**/.*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"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",
|
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
|
||||||
"prepare": "bob build",
|
"prepare": "bob build",
|
||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
@@ -46,7 +38,7 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+.git"
|
"url": "git+.git"
|
||||||
},
|
},
|
||||||
"author": " <> ()",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "/issues"
|
"url": "/issues"
|
||||||
@@ -62,7 +54,6 @@
|
|||||||
"react": "19.2.0",
|
"react": "19.2.0",
|
||||||
"react-native": "0.83.0",
|
"react-native": "0.83.0",
|
||||||
"react-native-builder-bob": "^0.40.13",
|
"react-native-builder-bob": "^0.40.13",
|
||||||
"turbo": "^2.5.6",
|
|
||||||
"typescript": "^5.9.2"
|
"typescript": "^5.9.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"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 {
|
export interface NativeBplScale {
|
||||||
multiply(a: number, b: number): number;
|
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 {
|
export type BLEDevice = {
|
||||||
return BplScale.multiply(a, b);
|
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