更新记录

1.0.0(2026-08-18) 下载此版本

ble-test


平台兼容性

八电极设备对接文档

设备对接流程

申请插件APPID

填写申请接入插件的资料文件,并提供给业务申请插件对接授权。

插件对接申请表.xlsx

APP与设备工作流示意图

下载<span data-type=.jpeg" />

体脂秤用户说明

19<span data-type=_38_04.png" />

服务器秤端用户存储

结合第4点体脂秤用户说明,我们建议服务器采用设备mac为唯一标识,将秤端每个用户的index、secret、userId信息进行持久化存储,用于APP本地持久化数据被删后可从服务器获取更新及多账号绑定同一个秤的情况。APP本地同步到服务器的时机应在设备操作成功后立即同步,如注册用户成功,删除用户成功时间节点

设备测量指标

设备测量完成后,将统一提供体重、BMI、体脂率、体脂量、皮下脂肪率、皮下脂肪量、内脏脂肪等级、体水分率、体水分量、骨骼肌率、骨骼肌量、四肢骨骼肌指数、骨量、骨量比、基础代谢率、体型、蛋白质率、蛋白质质量、去脂体重、肌肉量、肌肉率、体年龄、分数、腰臀比、肥胖度、肥胖等级、目标体重、体重控制、脂肪控制、肌肉控制、左手肌肉量、右手肌肉量、躯干肌肉量、左腿肌肉量、右腿肌肉量、左手脂肪量、右手脂肪量、躯干脂肪量、左腿脂肪量、右腿脂肪量、左手肌肉率、右手肌肉率、躯干肌肉率、左腿肌肉率、右腿肌肉率、左手脂肪率、右手脂肪率、躯干脂肪率、躯干脂肪率、左腿脂肪率 50项指标,可根据所需自行对指标进行处理及报告展示。

测量报告

对于八电极设备的测量报告,我司提供34个指标和50个指标的Web版本的深度分析报告与PDF报告两种模版项目源码,供您快速接入。您可根据我司提供的模版项目里的README.md指引生成预览或编译产物进行查看(项目模版请联系业务经理获取),对于50个指标需要增加show_50_flag、whr两个参数,具体见模版项目。若您对于模版报告样式有微调需求,可直接使用我们模版项目修改里面的布局方式。倘若您计划采用全新的样式,亦可提取项目模版中的libs文件下的eight-electrodes-report-standard文件,该文件中将输出报告所涉及的所有指标标准

若您需要采iOS/Android原生端技术栈开发报告页面,可跟业务经理获取我司提供原生端指标标准库,指标库中将包含报告绘制指标的所有内容信息。

35项指标报告展示

eight<span data-type=_electrode_scale_report.png" />

eight<span data-type=_electrode_scale_pdf.png" />

50项指标报告展示

eight<span data-type=_electrode_scale_report_50.png" />

eight<span data-type=_electrode_scale_pdf.png" />

eight<span data-type=_electrode_scale_pdf_2.png" />

iOS接入步骤

项目引入插件

通过cocoapods方式

//在项目Podfile文件中,增加插件及指定版本的引入
pod 'QNSDK', '2.29.0' //此处版本号可根据github中显示的最新版本进行引入

GIthub地址:[sdk-ios-demo](https://github.com/YolandaQingniu/sdk-ios-demo)

通过carthage方式

//在项目Cartfile文件中,增加插件引入
github "https://github.com/YolandaQingniu/sdk-ios-demo.git"

手动导入方式

1. 将.a文件放置项目中的指定位置
2. 在【TARGETS】-> 【Build Setting】->【Search Paths】->【LibrarySearch Paths】中添加SDK路径
3. 配置链接器 【TARGETS】-> 【Build Setting】-> 【Linking】-> 【Other Linker Flags】中添加 -ObjC、-all_load、-force_load [SDK路径] 其中之一

配置项目蓝牙权限使用说明

在项目的Info.plist文件中,增加 Privacy - Bluetooth Peripheral Usage Description、Privacy - Bluetooth Always Usage Description 键,进行蓝牙的使用说明

导入插件API头文件

#import <QNSDK/QNDeviceSDK.h>

初始插件

//获取项目中授权文件地址(入参为qn文件的文件名称和文件扩展名)
NSString *file = [[NSBundle mainBundle] pathForResource:@"123456789" ofType:@"qn"];
//实例化插件, QNBleApi为单例对象
QNBleApi *bleApi = [QNBleApi sharedBleApi];
//获取插件授权(入参为appid和qn文件路径),注意此处需要替换为所申请的appid
[bleApi initSdk:@"123456789" firstDataFile:file callback:^(NSError *error) {
    if(error) {
       //监听异常回调
    }
}];

监听系统蓝牙状态变化(QNBleStateListener)

//设置监听(系统蓝牙状态)
bleApi.bleStateListener = self;

/*
typedef NS_ENUM(NSUInteger, QNBLEState) {
    QNBLEStateUnknown = 0, //未知状态
    QNBLEStateResetting = 1, //系统蓝牙正在重置
    QNBLEStateUnsupported = 2, //系统不支持蓝牙使用
    QNBLEStateUnauthorized = 3, //未授权蓝牙使用
    QNBLEStatePoweredOff = 4, //系统蓝牙处于关闭状态
    QNBLEStatePoweredOn = 5, //系统蓝牙处于开启状态
};
*/
- (void)onBleSystemState:(QNBLEState)state {

}

监听SDK日志(QNLogProtocol)

//设置监听(SDK日志),我们推荐您开启并在app内记录日志,当用户反馈异常时,用户可上传日志,您拿到日志后可向我们寻找问题协助分析处理
bleApi.logListener = self;

- (void)onLog:(nonnull NSString *)log { 
    //SDK输出的日志会在此处返回
}

开启蓝牙扫描,拿到设备对象

开启蓝牙扫描

[bleApi startBleDeviceDiscovery:^(NSError *error) {

}];

监听扫描状态的变化(QNBleDeviceDiscoveryListener)

//设置监听(扫描设备)
bleApi.discoveryListener = self;

//启动扫描时,触发该函数回调
- (void)onStartScan {

}

//停止扫描时,触发该函数回调
- (void)onStopScan {

}

/*
 * 当启动扫描,扫描到设备时会回调该函数。此处仅回调所支持的设备
 * QNBleDevice {
 *   mac: 设备mac地址,设备的唯一标识,
 *   name: 设备型号名称,
 *   modeId:设备型号标识,
 *   bluetoothName:设备蓝牙名称,
 *   RSSI:当前扫描到设备的信号强度,
 *   maxUserNum:设备支持最大用户数,
 *   registeredUserNum:当前设备已注册用户数
 * }
*/  
- (void)onDeviceDiscover:(QNBleDevice *)device {
    //后续调用SDK发起连接设备方法时,需要传入该device
}

连接设备

开始连接设备

//建议先停止扫描
[_bleApi stopBleDeviceDiscorvery:^(NSError *error) { }];

//设置秤端单位,连接设备后,将同步该单位给到设备显示
QNConfig *sdkConfig = [QNConfig sharedConfig];
sdkConfig.unit = QNUnitKG;
[sdkConfig save];

//开始连接秤设备
/*
device:需要连接的蓝牙设备,即扫描设备监听方法onDeviceDiscover返回的(QNBleDevice *)device
config:连接用户秤设备时的配置项,可以参考下面示例
*/
[bleApi connectUserScaleDevice:device config:config callback:^(NSError *error) {

}];

//config参数示例:
QNUserScaleConfig *config = [[QNUserScaleConfig alloc] init];

//1. 此次测量的用户信息
QNUser *user = [[QNUser alloc] init];
user.userId = "";//业务逻辑中用户唯一标识,用于业务中用户区分判断
user.height = 170;//用户身高,单位cm
user.gender = @"male";//用户性别,"male" 男性,"female" 女性
user.birthday = [NSDate dateWithTimeIntervalSince1970:631199317];//用户生日,入参是秒级时间戳
user.hmac = lastValidEightHmac; //注意:需要是该用户最近一条(测量体脂率大于0并且是同类型八电极设备的)测量数据的hmac,同类型可根据QNScaleData.newEightModel属性进行判断

//2. 用户管理模式
//2.1 此处为注册用户需填写
/*
向秤端注册的用户

注册用户前,需要核实一下秤设备的秤端用户是否已满(秤端最多存储8个用户),可以根据QNBleDevice的registeredUserNum进行判断
注册秤端用户成功后,SDK会拿到秤端返回的user.index,见registerUserComplete回调函数。
注册秤端用户成功后,可以接收到秤端的实时体重数据和结果数据
*/
config.curUser = user;

//2.2此处为访问用户需填写【注册和访问是不能同时进行的】
/*
访问秤端用户(访问秤端已经有的用户),需要传入user.index,用于秤端校验,如果与秤端存储的不一致,则会访问失败
访问用户传入的用户信息,会更新秤端保存的对应信息,例如秤端针对该用户保存的身高是170cm,如果此次传入的时171cm,那么秤端就会更新为171cm
访问秤端用户成功后,可以接收到秤端的实时体重数据和结果数据
*/
user.index = 1; //用户坑位,即秤端用户索引,有效范围[1,8],代表访问的是秤端哪个位置上的用户。该值由向秤端注册用户时,秤端返回,见registerUserComplete回调函数。
user.hmac = "";//该用户同类型设备上一笔体脂率大于0数据的hmac数据,同类型是指scaleData.newEightModel同数值的测量数据。当无上一笔体脂率大于0数据的时,此处赋值为""
config.curUser = user;

//2.3删除用户【SDK会先进行删除用户操作,然后才进行注册用户或者访问用户操作】
//已注册的秤端用户列表,在本次连接时,会向秤端删除该数组不包含的秤端用户
NSMutableArray<QNUser *> *registeredUserList = [NSMutableArray array];
config.userlist = registeredUserList;

监听设备连接状态变化(QNBleConnectionChangeListener)

//设置监听(设备连接状态变化)
bleApi.connectionChangeListener = self;

//设备正在连接中回调
- (void)onConnecting:(QNBleDevice *)device {

}

//设备连接成功回调
- (void)onConnected:(QNBleDevice *)device {

}

//设备通讯服务已搜索完成,此处一般不涉及逻辑处理
- (void)onServiceSearchComplete:(QNBleDevice *)device {
}

//设备连接失败,会回调失败异常信息,可在收到此回调做失败后的逻辑处理
- (void)onConnectError:(QNBleDevice *)device error:(NSError *)error {

}

//设备可以进行交互,即可以下发设备对应的操作指令
- (void)onStartInteracting:(QNBleDevice *)device {

}

//设备正在断开蓝牙连接
- (void)onDisconnecting:(QNBleDevice *)device {

}

//设备已断开
- (void)onDisconnected:(QNBleDevice *)device {

}

设备测量数据获取

获取设备数据交互(QNScaleDataListener)

//设置监听(设备连接状态变化)
bleApi.dataListener = self;

//向秤端注册用户成功,回调秤端分配的用户坑位
- (void)registerUserComplete:(QNBleDevice *)device user:(QNUser *)user {
    //注册用户时,秤端分配的坑位。app端应将该坑位、用户id、设备mac进行关联保存。下次该用户连接该设备时需用到该index进行秤端用户访问测量。建议在此时间同步保存到服务器
    int index = user.index;
}

/* 秤端交互过程状态变化
typedef NS_ENUM(NSInteger, QNScaleState) {
    QNScaleStateDisconnected = 0, //未连接
    QNScaleStateLinkLoss = -1, //失去连接
    QNScaleStateConnected = 1, //已连接
    QNScaleStateConnecting = 2, //正在连接
    QNScaleStateDisconnecting = 3, //正在断开
    QNScaleStateStartMeasure = 4, //正在测量
    QNScaleStateRealTime = 5, //正在测量体重
    QNScaleStateBodyFat = 7, //正在测量生物阻抗
    QNScaleStateMeasureCompleted = 9, //测量完成
}; 仅需关注该类状态
*/
- (void)onScaleStateChange:(QNBleDevice *)device scaleState:(QNScaleState)state {
}

/* 秤端行为状态变化
typedef NS_ENUM(NSInteger, QNScaleEvent) {
    QNScaleEventRegistUserSuccess = 4, //注册用户成功
    QNScaleEventRegistUserFail = 5, //注册用户失败
    QNScaleEventVisitUserSuccess = 6, //访问用户成功
    QNScaleEventVisitUserFail = 7, //访问用户失败
    QNScaleEventDeleteUserSuccess = 8, //删除用户成功
    QNScaleEventDeleteUserFail = 9, //删除用户失败
    QNScaleEventSyncUserInfoSuccess = 10, //同步用户信息成功
    QNScaleEventSyncUserInfoFail = 11, //同步用户信息失败
    QNScaleEventUpdateIdentifyWeightSuccess = 12, //更新用户识别体重成功
    QNScaleEventUpdateIdentifyWeightFail = 13, //更新用户识别体重失败
};仅需关注该类状态
*/
- (void)onScaleEventChange:(QNBleDevice *)device scaleEvent:(QNScaleEvent)scaleEvent {

}

/*
 设备测量过程实时重量的回调
 @param weight 实时体重,单位kg
*/
- (void)onGetUnsteadyWeight:(QNBleDevice *)device weight:(double)weight {

}

/*
 设备测量完成测量数据回调
 @param scaleData 测量数据
*/
- (void)onGetScaleData:(QNBleDevice *)device data:(QNScaleData *)scaleData {
    //测量完成获取完整测量数据,针对新方案八电极设备,需判断本次测量数据是否异常
    if (scaleData.newEightModel == 1) { //新方案八电极设备
        //(新方案八电极专属)本次测量是否异常,0-正常;1-异常。
        if (scaleData.eightIsAbnormal == 1) {
            //(新方案八电极专属)本次测量异常原因,0-正常;1-手部异常;2-腿部异常;3-手脚均异常;
            NSInteger reasonMask = scaleData.eightReasonMask;
            //根据异常原因,可提示用户重新测量

            return;
        }
    }
    NSDate *measureData = scaleData.measureTime;//测量时间
    double weight = scaleData.weight;//测量体重
    NSString *hmac = scaleData.hmac;//本次测量数据密钥,该字段的值必须保存,在设备连接时需要使用
    NSArray <QNScaleItemData *> *allTarget = [scaleData getAllItem];
    for (QNScaleItemData *item in allTarget) {
        item.name //指标名字
        item.type //指标类型,见QNScaleType
        item.value //指标数值,根据QNValueType确认该value的精确度
    }
    /*
    业务层面建议在此处增对用户上一笔测量体重的差异对比,我们建议当前测量体重与用户上一笔测量体重相差大于3kg时,提示用户进行确认是否是自己测量的数据

}

/*
 当前访问用户的存储数据及未知存储数据回调
 @param storedDataList 存储数据列表
*/
- (void)onGetStoredScale:(QNBleDevice *)device data:(NSArray <QNScaleStoreData *> *)storedDataList {
    /*
    通过QNScaleStoreData对象中的isDataComplete判断是否为未知存储数据,false为未知存储数据,true为已知存储数据;

    1. 已知存储数据,即属于当前访问用户的存储数据,为用户在不通过蓝牙连接时所测量被设备自动识别到用户的测量数据
    2. 未知存储数据,为用户在不通过蓝牙连接时所测量无法被设备识别到具体用户的测量数据。对于此类数据我们建议可以在APP上实现未知测量数据认领功能,将该类数据分发给绑定设备的用户进行认领,在认领时可通过调用calculateScaleDataByHmac函数计算具体测量指标。或者根据内部逻辑直接默认分配给某个用户
    */
    //根据内部逻辑直接默认分配给某个用户的伪代码
    NSMutableArray<QNScaleStoreData *> *unknowStoreDataList = [NSMutableArray array];
    for (QNScaleStoreData *storeData in storedDataList) {
        if(!storeData.isDataComplete){ //未知测量数据
            [unknowStoreDataList addObject:storeData];
        } else {
            NSString *lastValidEightHmac = @"xxxxxx";

            QNScaleData *scaleData = [storeData generateScaleDataWithLastEightHmac: lastValidEightHmac];
            NSDate *measureData = scaleData.measureTime;//测量时间
            double weight = scaleData.weight;//测量体重
            NSArray <QNScaleItemData *> *allTarget = [scaleData getAllItem];
            for (QNScaleItemData *item in allTarget) {
                item.name //指标名字
                item.type //指标类型,见QNScaleType
                item.value //指标数值,根据QNValueType确认该value的精确度
            }
        }
    }
}

主动断开设备连接

//若有需要,可主动调用断开设备连接方法
[bleApi disconnectDevice:nil callback:^(NSError *error) {

 }];

数据计算

重算指标,也适用于存储数据生成测量数据

/// 重算数据指标
/// @param user 重算数据指标的目标用户
/// @param hmac 重算数据的hmac
/// @param lastEightHmac 上次测量数据的hmac,八电极设备拟合要用
/// @param callback 回调
- (QNScaleData *)calculateScaleDataByHmac:(QNUser *)user hmac:(NSString *)hmac lastEightHmac:(nullable NSString *)lastEightHmac callback:(QNResultCallback)callback;

使用示例:
//目标用户的用户信息
QNUser *user = [[QNUser alloc] init];
user.height = 170;//用户身高,单位cm
user.gender = @"male";//用户性别,"male" 男性,"female" 女性
user.birthday = [NSDate dateWithTimeIntervalSince1970:631199317];//用户生日,传入秒级时间戳

//hmac为重算数据的hmac,可以是存储数据的hmac(对应使用场景是存储数据生成测量数据),也可以是测量数据的hmac(对应使用场景是测量数据 重算指标)

//lastValidEightHmac为目标用户的最近一条(测量体脂率大于0并且是同类型八电极设备的)测量数据的hmac,同类型可根据QNScaleData.newEightModel属性进行判断

QNScaleData scaleData = [bleApi calculateScaleDataByHmac:user hmac:hmac lastEightHmac:lastValidEightHmac callback:^(NSError *error) {
    //如果入参检验不通过,此处会报错,而有对应的SDK日志打印   
}];

Android接入步骤

项目引入插件

dependencyResolutionManagement管理

  • 在工程的根目录下的setting\.gradle添加jitpack支持
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
    repositories {
        //申明仓库地址
        maven { url 'https://jitpack.io' }
        //其他仓库配置
        ...
    }
}
  • 在App目录下的build\.gradle添加依赖
dependencies {
    //申明依赖,X.Y.Z需修改成具体的版本号
    implementation("com.github.YolandaQingniu:qnscalesdkX:X.Y.Z")

    //其他三方依赖
    ...
}

allprojects管理

  • 在工程的根目录下的 build\.gradle添加jitpack支持
allprojects {
    repositories {
        //其它仓库配置
        maven { url 'https://jitpack.io' }
        //其他仓库配置
        ...
    }
}
  • 在App目录下的build\.gradle添加依赖
dependencies {
    //申明依赖,X.Y.Z需修改成具体的版本号
    implementation("com.github.YolandaQingniu:qnscalesdkX:X.Y.Z")

    //其他三方依赖
    ...
}

配置项目蓝牙权限使用说明

app的targetSdk\>30,且手机的系统版本为安卓12及以上,则需要以下权限

  • android\.permission\.BLUETOOTH\_ADVERTISE
  • android\.permission\.BLUETOOTH\_SCAN
  • android\.permission\.BLUETOOTH\_CONNECT

否则需要以下权限

  • android\.permission\.BLUETOOTH
  • android\.permission\.BLUETOOTH\_ADMIN
  • android\.permission\.ACCESS\_COARSE\_LOCATION
  • android\.permission\.ACCESS\_FINE\_LOCATION

混淆配置

-keep class com.qingniu.scale.model.BleScaleData{*;}
-keep class com.jieli.** {*;}

初始插件

可在BaseApplication中的onCreate生命周期方法中初始化

//获取项目中授权文件地址
String encryptPath = "file:///android_asset/123456789.qn";
//实例化插件, QNBleApi为单例对象
QNBleApi bleApi = QNBleApi.getInstance(this);
//获取插件授权
bleApi.initSdk("123456789", encryptPath, new QNResultCallback() {
    @Override
    public void onResult(int code, String msg) {
    }
});

监听系统蓝牙状态变化(QNBleStateListener)

/*
enum QNBLEState {
    Unknown,
    setting,
    Unsupported,
    Unauthiorized,
    PoweredOff,
    PoweredOn;
 */
bleApi.setBleStateListener(new QNBleStateListener() {
    @Override
    public void onBleSystemState(QNBLEState qnbleState) {

    }
});

监听SDK日志(QNLogListener)

//我们推荐您开启并在app内记录日志,当用户反馈异常时,用户可上传日志,您拿到日志后可向我们寻找问题协助分析处理
bleApi.setLogListener(new QNLogListener() {
    @Override
    public void onLog(String log) {
        //SDK输出的日志会在这里返回
    }
});

开启蓝牙扫描,拿到设备对象

开启蓝牙扫描

在需要获取蓝牙设备的业务界面,调用以下方法

bleApi.startBleDeviceDiscovery(new QNResultCallback() {
    @Override
    public void onResult(int code, String msg) {
        //此处回调结果仅代表扫描周围设备方法调用是否成功
    }
});

获取蓝牙扫描的设备

在成功调用SDK的蓝牙扫描方法后,SDK会将扫描到的设备通过QNBleDeviceDiscoveryListener监听进行回调,同时该监听也可获取到SDK的蓝牙扫描相关状态

开发者可按以下示例注册监听回调,该回调监听业务维护上建议注册一次即可

bleApi.setBleDeviceDiscoveryListener(new QNBleDeviceDiscoveryListener() {
    @Override
    public void onDeviceDiscover(QNBleDevice device) {
      //当启动扫描,扫描到设备时会回调该函数。此处仅回调所支持的设备
      //此处可判断device对象是否是目标设备,如果是目标设备,可缓存至内存中,便于后续发起蓝牙连接
    }

    @Override
    public void onStartScan() {
      //启动扫描时,会回调该函数

    }

    @Override
    public void onStopScan() {
      //停止扫描时,会回调该函数

    }

     @Override
     public void onScanFail(int code) {
       //启动扫描失败回调

     }
});

设备连接

开始设备连接

在通过第2步骤扫描获取到目标设备对象之后,可按以下方法调用设备连接方法

//持续的蓝牙扫描会耗电,建议在执行连接之前停止扫描
//谷歌官方建议:https://developer.android.com/develop/connectivity/bluetooth/ble/find-ble-devices?hl=zh-cn
bleApi.stopBleDeviceDiscovery(new QNResultCallback() {
    @Override
    public void onResult(int code, String msg) {

    }
});

//设置秤端单位
QNConfig sdkConfig = bleApi.getConfig()
sdkConfig.unit = 0; //0-kg,1-lb,2-斤,3-st:lb,4-st,
sdkConfig.save();

QNUserScaleConfig config = new QNUserScaleConfig();
//1、已注册的秤端用户列表,在本次连接时,会向秤端删除该数组不包含的秤端用户
ArrayList<QNUser> deviceUserList = new ArrayList<>();
config.setUserlist(deviceUserList);

//2、需要告知秤端,本次连接时,是向秤端注册用户还是访问秤端用户
//如果是注册秤端用户
//注册⽤户前,需要核实⼀下秤设备的秤端⽤户是否已满(秤端最多存储8个⽤户),可以根据QNBleDevice.this.getRegisteredUserNum()进⾏判断
//注册秤端⽤户成功后,SDK会拿到秤端返回的user.index,⻅registerUserComplete回调函数。
//注册秤端⽤户成功后,可以接收到秤端的实时体重数据和结果数据
config.setRegist(true)
config.setChange(false)
int index = 0;

//3、如果是访问秤端用户
//访问⽤户传⼊的⽤户信息,会更新秤端保存的对应信息,例如秤端针对该⽤户保存的身⾼是170cm,如果此次传⼊的时171cm,那么秤端就会更新为171cm
//访问秤端⽤户成功后,可以接收到秤端的实时体重数据和结果数据
config.setRegist(false)
config.setChange(true)
int index = 1; //有效范围[1,8],代表访问的是秤端哪个位置上的用户。该值由向秤端注册用户时,秤端返回,见registerUserComplete回调函数。

//构建本次连接的用户信息
String userId = "";//业务逻辑中用户唯一标识,用于业务中用户区分判断
int height = 170;//用户身高,单位cm
String gender = "male"; //用户性别,"male" 男性,"female" 女性
Date birthday = new Date(631199317000L);//用户生日

QNUser user = bleApi.buildUser(userId, height, gender, birthday, 0, UserShape.SHAPE_NONE, UserGoal.GOAL_NONE, 0, index, 0,
        new QNResultCallback() {
            @Override
            public void onResult(int code, String msg) {

            }
        });
//lastHmac需要是上笔测量体脂率大于0并且与hmac同类型的数据,同类型可根据QNScaleData.this.getNewEightModel()是否一致判断。当无上一笔体脂率大于0数据的时,此处赋值为""
user.setHmac(lastHmac);

config.setCurUser(user);

bleApi.connectUserScaleDevice(device, config, new QNResultCallback() {
    @Override
    public void onResult(int code, String msg) {

    }
});

设备连接状态监听(QNBleConnectionChangeListener)

bleApi.setBleConnectionChangeListener(new QNBleConnectionChangeListener() {
    @Override
    public void onConnecting(QNBleDevice device) {
    //设备正在连接时回调

    }

    //已连接
    @Override
    public void onConnected(QNBleDevice device) {
    //设备已连接时回调

    }

    @Override
    public void onServiceSearchComplete(QNBleDevice device) {
    //设备的服务搜索完成
    }

    @Override
    public void onDisconnecting(QNBleDevice device) {
    //设备正在断开时回调

    }

    @Override
    public void onDisconnected(QNBleDevice device) {
    //设备已断开时回调

    }

    @Override
    public void onConnectError(QNBleDevice device, int errorCode) {
    //设备出现了连接错误回调
    }

    @Override
    public void onStartInteracting(QNBleDevice device) {
    //设备可以进行交互,即可以下发设备对应的操作指令

    }
});

设备测量数据获取

设备数据交互监听(QNUserScaleDataListener)

在执行3\.1步骤,与设备蓝牙连接上后,相关数据获取示例如下

bleApi.setDataListener(new QNUserScaleDataListener() {
    //向秤端注册用户成功,回调秤端分配的用户坑位
    @Override
    public void registerUserComplete(QNBleDevice device, QNUser user) {
    //注册用户时,秤端分配的坑位。app端应将该坑位、用户id、设备mac进行关联保存。下次该用户连接该设备时需用到该index进行秤端用户访问测量。建议在此时间同步保存到服务器
        int index = user.index;
    }

    //设备测量过程实时重量的回调, weight实时体重,单位kg
    @Override
    public void onGetUnsteadyWeight(QNBleDevice device, double weight) {

    }

    //设备测量完成测量数据回调, data 测量数据
    @Override
    public void onGetScaleData(QNBleDevice device, QNScaleData data) {
        //测量完成获取完整测量数据,针对新⽅案⼋电极设备,可判断本次测量数据是否异常
        if (data.getNewEightModel() == 1) {
            //本次测量是否异常,0-正常;1-异常
            int isAbnormal = data.getEightIsAbnormal();
            if (isAbnormal == 1) {
                //本次测量异常原因,0-正常,1-手部接触异常;2-腿部接触异常;3-手脚接触均异常
                int reasonMask = data.getEightReasonMask();
                //根据异常原因,可提示用户重新测量
                //可参考[附表-八电极异常测量提示]

                return;
            }
        }

        //本次测量手脚各部位接触正常
        Date measureDate = data.getMeasureTime();
        double weight = data.getWeight();//测量体重
        List<QNScaleItemData> allTarget = data.getAllItem();

        for (QNScaleItemData item: allTarget) {
            item.getType() //指标类型,见QNScaleType
            item.getValue() //指标数值
        }

        //业务层面建议在此处增对用户上一笔测量体重的差异对比,我们建议当前测量体重与用户上一笔测量体重相差大于3kg时,提示用户进行确认是否是自己测量的数据

    }

    // 当前访问用户存储数据及未知存储回调,storedDataList 存储数据列表,通过QNScaleStoreData对象中的isDataComplete判断是否为未知测量数据,false为未知测量数据,true为已知测量数据

    //1. 有已知存储数据(即属于当前访问⽤户的存储数据)和未知存储数据
    //2. 对于未知测量数据,可通知相关APP⽤户进⾏认领,即让APP⽤户选择这笔数据是否是⾃⼰的
    //3. 存储数据转化为测量数据分为两步:
        //4.1 设置该存储数据的拥有者(已知存储数据则不需要这⼀步) [QNScaleStoreData setUser:<#(QNUser *)#>];
        //4.2 将存储数据转换为测量数据 [QNScaleStoreData generateScaleData: lastValidEightHmac]
    //注意:lastValidEightHmac是数据拥有者的上⼀条(测量体脂率⼤于0并且是同类型⼋电极设备的)测量数据的hmac,同类型可根据QNScaleData.newEightModel属性进⾏判断
    @Override
    public void onGetStoredScale(QNBleDevice device, List<QNScaleStoreData> storedDataList) {
        //对于未知测量数据,可通知相关用户,让用户选择这笔数据是否是自己的
        ArrayList<QNScaleStoreData> unknowStoreDataList = new ArrayList<>();
        for (QNScaleStoreData storeData : storedDataList) {
            if(!storeData.isDataComplete()){
                unknowStoreDataList.add(storeData);
            } else {
            //已知当前用户的存储数据,该存储数据可直接归属到该用户下
                QNScaleData scaleData = storeData.generateScaleData(lastValidEightHmac);
                for (QNScaleItemData item: scaleData.getAllItem()) {
                    item.type //指标类型,见QNScaleType
                    item.value //指标数值
                }
            }
        }
    }

    //测量过程中的连接状态
    @Override
    public void onScaleStateChange(QNBleDevice device, int status) {

    }

});

设备断开

//若有需要,可主动调用断开设备连接方法
bleApi.disconnectDevice(bleApi, new QNResultCallback() {
    @Override
    public void onResult(int code, String msg) {

    }
});

数据计算

未知数据计算、重新计算

//未知存储数据归属用户的用户信息
String userId = "";
int height = 170;//用户身高,单位cm
String gender = "male"; //用户性别,"male" 男性,"female" 女性
Date birthday = new Date(631199317000L);//用户生日
QNUser user = mQNBleApi.buildUser(userId, height, gender, birthday, new QNResultCallback() {
    @Override
    public void onResult(int code, String msg) {

    }
});

//hmac为本次未知测量数据中的hmac,即QNScaleStoreData中的hmac
//lastHmac需要是上笔测量体脂率大于0并且与hmac同类型的数据,同类型可根据QNScaleData.this.getNewEightModel()是否一致判断
QNScaleData scaleData = mQNBleApi.calculateScaleDataByHmac(user, hmac,lastHmac);

微信小程序接入步骤

添加插件

在小程序管理后台首页的“小程序信息设置页 → 第三方设置 → 插件管理”中添加插件。开发者可登录小程序管理后台,通过 AppID(wx2a4ca48ed5e96748)查找插件并添加。

项目引入插件

在项目根目录下的 app.json 文件中引入插件代码包:

{
  "plugins": {
    "QNBleApi": {
      "version": "x.y.z", // 插件版本号,需要自行填写(建议填写最新版本)
      "provider": "wx2a4ca48ed5e96748" // 插件开发者(云康宝)的 AppID,固定值,无需修改
    }
  }
}

在分包内引入插件代码包

如果插件只在一个分包内使用,可以仅在该分包内引入插件,例如:

{
  "subpackages": [
    {
      "root": "packageA",
      "pages": [
        "pages/cat",
        "pages/dog"
      ],
      "plugins": {
        "QNBleApi": {
          "version": "x.y.z", // 插件版本号,需要自行填写(建议填写最新版本)
          "provider": "wx2a4ca48ed5e96748" // 插件开发者(云康宝)的 AppID,固定值,无需修改
        }
      }
    }
  ]
}

蓝牙相关权限说明

根据微信官方对小程序蓝牙权限的使用要求,在进行初始化时,如果系统蓝牙开关未开启或相关权限不足,蓝牙初始化将产生错误。因此,建议在调用初始化方法之前先检查蓝牙开关和相关权限。

iOS 端

目前主要涉及以下三项授权:

  • 系统蓝牙开关
  • 微信 APP 的蓝牙使用权限
  • 小程序的蓝牙使用权限

Android 端

目前主要涉及以下三项授权:

  • 系统蓝牙开关
  • 微信 APP 的定位权限、附近的设备(Android 12 及以上)
  • 小程序的蓝牙使用权限

鸿蒙端

目前主要涉及以下三项授权:

  • 系统蓝牙开关
  • 微信 APP 的设备发现和连接权限
  • 小程序的蓝牙使用权限

配置业务域名

在小程序后台“开发管理 → 开发设置 → 业务域名”中添加 https://third-api.yolanda.hk

引入插件

const { QNMPPlugin } = requirePlugin('QNBleApi');

实例化插件对象

const logger = () => {
  const wxlogger = wx.getLogManager && wx.getLogManager({ level: 0 });
  const log = (...params) => {
    console.log(...params);
    wxlogger && wxlogger.log(...params);
  };
  return {
    log,
  };
};

const qnbleApi = new QNMPPlugin({
  appId: APPID, // 必填,客户小程序的 AppID,需要提前向 YKB 申请授权
  logger: logger(), // 选填。用于输出日志,插件会通过该函数回调相关日志信息。建议保存相关日志,方便日后用户遇到问题时进行日志分析
});

初始化插件

// 注册插件初始化成功回调
qnbleApi.onReady = ({
  bleEnableState, // 当前蓝牙是否处于开启状态
}) => {
  if (bleEnableState) {
    console.log('蓝牙状态为开启');
    // 蓝牙可用时,立即开启蓝牙扫描
    qnbleApi.startBleDeviceDiscovery();
  } else {
    console.log('蓝牙状态为关闭');
  }
};

// 注册插件错误回调
qnbleApi.onError = (err) => {
  console.error('捕获到错误', err.detail);
};

// 注册插件蓝牙事件回调
const bleEventListener = {
  /**
   * @description: 发现目标设备
   * @param {BleDeviceType} bleDevice
   * 
   * 扫描到设备时触发。bleDevice 为扫描到的我司设备,非我司设备不会触发该回调。
   *
   * 可以在此调用 createBleConnection 连接设备。
   * 
   */
  onBleDeviceFound: (bleDevice) => {},
  /**
   * @description 系统蓝牙状态发生变化
   * available:系统蓝牙状态,true 表示开启,false 表示关闭
   */
  onBluetoothEnableChange: ({ available }) => {
    if (available) {
      console.log('蓝牙状态可用');
      qnbleApi.startBleDeviceDiscovery();
    } else {
      console.log('蓝牙状态为关闭');
    }
  },
  /**
   * @description: 启动扫描行为的回调
   */
  onStartDiscoveryDevice: () => {},
  /**
   * @description: 停止扫描行为的回调
   */
  onStopDiscoveryDevice: () => {},
  /**
   * @description 连接设备成功回调。iOS 端的 deviceId 为系统生成的 UUID,Android 端的 deviceId 为设备 MAC 地址。
   */
  onConnected: ({ deviceId }) => {},
  /**
   * @description 设备断开连接回调。iOS 端的 deviceId 为系统生成的 UUID,Android 端的 deviceId 为设备 MAC 地址。
   */
  onDisconnected: ({ deviceId }) => {},
  /**
   * @description: 设备连接超时的回调,iOS端为系统标识UUID,android端为设备mac地址
   * @return {*}
   */
  onConnectOverTime: ({ deviceId }) => {}
};
qnbleApi.setBleEventListener(bleEventListener);

// 初始化插件
qnbleApi.init();

监听系统蓝牙状态变化

onBluetoothEnableChange 事件中监听系统蓝牙状态变化,例如:

onBluetoothEnableChange(({ available }) => {
  // 系统蓝牙状态发生变化,true 开启,false 关闭
});

开启蓝牙扫描

开启蓝牙扫描 startBleDeviceDiscovery,例如:

qnbleApi.startBleDeviceDiscovery();

建议在 onBluetoothEnableChangeonReady 回调中发起蓝牙扫描,例如:

bleEventListener.onBluetoothEnableChange = ({ available }) => {
  if (available) {
    qnbleApi.startBleDeviceDiscovery();
  }
};

qnbleApi.onReady = ({
  bleEnableState, // 当前蓝牙是否处于开启状态
}) => {
  if (bleEnableState) {
    qnbleApi.startBleDeviceDiscovery();
  }
};

连接设备

// 用户信息字段可查看下方对象声明
const user = {
  height: 172, // 身高,单位cm
  gender: 'male', // 性别,male(男); female(女)
  age: 30, // 年龄
};

// 连接设备config
const config = {
  // 测量用户信息
  user,
  // 秤端重量单位:kg(千克) / lb(磅) / jin(斤)
  unit: 'kg',
  /**
   * 上一笔有效HMAC数据
   * 有效条件:同型号设备(测量数据中的newEightModel一致)、体脂率>0
   * 无历史有效数据时赋值为空字符串""
   */
  hmac: lastValidEightHmac,
  /**
   * 是否注册秤端用户坑位
   * true:注册新坑位;false:秤端坑位已满/访客测量
   * 1.首次测量、坑位未满:传 true;坑位已满,可先通过 deletedUserIndexArr 清理旧坑位再注册,或传 false 使用访客模式
   * 2.访问已存在用户:传 false
   * 坑位满溢判断:onBleDeviceFound 回调 device 对象,取 totalUserCount(总坑位)、registerUserCount(已占用坑位)
   */
  isRegisterUser: false,
  /**
   * 秤端用户坑位索引,取值范围 1‑8
   * 由注册回调 onGetRegisterUserResult 返回;注册成功后服务端需要持久化该坑位与用户的关联关系,后续复用该坑位访问用户
   * 首次注册新坑位无需传入该字段
   */
  userIndex: 1,
  // 需要删除的秤端坑位索引数组,元素取值1‑8;插件会优先清除这些坑位,无需删除则不传递该字段
  deletedUserIndexArr: [2, 3, 5, 8],
};

// 连接设备注册的监听事件
const deviceListener = {
    /**
     * 当设备连接成功后,读取设备信息进行回调
     * @param {DeviceInfoType} deviceInfo,具体字段信息可查看下方对象声明
     */
    onGetDeviceInfo: (deviceInfo) => {},
    /**
     * 当前用户测量的实时体重数据
     * @param {number} weight 
     */
    onGetUnsteadyWeight: ({ weight }) => {},
    /**
     * 设备进入体脂测量回调(部分设备不会支持该状态回调)
     */
    onMeasuringFat: () => {},
    /**
     * 注册用户结果回调函数
     * @param {Object} params - 回调参数对象
     * @param {boolean} params.isSuccess - 注册用户结果,true-注册成功,false-注册失败
     * @param {number} params.userIndex - 用户索引,注册成功后返回的用户唯一标识,一般是1~8
     */
    onGetRegisterUserResult: ({
      isSuccess /* 注册用户结果 */,
      userIndex /* 用户索引 */,
    }) => {},
     /**
     * 向秤端删除用户的结果回调
     */
    onGetDeleteUserResult: ({ isSuccess }) => {},
     /**
     * 向秤端访问的结果回调
     */
    onGetVisitUserResult: ({ isSuccess }) => {
      console.log('访问用户回调结果', isSuccess);
    },
     /**
     * 当前连接蓝牙测量结果数据
     * @param {object} measure,具体字段信息可查看下方对象声明
     * 
     */
    onGetScaleData: ({ measure }) => {},
    /*
     * 已知存储数据,即当前连接用户的存储数据
     */
    onGetKnownStoreData: ({ dataList }) => {},
    /*
     * 未知存储数据,需要用户确认该数据属于谁
     */
    onGetUnknownStoreData: ({ dataList }) => {},
};

// 连接设备前建议停止扫描
qnbleApi.stopBleDeviceDiscovery();

// 发起设备连接
qnbleApi.createBleConnection(bleDevice, deviceListener, config);

设备测量数据获取

设备测量数据可在 onGetScaleData 中获取;已知存储数据可在 onGetKnownStoreData 中获取;未知存储数据可在 onGetUnknownStoreData 中获取。示例如下:

/**
 * 测量数据回调
 * @param {MeasureDataType} measure
 * 
 */
onGetScaleData({ measure }) {
  console.log('当前测量数据', measure);

  // 测量完成后获取完整测量数据。针对新方案八电极设备,需要判断本次测量数据是否异常
  if (measure.newEightModel === 1 && measure.eight_is_abnormal === 1) {
    // 新方案八电极专属:0-正常;1-手部异常;2-腿部异常;3-手脚均异常
    // 可根据异常原因提示用户重新测量
    return;
  }

  // 保存测量数据前,业务层建议查询上一条有效且同类型的测量数据(lastValidMeasureData)进行差异判断。
  // 有效数据的体脂率应大于 0,且 newEightModel 值应与本次测量数据相同
  // 当体重相差 3 kg 以上或体脂率相差 3 个百分点以上时,可提示用户确认是否保存本次测量数据
  const weightDiff = Math.abs(measure.weight - lastValidMeasureData.weight);
  const bodyfatDiff = Math.abs(measure.bodyfat - lastValidMeasureData.bodyfat);

  if (weightDiff > 3 || bodyfatDiff > 3) {
    // 引导用户确认是否保存本次测量数据
    return;
  }

  // 将测量数据保存到服务器。
}

/**
 * 已知存储数据回调。
 * 此类数据属于当前访问的用户:用户未通过蓝牙连接进行测量时,设备自动识别出该用户后产生的数据。
 * @param {Array<MeasureDataType>} dataList
 */
onGetKnownStoreData({ dataList }) {
  console.log('已知存储测量数据', dataList);
}

/**
 * 未知存储数据回调。
 * 此类数据为用户未通过蓝牙连接进行测量时,设备无法识别具体用户而产生的数据。
 *
 * 建议在小程序中实现未知测量数据认领功能,将此类数据分发给已绑定设备的用户进行认领;
 * 认领时可调用 calcEightElectrodeScaleUnknownMeasurement 函数计算具体测量指标,
 * 也可根据业务逻辑直接将数据分配给某个用户。
 *
 * 重算流程:
 * 1. 从服务器获取该用户最近一条有效且同设备类型的测量数据的 HMAC(体脂率大于 0)。
 * 2. 从 `onGetUnknownStoreData` 回调中提取每条数据的 HMAC(即 curHMAC)。
 * 3. 调用重算方法 calcEightElectrodeScaleUnknownMeasurement。
 * 4. 校验返回结果后,逐条入库。
 * @param {Array<MeasureDataType>} dataList
 */
async onGetUnknownStoreData({ dataList }) {
  console.log('未知存储测量数据', dataList);
  for (const item of dataList) {
    try {
      const measureData = await qnbleApi.calcEightElectrodeScaleUnknownMeasurement({
        curHMAC: item.hmac, // 本条离线数据的 HMAC
        lastHMAC: item.lastHMAC, // 上一条有效测量的 HMAC(从服务器获取)
        user: {
          height: 180,
          gender: 1, // 1:男;0:女
          birthday: '2000-01-01', // 格式:YYYY-MM-DD(不支持 age)
        },
      });
      // 重算成功后,保存到服务器。
      console.log(measureData);
    } catch (err) {
      console.error('离线数据重算失败', item, err);
    }
  }
}

断开设备连接

// 停止扫描 + 断开连接
qnbleApi.stop();
// 完全释放资源(再次使用需重新 init)
qnbleApi.releaseBleSource();

类型说明

MeasureDataType

体脂秤测量数据类型

参数 类型 说明
weight number 体重,单位:kg
time string 测量日期和时间
bodyfat number 体脂率,单位:%
subfat number 皮下脂肪率,单位:%
visfat number 内脏脂肪等级
water number 水分率,单位:%
bmr number 基础代谢率,单位:kcal
muscle number 骨骼肌率,单位:%
bone number 骨量,单位:kg
lbm number 去脂体重,单位:kg
protein number 蛋白质率,单位:%
muscleMass number 肌肉质量,单位:kg
score number 身体评分
bodyAge number 身体年龄
bmi number 身体质量指数
bodyShape number 体型
muscleRh number 右上肢肌肉量,单位:kg
muscleLh number 左上肢肌肉量,单位:kg
muscleT number 躯干肌肉量,单位:kg
muscleRf number 右下肢肌肉量,单位:kg
muscleLf number 左下肢肌肉量,单位:kg
fatRh number 右上肢脂肪量,单位:kg
fatLh number 左上肢脂肪量,单位:kg
fatT number 躯干脂肪量,单位:kg
fatRf number 右下肢脂肪量,单位:kg
fatLf number 左下肢脂肪量,单位:kg
paunch number 腹围,单位:cm
obesity number 肥胖度
age number 年龄
birthday string 生日
gender number 性别(0-女,1-男)
heartRate number 心率
height number 身高,单位:cm
hmac string HMAC 校验值
method number 使用的算法
eight_is_abnormal number 测量是否异常(0:正常;1:异常)
eight_reason_mask number 异常原因(0:正常;1:手部异常;2:脚部异常;3:手脚均异常)
newEightModel number 是否使用新八电极算法(1:是;0:否;无此值时需赋值为 0 后保存)

BleDeviceType

蓝牙设备基础信息和功能特性描述。

字段 类型 说明
deviceId string 设备ID。Android: MAC地址,iOS: UDID
name string 逻辑蓝牙名。有真实蓝牙名则使用真实名,否则设置非空字符串标识类型
displayName string 显示型号(可选),客户可自定义显示
deviceModel number 设备型号,一般用于同协议下区分不同设备
mac string MAC地址
sn string 设备序列号
modelId string 型号ID,不同型号不同值
manufacturer number[] 厂商字段(原始数据)
services string[] 服务UUID列表
totalUserCount number 秤端支持的坑位总数
registerUserCount number 已注册秤端坑位数
offlineDataCount number 离线数据条数
firmwareVersion number 固件版本号
scaleVersion number 秤端版本号
hardVersion number 硬件版本号
protolVersion number 协议版本
bleVersion number 蓝牙版本

DeviceInfoType

设备信息

字段 类型 说明
deviceId string 设备ID。Android: MAC地址,iOS: UDID
info.fwVersion string 固件版本号
info.bleVersion string 蓝牙版本号

常见问题

初始化失败:FETCH_SDK_INFO_FAILED

现象:提示"请求 SDK 配置信息失败,请检查 appid 是否正确或网络是否正常"。

::: warning 原因 初始化时传入的 APPID 未在云康宝后台配置。 :::

::: tip 解决 联系业务对接人员填写 APPID 申请表,审批通过后即可正常使用。 :::


初始化失败:INIT_FAILED - Cannot read properties of null (reading 'init')

::: warning 原因 引入插件语法错误,缺少花括号导致解构失败。 :::

::: code-group

import QNMPPlugin from '...';
import { QNMPPlugin } from '...';

:::


连接失败:errCode 10003, connection fail status 133

::: warning 原因 微信蓝牙 API 报错,非插件内部错误,通常表示"设备未准备好"或"连接被远端拒绝",属低概率偶现。 :::

::: tip 解决 稍后重新连接设备即可。 :::


蓝牙接口报错:auth denied / no permission

::: warning 原因 用户权限未开启,需依次检查以下三项:

  • 系统蓝牙开关
  • 微信蓝牙权限(旧版安卓:"蓝牙权限";安卓 12+:"附近设备权限";鸿蒙:"设备发现和连接")
  • 小程序使用蓝牙权限 :::

扫描报错:startBluetoothDevicesDiscovery:fail

部分系统的位置权限会影响蓝牙扫描,按以下步骤排查:

  1. startBluetoothDevicesDiscovery:fail:location service is disabled → 系统未开启定位
  2. startBluetoothDevicesDiscovery:fail:location permission is denied → 未授权微信定位

可通过 wx.getSystemInfo 判断:

  • locationEnabled:地理位置系统开关
  • locationAuthorized:微信定位权限

接口报错:fail appid privacy api banned

::: warning 原因 小程序后台"用户隐私保护指引"中未声明对应 API 的隐私权限,或发布时未勾选"收集用户隐私"。 :::

::: tip 解决 在小程序后台更新隐私配置,重新发布。 :::


开启控制台正常,关闭后无法使用

::: warning 原因 未配置业务域名,通常伴随 request:fail url not in domain list。 :::

::: tip 解决 在小程序后台"业务域名"中添加:

https://third-api.yolanda.hk

:::


不同平台的 deviceId 不一致

iOS 与 Android 的 deviceId 生成规则不同。业务上建议以 mac 字段为准,mac 在多端保持一致且不变。


很多不同型号设备蓝牙名都是 QN-Scale

无定制需求时,设备广播名统一为 QN-Scale,可通过 device.modelIddevice.type 区分型号和类型。


如何查看插件内部的嵌套报错

插件抛出的报错通常为多层嵌套,详细原始错误存储在 extra 字段。排查时需递归查看 extra 字段内容。


如何记录日志以便反馈问题

参考 pages/measuring/measuring.js 中的 logger 处理方式,注入 logger 后插件内部会使用微信日志系统记录,用户通过小程序反馈后可在小程序平台查看日志。


小程序 SDK 测量结果与轻牛 APP 有出入

::: warning 原因 可能是两端使用的算法配置不一致。 :::

::: tip 解决 与技术对接人员确认算法配置是否相同。 :::

完整的实例代码

const { QNMPPlugin } = requirePlugin('QNBleApi');

Page({
  data: {
    lastMeasureData: null,
    userInfo: {
      height: 172,
      gender: 'male',
      age: 30,
    },
    deviceInfo: null,
  },
  onShow() {
    // 从服务器拉取上一条体脂率大于 0 且同类型的测量数据。
    // 同类型可通过测量数据中的 newEightModel 判断:该字段值相同即为同类型。
    this.lastMeasureData = {
      weight: 65,
      hmac: '',
    };
  },
  onLoad() {
    // 1.实例化插件
    this.qnbleApi = new QNMPPlugin({
      appId: APPID, // 注意:需要手动填入申请后的 AppID
      logger: this.logger(),
    });

    // 注册插件初始化成功后的回调
    this.qnbleApi.onReady = ({
      bleEnableState, // 当前蓝牙是否为开启状态
    }) => {
      if (bleEnableState) {
        console.log('蓝牙状态为开启');
        // 蓝牙可用立即开启蓝牙扫描
        this.qnbleApi.startBleDeviceDiscovery();
      } else {
        console.log('蓝牙状态为关闭');
      }
    };

    // 注册插件错误回调
    this.qnbleApi.onError = (err) => {
      console.error('捕捉到错误', err.detail);
    };

    // 注册蓝牙监听事件
    this.qnbleApi.setBleEventListener(this.bleEventListener());

    // 初始化蓝牙
    this.qnbleApi.init();
  },
  onUnload() {
    this.stopBle();
  },
  logger() {
    const wxlogger = wx.getLogManager && wx.getLogManager({ level: 0 });
    const log = (...params) => {
      console.log(...params);
      wxlogger && wxlogger.log(...params);
    };
    return {
      log,
    };
  },

  doConnectDevice() {
    const {
      height,
      gender,
      age,
      userIndex,
    } = this.data.userInfo;

    const {
      totalUserCount,
      registerUserCount,
    } = this.data.deviceInfo;

    // 用户信息字段可查看下方对象声明
    const user = {
      height, // 身高,单位:cm
      gender, // 性别:male(男)/ female(女)
      age,
    };

    const config = {
      user,
      unit: 'kg', // 秤端重量单位:kg(千克)/ lb(磅)/ jin(斤)
      // 上一条体脂率大于 0 的八电极设备测量数据中的 HMAC。
      // 该字段用于提升测量稳定性,并设置秤端注册用户的基准体重;也可以不传递。
      hmac: this.lastMeasureData?.hmac || '',
      isRegisterUser: false, // 是否注册用户
      deletedUserIndexArr: [], // 本次连接需要删除的用户坑位索引
    };

    if (userIndex) {
      config.userIndex = userIndex; // 当前连接需要访问的用户
    } else if (totalUserCount > registerUserCount) {
      config.isRegisterUser = true;
    } else {
      config.deletedUserIndexArr = [1, 2, 3, 4, 5, 6, 7, 8];
      config.isRegisterUser = true;
    }

    // 连接设备前建议停止扫描
    this.qnbleApi.stopBleDeviceDiscovery();

    // 发起设备连接
    this.qnbleApi.createBleConnection(
      this.data.deviceInfo,
      this.deviceEventListener(),
      config,
    );
  },

  bleEventListener() {
    return {
      onBleDeviceFound: (bleDevice) => {
        // 扫描到我司设备时触发,非我司设备不会触发该回调。bleDevice 的具体字段可查看下方对象声明。
        // 发现设备后,可调用 createBleConnection 方法发起连接。
        this.deviceInfo = bleDevice;
        this.setData({
          deviceInfo: bleDevice,
        });
      },
      onBluetoothEnableChange: ({ available }) => {
        // 系统蓝牙状态发生变化,true 表示开启,false 表示关闭
        if (available) {
          // 发起蓝牙扫描
          this.qnbleApi.startBleDeviceDiscovery();
        } else {
          console.log('蓝牙状态为关闭');
        }
      },
      onStartDiscoveryDevice: () => {
        // 启动扫描行为的回调
      },
      onStopDiscoveryDevice: () => {
        // 停止扫描行为的回调
      },
      onConnected: ({ deviceId }) => {
        // 连接设备成功回调。iOS 端的 deviceId 为系统生成的 UUID,Android 端为设备 MAC 地址。
      },
      onDisconnected: ({ deviceId }) => {
        // 设备断开连接回调。iOS 端的 deviceId 为系统生成的 UUID,Android 端为设备 MAC 地址。
      },
      onConnectOverTime: ({ deviceId }) => {
        // 设备连接超时回调。iOS 端的 deviceId 为系统生成的 UUID,Android 端为设备 MAC 地址。
      },
    };
  },

  deviceEventListener() {
    return {
      /**
       * 当设备连接成功后,读取设备信息进行回调
       * @param {DeviceInfoType} deviceInfo,具体字段信息可查看下方对象声明
       */
      onGetDeviceInfo: (deviceInfo) => {},
      /**
       * 当前用户测量的实时体重数据
       * @param {number} weight
       */
      onGetUnsteadyWeight: ({ weight }) => {},
      /**
       * 设备进入体脂测量回调(部分设备不会支持该状态回调)
       */
      onMeasuringFat: () => {},
      /**
       * 向秤端注册用户的结果回调
       */
      onGetRegisterUserResult: ({ isSuccess /* 注册用户结果 */, userIndex /* 用户索引 */ }) => {
        if (isSuccess) {
          this.setData({
            userInfo: {
              ...this.data.userInfo,
              userIndex,
            },
          });
        }
      },
      /**
       * 向秤端删除用户的结果回调
       */
      onGetDeleteUserResult: ({ isSuccess }) => {},
      /**
       * 向秤端访问的结果回调
       */
      onGetVisitUserResult: ({ isSuccess }) => {
        console.log('访问用户回调结果', isSuccess);
      },
      /**
       * 当前连接蓝牙测量结果数据
       * @param {MeasureDataType} measure,具体字段信息可查看下方对象声明
       */
      onGetScaleData: ({ measure }) => {},
      /*
       * 已知存储数据,即当前连接用户的存储数据
       */
      onGetKnownStoreData: ({ dataList }) => {},
      /*
       * 未知存储数据,需要用户确认该数据属于谁
       */
      onGetUnknownStoreData: ({ dataList }) => {},
    };
  },

  async stopBle() {
    // 停止扫描 + 断开连接
    this.qnbleApi.stop();
    // 完全释放资源(再次使用需重新 init)
    this.qnbleApi.releaseBleSource();
  }
});

在小程序后台"业务域名"中添加:

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。