├── android ├── uniplugin_module │ ├── consumer-rules.pro │ ├── .gitignore │ ├── .DS_Store │ ├── src │ │ ├── .DS_Store │ │ ├── main │ │ │ ├── .DS_Store │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ ├── vise │ │ │ │ └── baseble │ │ │ │ │ ├── common │ │ │ │ │ ├── BleExceptionCode.java │ │ │ │ │ ├── PropertyType.java │ │ │ │ │ ├── ConnectState.java │ │ │ │ │ ├── BluetoothServiceType.java │ │ │ │ │ ├── BleConstant.java │ │ │ │ │ └── BleConfig.java │ │ │ │ │ ├── callback │ │ │ │ │ ├── scan │ │ │ │ │ │ ├── IScanFilter.java │ │ │ │ │ │ ├── IScanCallback.java │ │ │ │ │ │ ├── UuidFilterScanCallback.java │ │ │ │ │ │ ├── ListFilterScanCallback.java │ │ │ │ │ │ ├── RegularFilterScanCallback.java │ │ │ │ │ │ ├── SingleFilterScanCallback.java │ │ │ │ │ │ └── ScanCallback.java │ │ │ │ │ ├── IRssiCallback.java │ │ │ │ │ ├── IBleCallback.java │ │ │ │ │ └── IConnectCallback.java │ │ │ │ │ ├── exception │ │ │ │ │ ├── OtherException.java │ │ │ │ │ ├── TimeoutException.java │ │ │ │ │ ├── InitiatedException.java │ │ │ │ │ ├── GattException.java │ │ │ │ │ ├── BleException.java │ │ │ │ │ ├── handler │ │ │ │ │ │ ├── DefaultBleExceptionHandler.java │ │ │ │ │ │ └── BleExceptionHandler.java │ │ │ │ │ └── ConnectException.java │ │ │ │ │ ├── core │ │ │ │ │ ├── LruHashMap.java │ │ │ │ │ ├── BluetoothGattChannel.java │ │ │ │ │ └── DeviceMirrorPool.java │ │ │ │ │ ├── utils │ │ │ │ │ ├── BleUtil.java │ │ │ │ │ ├── AdRecordUtil.java │ │ │ │ │ └── HexUtil.java │ │ │ │ │ ├── model │ │ │ │ │ ├── BluetoothLeDeviceStore.java │ │ │ │ │ ├── adrecord │ │ │ │ │ │ ├── AdRecordStore.java │ │ │ │ │ │ └── AdRecord.java │ │ │ │ │ ├── resolver │ │ │ │ │ │ └── BluetoothClassResolver.java │ │ │ │ │ └── BluetoothLeDevice.java │ │ │ │ │ └── ViseBle.java │ │ │ │ └── sand │ │ │ │ └── bluetooth │ │ │ │ └── Const.java │ │ ├── test │ │ │ ├── .DS_Store │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── sand │ │ │ │ └── bluetooth │ │ │ │ └── ExampleUnitTest.java │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── sand │ │ │ └── bluetooth │ │ │ └── ExampleInstrumentedTest.java │ ├── local.properties │ ├── proguard-rules.pro │ └── build.gradle └── .DS_Store ├── .DS_Store ├── ios ├── .DS_Store └── sand-plugin-bluetooth-ios │ ├── .DS_Store │ ├── sand-plugin-bluetooth │ ├── .DS_Store │ ├── objc │ │ ├── BabyDefine.m │ │ ├── BabyToy.h │ │ ├── BabyCallback.m │ │ ├── BabyRhythm.h │ │ ├── BabySpeaker.h │ │ ├── BabyOptions.m │ │ ├── BabyRhythm.m │ │ ├── BabyPeripheralManager.h │ │ ├── BabyCentralManager.h │ │ ├── BabyToy.m │ │ ├── BabyDefine.h │ │ ├── BabyOptions.h │ │ ├── BabySpeaker.m │ │ ├── BabyCallback.h │ │ └── BabyPeripheralManager.m │ ├── SandPluginBluetoothProxy.h │ ├── SandPluginBluetooth.h │ ├── Const.h │ ├── Const.m │ └── SandPluginBluetoothProxy.m │ └── sand-plugin-bluetooth.xcodeproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcuserdata │ │ ├── mac.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ │ └── qianjiaowang.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ ├── xcuserdata │ └── qianjiaowang.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist │ └── xcshareddata │ └── xcschemes │ └── sand-plugin-bluetooth.xcscheme ├── ble.js └── README.md /android/uniplugin_module/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/uniplugin_module/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/.DS_Store -------------------------------------------------------------------------------- /ios/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/ios/.DS_Store -------------------------------------------------------------------------------- /android/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/android/.DS_Store -------------------------------------------------------------------------------- /android/uniplugin_module/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/android/uniplugin_module/.DS_Store -------------------------------------------------------------------------------- /android/uniplugin_module/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/android/uniplugin_module/src/.DS_Store -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/ios/sand-plugin-bluetooth-ios/.DS_Store -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/android/uniplugin_module/src/main/.DS_Store -------------------------------------------------------------------------------- /android/uniplugin_module/src/test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/android/uniplugin_module/src/test/.DS_Store -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/.DS_Store -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyDefine.m: -------------------------------------------------------------------------------- 1 | // 2 | // BabyBlueDefine.m 3 | // BabyTestProject 4 | // 5 | // Created by xuanyan.lyw on 16/4/19. 6 | // Copyright © 2016年 liuyanwei. All rights reserved. 7 | // 8 | 9 | #import "BabyDefine.h" 10 | 11 | 12 | 13 | 14 | @implementation BabyDefine 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/project.xcworkspace/xcuserdata/qianjiaowang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pkkhandsome/uniapp-sand-plugin-bluetooth/HEAD/ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/project.xcworkspace/xcuserdata/qianjiaowang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/uniplugin_module/local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Fri Apr 12 19:06:54 CST 2024 8 | sdk.dir=/Users/mac/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/SandPluginBluetoothProxy.h: -------------------------------------------------------------------------------- 1 | // 2 | // SandPluginBluetoothProxy.h 3 | // sand-plugin-bluetooth 4 | // 5 | // Created by Qianjiao Wang on 2022/6/15. 6 | // 7 | #import 8 | #import "UniPluginProtocol.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface SandPluginBluetoothProxy : NSObject 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/common/BleExceptionCode.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.common; 2 | 3 | /** 4 | * @Description: BLE异常Code 5 | * @author: DAWI 6 | * @date: 16/8/14 10:43. 7 | */ 8 | public enum BleExceptionCode { 9 | TIMEOUT, //超时 10 | CONNECT_ERR, //连接异常 11 | GATT_ERR, //GATT异常 12 | INITIATED_ERR, //初始化异常 13 | OTHER_ERR //其他异常 14 | } 15 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/IScanFilter.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import com.vise.baseble.model.BluetoothLeDevice; 4 | 5 | /** 6 | * @Description: 扫描过滤接口,根据需要实现过滤规则 7 | * @author: DAWI 8 | * @date: 17/9/10 18:19. 9 | */ 10 | public interface IScanFilter { 11 | BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice); 12 | } 13 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/OtherException.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception; 2 | 3 | import com.vise.baseble.common.BleExceptionCode; 4 | 5 | /** 6 | * @Description: 其他异常 7 | * @author: DAWI 8 | * @date: 16/8/14 10:32. 9 | */ 10 | public class OtherException extends BleException { 11 | public OtherException(String description) { 12 | super(BleExceptionCode.OTHER_ERR, description); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/TimeoutException.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception; 2 | 3 | import com.vise.baseble.common.BleExceptionCode; 4 | 5 | /** 6 | * @Description: 超时异常 7 | * @author: DAWI 8 | * @date: 16/8/14 10:29. 9 | */ 10 | public class TimeoutException extends BleException { 11 | public TimeoutException() { 12 | super(BleExceptionCode.TIMEOUT, "Timeout Exception Occurred! "); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/SandPluginBluetooth.h: -------------------------------------------------------------------------------- 1 | // 2 | // SandPluginBluetooth.h 3 | // sand-plugin-bluetooth 4 | // 5 | // Created by Qianjiao Wang on 2022/4/28. 6 | // 7 | #import 8 | #import "DCUniModule.h" 9 | #import 10 | #import "BabyBluetooth.h" 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface SandPluginBluetooth : DCUniModule 15 | 16 | @end 17 | 18 | NS_ASSUME_NONNULL_END 19 | /* SandPluginBluetooth_h */ 20 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/test/java/com/sand/bluetooth/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.sand.bluetooth; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/InitiatedException.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception; 2 | 3 | import com.vise.baseble.common.BleExceptionCode; 4 | 5 | /** 6 | * @Description: 初始化异常 7 | * @author: DAWI 8 | * @date: 16/8/14 10:30. 9 | */ 10 | public class InitiatedException extends BleException { 11 | public InitiatedException() { 12 | super(BleExceptionCode.INITIATED_ERR, "Initiated Exception Occurred! "); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/IRssiCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback; 2 | 3 | import com.vise.baseble.exception.BleException; 4 | import com.vise.baseble.model.BluetoothLeDevice; 5 | 6 | /** 7 | * @Description: 获取信号值回调 8 | * @author: DAWI 9 | * @date: 2017/10/19 15:19 10 | */ 11 | public interface IRssiCallback { 12 | void onSuccess(BluetoothLeDevice device, int rssi); 13 | 14 | void onFailure(BluetoothLeDevice device,BleException exception); 15 | } 16 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/IBleCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback; 2 | 3 | import com.vise.baseble.core.BluetoothGattChannel; 4 | import com.vise.baseble.exception.BleException; 5 | import com.vise.baseble.model.BluetoothLeDevice; 6 | 7 | /** 8 | * @Description: 操作数据回调 9 | * @author: DAWI 10 | * @date: 2017/10/17 19:42 11 | */ 12 | public interface IBleCallback { 13 | void onSuccess(byte[] data, BluetoothGattChannel bluetoothGattChannel, BluetoothLeDevice bluetoothLeDevice); 14 | 15 | void onFailure(BleException exception); 16 | } 17 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/IScanCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import com.vise.baseble.model.BluetoothLeDevice; 4 | import com.vise.baseble.model.BluetoothLeDeviceStore; 5 | 6 | /** 7 | * @Description: 扫描回调 8 | * @author: DAWI 9 | * @date: 17/9/10 18:14. 10 | */ 11 | public interface IScanCallback { 12 | //发现设备 13 | void onDeviceFound(BluetoothLeDevice bluetoothLeDevice); 14 | 15 | //扫描完成 16 | void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore); 17 | 18 | //扫描超时 19 | void onScanTimeout(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/common/PropertyType.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.common; 2 | 3 | /** 4 | * @Description: 属性类型 5 | * @author: DAWI 6 | * @date: 2017/10/17 20:27 7 | */ 8 | public enum PropertyType { 9 | PROPERTY_READ(0x01), 10 | PROPERTY_WRITE(0x02), 11 | PROPERTY_NOTIFY(0x04), 12 | PROPERTY_INDICATE(0x08); 13 | 14 | private int propertyValue; 15 | 16 | PropertyType(int propertyValue) { 17 | this.propertyValue = propertyValue; 18 | } 19 | 20 | public int getPropertyValue() { 21 | return propertyValue; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/xcuserdata/qianjiaowang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | sand-plugin-bluetooth.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4684031C281AA58F00882266 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/IConnectCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback; 2 | 3 | import com.vise.baseble.core.DeviceMirror; 4 | import com.vise.baseble.exception.BleException; 5 | import com.vise.baseble.model.BluetoothLeDevice; 6 | 7 | /** 8 | * @Description: 连接设备回调 9 | * @author: DAWI 10 | * @date: 17/8/1 23:00. 11 | */ 12 | public interface IConnectCallback { 13 | //连接成功 14 | void onConnectSuccess(DeviceMirror deviceMirror); 15 | 16 | //连接失败 17 | void onConnectFailure(BluetoothLeDevice device,BleException exception); 18 | 19 | //连接断开 20 | void onDisconnect(BluetoothLeDevice device, boolean isActive); 21 | } 22 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/common/ConnectState.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.common; 2 | 3 | /** 4 | * @Description: 状态描述 5 | * @author: DAWI 6 | * @date: 16/8/20 17:06. 7 | */ 8 | public enum ConnectState { 9 | CONNECT_INIT(-1), //连接初始化 10 | CONNECT_PROCESS(0x00), //连接中 11 | CONNECT_SUCCESS(0x01), //连接成功 12 | CONNECT_FAILURE(0x02), //连接失败 13 | CONNECT_TIMEOUT(0x03), //连接超时 14 | CONNECT_DISCONNECT(0x04); //连接断开 15 | 16 | private int code; 17 | 18 | ConnectState(int code) { 19 | this.code = code; 20 | } 21 | 22 | public int getCode() { 23 | return code; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/Const.h: -------------------------------------------------------------------------------- 1 | // 2 | // Const.h 3 | // sand-plugin-bluetooth 4 | // 5 | // Created by Qianjiao Wang on 2022/6/7. 6 | // 7 | 8 | #ifndef Const_h 9 | #define Const_h 10 | extern NSString * const STATUS; 11 | extern NSString * const MESSAGE; 12 | extern NSString * const DEVICE_ID; 13 | extern NSString * const SERVICE_ID; 14 | extern NSString * const CHARACTERISTIC_ID; 15 | extern NSString * const DEVICES; 16 | 17 | extern NSString * const STATUS_SUCCESS; 18 | extern NSString * const STATUS_UN_INIT; 19 | extern NSString * const STATUS_FAIL_NO_DEVICE; 20 | extern NSString * const STATUS_FAIL_NO_FOUND; 21 | extern NSString * const STATUS_UN_SUPPORT; 22 | extern NSString * const STATUS_UNKNOWN_ERROR; 23 | 24 | #endif /* Const_h */ 25 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/Const.m: -------------------------------------------------------------------------------- 1 | // 2 | // Const.m 3 | // sand-plugin-bluetooth 4 | // 5 | // Created by Qianjiao Wang on 2022/6/7. 6 | // 7 | 8 | #import 9 | 10 | NSString * const STATUS=@"status"; 11 | NSString * const MESSAGE=@"message"; 12 | NSString * const DEVICE_ID=@"deviceId"; 13 | NSString * const SERVICE_ID=@"serviceId"; 14 | NSString * const CHARACTERISTIC_ID=@"characteristicId"; 15 | NSString * const DEVICES=@"devices"; 16 | //正常 17 | NSString * const STATUS_SUCCESS=@"2500"; 18 | //蓝牙适配器不可用 19 | NSString * const STATUS_UN_INIT=@"2501"; 20 | //设备未发现 21 | NSString * const STATUS_FAIL_NO_DEVICE=@"2502"; 22 | //其他服务特征等未发现 23 | NSString * const STATUS_FAIL_NO_FOUND=@"2503"; 24 | //不支持的操作 25 | NSString * const STATUS_UN_SUPPORT=@"2504"; 26 | //未知错误 27 | NSString * const STATUS_UNKNOWN_ERROR=@"2505"; 28 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/androidTest/java/com/sand/bluetooth/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.sand.bluetooth; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.sand.bluetooth.test", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /android/uniplugin_module/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -keep public class * extends io.dcloud.feature.uniapp.common.UniModule{*;} -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/sand/bluetooth/Const.java: -------------------------------------------------------------------------------- 1 | package com.sand.bluetooth; 2 | 3 | public class Const { 4 | public static final String STATUS = "status"; 5 | public static final String MESSAGE = "message"; 6 | public static final String DEVICE_ID = "deviceId"; 7 | public static final String MTU = "mtu"; 8 | public static final String SERVICE_ID = "serviceId"; 9 | public static final String CHARACTERISTIC_ID = "characteristicId"; 10 | public static final String DEVICES = "devices"; 11 | //正常 12 | public static final String STATUS_SUCCESS = "2500"; 13 | //蓝牙适配器不可用 14 | public static final String STATUS_UN_INIT = "2501"; 15 | //设备未发现 16 | public static final String STATUS_FAIL_NO_DEVICE = "2502"; 17 | //其他服务特征等未发现 18 | public static final String STATUS_FAIL_NO_FOUND = "2503"; 19 | //不支持的操作 20 | public static final String STATUS_UN_SUPPORT = "2504"; 21 | //未知错误 22 | public static final String STATUS_UNKNOWN_ERROR = "2505"; 23 | } 24 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/GattException.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception; 2 | 3 | import com.vise.baseble.common.BleExceptionCode; 4 | 5 | /** 6 | * @Description: Gatt异常 7 | * @author: DAWI 8 | * @date: 16/8/14 10:30. 9 | */ 10 | public class GattException extends BleException { 11 | private int gattStatus; 12 | 13 | public GattException(int gattStatus) { 14 | super(BleExceptionCode.GATT_ERR, "Gatt Exception Occurred! "); 15 | this.gattStatus = gattStatus; 16 | } 17 | 18 | public int getGattStatus() { 19 | return gattStatus; 20 | } 21 | 22 | public GattException setGattStatus(int gattStatus) { 23 | this.gattStatus = gattStatus; 24 | return this; 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "GattException{" + 30 | "gattStatus=" + gattStatus + 31 | '}' + super.toString(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyToy.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief babybluetooth 工具类 7 | 8 | */ 9 | 10 | // Created by 刘彦玮 on 15/8/1. 11 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 12 | // 13 | 14 | #import 15 | #import 16 | 17 | 18 | 19 | @interface BabyToy : NSObject 20 | 21 | //十六进制转换为普通字符串的。 22 | + (NSString *)ConvertHexStringToString:(NSString *)hexString; 23 | //普通字符串转换为十六进制 24 | + (NSString *)ConvertStringToHexString:(NSString *)string; 25 | //int转data 26 | +(NSData *)ConvertIntToData:(int)i; 27 | //data转int 28 | +(int)ConvertDataToInt:(NSData *)data; 29 | //十六进制转换为普通字符串的。 30 | + (NSData *)ConvertHexStringToData:(NSString *)hexString; 31 | //根据UUIDString查找CBCharacteristic 32 | +(CBCharacteristic *)findCharacteristicFormServices:(NSMutableArray *)services 33 | UUIDString:(NSString *)UUIDString; 34 | @end 35 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyCallback.m: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | */ 6 | 7 | // Created by 刘彦玮 on 15/9/2. 8 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 9 | // 10 | 11 | #import "BabyCallback.h" 12 | 13 | @implementation BabyCallback 14 | 15 | 16 | - (instancetype)init { 17 | self = [super init]; 18 | if (self) { 19 | [self setFilterOnDiscoverPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) { 20 | if (![peripheralName isEqualToString:@""]) { 21 | return YES; 22 | } 23 | return NO; 24 | }]; 25 | [self setFilterOnconnectToPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) { 26 | if (![peripheralName isEqualToString:@""]) { 27 | return YES; 28 | } 29 | return NO; 30 | }]; 31 | } 32 | return self; 33 | } 34 | @end 35 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/common/BluetoothServiceType.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.common; 2 | 3 | import android.bluetooth.BluetoothClass; 4 | 5 | /** 6 | * @Description: BLE服务类型 7 | * @author: DAWI 8 | * @date: 16/8/7 22:07. 9 | */ 10 | public enum BluetoothServiceType { 11 | AUDIO(BluetoothClass.Service.AUDIO), //音频服务 12 | CAPTURE(BluetoothClass.Service.CAPTURE), //捕捉服务 13 | INFORMATION(BluetoothClass.Service.INFORMATION), //信息服务 14 | LIMITED_DISCOVERABILITY(BluetoothClass.Service.LIMITED_DISCOVERABILITY), //有限发现服务 15 | NETWORKING(BluetoothClass.Service.NETWORKING), //网络服务 16 | OBJECT_TRANSFER(BluetoothClass.Service.OBJECT_TRANSFER), //对象传输服务 17 | POSITIONING(BluetoothClass.Service.POSITIONING), //定位服务 18 | RENDER(BluetoothClass.Service.RENDER), //给予服务 19 | TELEPHONY(BluetoothClass.Service.TELEPHONY); //电话服务 20 | 21 | private int code; 22 | 23 | BluetoothServiceType(int code) { 24 | this.code = code; 25 | } 26 | 27 | public int getCode() { 28 | return this.code; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/core/LruHashMap.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.core; 2 | 3 | import java.util.LinkedHashMap; 4 | 5 | /** 6 | * @Description: Lru算法实现的HashMap 7 | * @author: DAWI 8 | * @date: 2017/9/28 19:57 9 | */ 10 | public class LruHashMap extends LinkedHashMap { 11 | private final int MAX_SAVE_SIZE; 12 | 13 | public LruHashMap(int saveSize) { 14 | super((int) Math.ceil(saveSize / 0.75) + 1, 0.75f, true); 15 | MAX_SAVE_SIZE = saveSize; 16 | } 17 | 18 | @Override 19 | protected boolean removeEldestEntry(Entry eldest) { 20 | if (size() > MAX_SAVE_SIZE && eldest.getValue() instanceof DeviceMirror) { 21 | ((DeviceMirror) eldest.getValue()).disconnect(); 22 | } 23 | return size() > MAX_SAVE_SIZE; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | StringBuilder sb = new StringBuilder(); 29 | for (Entry entry : entrySet()) { 30 | sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue())); 31 | } 32 | return sb.toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyRhythm.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief babybluetooth Rhythm用于检测蓝牙的任务执行情况,处理复杂的蓝牙流程操作 7 | 8 | */ 9 | // 10 | // Created by ZTELiuyw on 15/9/15. 11 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 12 | // 13 | 14 | #import 15 | #import "BabyDefine.h" 16 | 17 | 18 | @interface BabyRhythm : NSObject 19 | 20 | 21 | typedef void (^BBBeatsBreakBlock)(BabyRhythm *bry); 22 | typedef void (^BBBeatsOverBlock)(BabyRhythm *bry); 23 | 24 | //timer for beats 25 | @property (nonatomic, strong) NSTimer *beatsTimer; 26 | 27 | //beat interval 28 | @property NSInteger beatsInterval; 29 | 30 | 31 | 32 | #pragma mark beats 33 | //心跳 34 | - (void)beats; 35 | //主动中断心跳 36 | - (void)beatsBreak; 37 | //结束心跳,结束后会进入BlockOnBeatOver,并且结束后再不会在触发BlockOnBeatBreak 38 | - (void)beatsOver; 39 | //恢复心跳,beatsOver操作后可以使用beatsRestart恢复心跳,恢复后又可以进入BlockOnBeatBreak方法 40 | - (void)beatsRestart; 41 | 42 | //心跳中断的委托 43 | - (void)setBlockOnBeatsBreak:(void(^)(BabyRhythm *bry))block; 44 | //心跳结束的委托 45 | - (void)setBlockOnBeatsOver:(void(^)(BabyRhythm *bry))block; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabySpeaker.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief babybluetooth block查找和channel切换 7 | 8 | */ 9 | 10 | // Created by 刘彦玮 on 15/9/2. 11 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 12 | // 13 | 14 | #import "BabyCallback.h" 15 | #import 16 | 17 | 18 | @interface BabySpeaker : NSObject 19 | 20 | - (BabyCallback *)callback; 21 | - (BabyCallback *)callbackOnCurrChannel; 22 | - (BabyCallback *)callbackOnChnnel:(NSString *)channel; 23 | - (BabyCallback *)callbackOnChnnel:(NSString *)channel 24 | createWhenNotExist:(BOOL)createWhenNotExist; 25 | 26 | //切换频道 27 | - (void)switchChannel:(NSString *)channel; 28 | 29 | //添加到notify list 30 | - (void)addNotifyCallback:(CBCharacteristic *)c 31 | withBlock:(void(^)(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error))block; 32 | 33 | //添加到notify list 34 | - (void)removeNotifyCallback:(CBCharacteristic *)c; 35 | 36 | //获取notify list 37 | - (NSMutableDictionary *)notifyCallBackList; 38 | 39 | //获取notityBlock 40 | - (void(^)(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error))notifyCallback:(CBCharacteristic *)c; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/BleException.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception; 2 | 3 | import com.vise.baseble.common.BleExceptionCode; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @Description: BLE异常基类 9 | * @author: DAWI 10 | * @date: 16/8/14 10:28. 11 | */ 12 | public class BleException implements Serializable { 13 | private BleExceptionCode code; 14 | private String description; 15 | 16 | public BleException(BleExceptionCode code, String description) { 17 | this.code = code; 18 | this.description = description; 19 | } 20 | 21 | public BleExceptionCode getCode() { 22 | return code; 23 | } 24 | 25 | public BleException setCode(BleExceptionCode code) { 26 | this.code = code; 27 | return this; 28 | } 29 | 30 | public String getDescription() { 31 | return description; 32 | } 33 | 34 | public BleException setDescription(String description) { 35 | this.description = description; 36 | return this; 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "BleException{" + 42 | "code=" + code + 43 | ", description='" + description + '\'' + 44 | '}'; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/handler/DefaultBleExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception.handler; 2 | 3 | import com.vise.baseble.exception.ConnectException; 4 | import com.vise.baseble.exception.GattException; 5 | import com.vise.baseble.exception.InitiatedException; 6 | import com.vise.baseble.exception.OtherException; 7 | import com.vise.baseble.exception.TimeoutException; 8 | import com.vise.log.ViseLog; 9 | 10 | /** 11 | * @Description: 异常默认处理 12 | * @author: DAWI 13 | * @date: 16/8/14 10:35. 14 | */ 15 | public class DefaultBleExceptionHandler extends BleExceptionHandler { 16 | @Override 17 | protected void onConnectException(ConnectException e) { 18 | ViseLog.e(e.getDescription()); 19 | } 20 | 21 | @Override 22 | protected void onGattException(GattException e) { 23 | ViseLog.e(e.getDescription()); 24 | } 25 | 26 | @Override 27 | protected void onTimeoutException(TimeoutException e) { 28 | ViseLog.e(e.getDescription()); 29 | } 30 | 31 | @Override 32 | protected void onInitiatedException(InitiatedException e) { 33 | ViseLog.e(e.getDescription()); 34 | } 35 | 36 | @Override 37 | protected void onOtherException(OtherException e) { 38 | ViseLog.e(e.getDescription()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/common/BleConstant.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.common; 2 | 3 | /** 4 | * @Description: BLE常量 5 | * @author: DAWI 6 | * @date: 16/8/20 20:31. 7 | */ 8 | public class BleConstant { 9 | public static final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb"; 10 | 11 | public static final int TIME_FOREVER = -1; 12 | 13 | public static final int DEFAULT_SCAN_TIME = 20000; 14 | public static final int DEFAULT_CONN_TIME = 10000; 15 | public static final int DEFAULT_OPERATE_TIME = 5000; 16 | 17 | public static final int DEFAULT_RETRY_INTERVAL = 1000; 18 | public static final int DEFAULT_RETRY_COUNT = 3; 19 | 20 | public static final int DEFAULT_MAX_CONNECT_COUNT = 5; 21 | 22 | public static final int MSG_CONNECT_TIMEOUT = 0x01; 23 | public static final int MSG_WRITE_DATA_TIMEOUT = 0x02; 24 | public static final int MSG_READ_DATA_TIMEOUT = 0x03; 25 | public static final int MSG_RECEIVE_DATA_TIMEOUT = 0x04; 26 | public static final int MSG_CONNECT_RETRY = 0x05; 27 | public static final int MSG_WRITE_DATA_RETRY = 0x06; 28 | public static final int MSG_READ_DATA_RETRY = 0x07; 29 | public static final int MSG_RECEIVE_DATA_RETRY = 0x08; 30 | 31 | //yankee 32 | public static final int DEFAULT_SCAN_REPEAT_INTERVAL = -1; 33 | } 34 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/utils/BleUtil.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.utils; 2 | 3 | import android.app.Activity; 4 | import android.bluetooth.BluetoothAdapter; 5 | import android.bluetooth.BluetoothManager; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager; 9 | 10 | /** 11 | * @Description: 蓝牙基础操作工具类 12 | * @author: DAWI 13 | * @date: 16/8/5 20:43. 14 | */ 15 | public class BleUtil { 16 | public static void enableBluetooth(Activity activity, int requestCode) { 17 | Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 18 | activity.startActivityForResult(intent, requestCode); 19 | } 20 | 21 | public static boolean isSupportBle(Context context) { 22 | if (context == null || !context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { 23 | return false; 24 | } 25 | BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); 26 | return manager.getAdapter() != null; 27 | } 28 | 29 | public static boolean isBleEnable(Context context) { 30 | if (!isSupportBle(context)) { 31 | return false; 32 | } 33 | BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); 34 | return manager.getAdapter().isEnabled(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/ConnectException.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | 5 | import com.vise.baseble.common.BleExceptionCode; 6 | 7 | /** 8 | * @Description: 连接异常 9 | * @author: DAWI 10 | * @date: 16/8/14 10:29. 11 | */ 12 | public class ConnectException extends BleException { 13 | private BluetoothGatt bluetoothGatt; 14 | private int gattStatus; 15 | 16 | public ConnectException(BluetoothGatt bluetoothGatt, int gattStatus) { 17 | super(BleExceptionCode.CONNECT_ERR, "Connect Exception Occurred! "); 18 | this.bluetoothGatt = bluetoothGatt; 19 | this.gattStatus = gattStatus; 20 | } 21 | 22 | public int getGattStatus() { 23 | return gattStatus; 24 | } 25 | 26 | public ConnectException setGattStatus(int gattStatus) { 27 | this.gattStatus = gattStatus; 28 | return this; 29 | } 30 | 31 | public BluetoothGatt getBluetoothGatt() { 32 | return bluetoothGatt; 33 | } 34 | 35 | public ConnectException setBluetoothGatt(BluetoothGatt bluetoothGatt) { 36 | this.bluetoothGatt = bluetoothGatt; 37 | return this; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "ConnectException{" + 43 | "gattStatus=" + gattStatus + 44 | ", bluetoothGatt=" + bluetoothGatt + 45 | "} " + super.toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/UuidFilterScanCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import android.os.ParcelUuid; 4 | 5 | import com.vise.baseble.model.BluetoothLeDevice; 6 | 7 | import java.util.UUID; 8 | 9 | /** 10 | * @Description: 根据指定uuid过滤设备 11 | * @author: DAWI 12 | * @date: 17/9/12 23:20. 13 | */ 14 | public class UuidFilterScanCallback extends ScanCallback { 15 | private UUID uuid;//设备uuid 16 | 17 | public UuidFilterScanCallback(IScanCallback scanCallback) { 18 | super(scanCallback); 19 | } 20 | 21 | public UuidFilterScanCallback setUuid(String uuid) { 22 | this.uuid = UUID.fromString(uuid); 23 | return this; 24 | } 25 | 26 | public UuidFilterScanCallback setUuid(UUID uuid) { 27 | this.uuid = uuid; 28 | return this; 29 | } 30 | 31 | @Override 32 | public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { 33 | BluetoothLeDevice tempDevice = null; 34 | if (bluetoothLeDevice != null && bluetoothLeDevice.getDevice() != null 35 | && bluetoothLeDevice.getDevice().getUuids() != null 36 | && bluetoothLeDevice.getDevice().getUuids().length > 0) { 37 | for (ParcelUuid parcelUuid : bluetoothLeDevice.getDevice().getUuids()) { 38 | if (uuid != null && uuid == parcelUuid.getUuid()) { 39 | tempDevice = bluetoothLeDevice; 40 | } 41 | } 42 | } 43 | return tempDevice; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/SandPluginBluetoothProxy.m: -------------------------------------------------------------------------------- 1 | // 2 | // SandPluginBluetoothProxy.m 3 | // sand-plugin-bluetooth 4 | // 5 | // Created by Qianjiao Wang on 2022/6/15. 6 | // 7 | 8 | 9 | #import "SandPluginBluetoothProxy.h" 10 | #import 11 | #import "BabyBluetooth.h" 12 | 13 | @implementation SandPluginBluetoothProxy 14 | 15 | - (void)onCreateUniPlugin { 16 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 17 | } 18 | 19 | - (BOOL)application:(UIApplication *_Nullable)application didFinishLaunchingWithOptions:(NSDictionary *_Nullable)launchOptions { 20 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 21 | NSArray *centralManagerIdentifiers = launchOptions[UIApplicationLaunchOptionsBluetoothCentralsKey]; 22 | NSLog(@"APP正常激活?%@",centralManagerIdentifiers); 23 | return YES; 24 | } 25 | 26 | - (void)applicationWillResignActive:(UIApplication * _Nullable)application { 27 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 28 | } 29 | 30 | - (void)applicationDidBecomeActive:(UIApplication *_Nullable)application { 31 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 32 | } 33 | 34 | - (void)applicationDidEnterBackground:(UIApplication *_Nullable)application { 35 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 36 | } 37 | 38 | - (void)applicationWillEnterForeground:(UIApplication *_Nullable)application { 39 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 40 | } 41 | 42 | - (void)applicationWillTerminate:(UIApplication *_Nullable)application { 43 | NSLog(@"UniPluginProtocol Func: %@,%s",self,__func__); 44 | } 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /android/uniplugin_module/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion '29.0.2' 6 | 7 | defaultConfig { 8 | minSdkVersion 20 9 | targetSdkVersion 31 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | consumerProguardFiles "consumer-rules.pro" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | 28 | } 29 | 30 | repositories { 31 | flatDir { 32 | dirs 'libs' 33 | } 34 | } 35 | 36 | dependencies { 37 | compileOnly fileTree(dir: 'libs', include: ['*.jar']) 38 | 39 | compileOnly fileTree(dir: '../app/libs', include: ['uniapp-v8-release.aar']) 40 | 41 | compileOnly 'androidx.recyclerview:recyclerview:1.0.0' 42 | compileOnly 'androidx.legacy:legacy-support-v4:1.0.0' 43 | compileOnly 'androidx.appcompat:appcompat:1.0.0' 44 | implementation 'com.alibaba:fastjson:1.2.83' 45 | implementation 'com.facebook.fresco:fresco:1.13.0' 46 | implementation 'com.vise.xiaoyaoyou:viselog:1.1.2' 47 | 48 | /*implementation 'com.android.support:appcompat-v7:28.0.0' 49 | testImplementation 'junit:junit:4.12' 50 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 51 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'*/ 52 | } 53 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyOptions.m: -------------------------------------------------------------------------------- 1 | // 2 | // BabyClothes.m 3 | // BabyBluetoothAppDemo 4 | // 5 | // Created by 刘彦玮 on 15/9/27. 6 | // Copyright © 2015年 刘彦玮. All rights reserved. 7 | // 8 | 9 | #import "BabyOptions.h" 10 | 11 | @implementation BabyOptions 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | if (self) { 16 | _scanForPeripheralsWithOptions = nil; 17 | _connectPeripheralWithOptions = nil; 18 | _scanForPeripheralsWithServices = nil; 19 | _discoverWithServices = nil; 20 | _discoverWithCharacteristics = nil; 21 | } 22 | return self; 23 | } 24 | 25 | - (instancetype)initWithscanForPeripheralsWithOptions:(NSDictionary *)scanForPeripheralsWithOptions 26 | connectPeripheralWithOptions:(NSDictionary *)connectPeripheralWithOptions 27 | { 28 | self = [super init]; 29 | if (self) { 30 | [self setScanForPeripheralsWithOptions:scanForPeripheralsWithOptions]; 31 | [self setConnectPeripheralWithOptions:connectPeripheralWithOptions]; 32 | } 33 | return self; 34 | } 35 | 36 | - (instancetype)initWithscanForPeripheralsWithOptions:(NSDictionary *)scanForPeripheralsWithOptions 37 | connectPeripheralWithOptions:(NSDictionary *)connectPeripheralWithOptions 38 | scanForPeripheralsWithServices:(NSArray *)scanForPeripheralsWithServices 39 | discoverWithServices:(NSArray *)discoverWithServices 40 | discoverWithCharacteristics:(NSArray *)discoverWithCharacteristics 41 | { 42 | self = [self initWithscanForPeripheralsWithOptions:scanForPeripheralsWithOptions connectPeripheralWithOptions:connectPeripheralWithOptions]; 43 | if (self) { 44 | [self setScanForPeripheralsWithServices:scanForPeripheralsWithServices]; 45 | [self setDiscoverWithServices:discoverWithServices]; 46 | [self setDiscoverWithCharacteristics:discoverWithCharacteristics]; 47 | } 48 | return self; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/ListFilterScanCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import com.vise.baseble.model.BluetoothLeDevice; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @Description: 指定设备集合进行过滤,一般用设备名称和Mac地址集合 9 | * @author: DAWI 10 | * @date: 17/9/12 22:50. 11 | */ 12 | public class ListFilterScanCallback extends ScanCallback { 13 | private List deviceNameList;//指定设备名称集合 14 | private List deviceMacList;//指定设备Mac地址集合 15 | 16 | public ListFilterScanCallback(IScanCallback scanCallback) { 17 | super(scanCallback); 18 | } 19 | 20 | public ListFilterScanCallback setDeviceNameList(List deviceNameList) { 21 | this.deviceNameList = deviceNameList; 22 | return this; 23 | } 24 | 25 | public ListFilterScanCallback setDeviceMacList(List deviceMacList) { 26 | this.deviceMacList = deviceMacList; 27 | return this; 28 | } 29 | 30 | @Override 31 | public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { 32 | BluetoothLeDevice tempDevice = null; 33 | if (deviceNameList != null && deviceNameList.size() > 0) { 34 | for (String deviceName : deviceNameList) { 35 | if (bluetoothLeDevice != null && bluetoothLeDevice.getName() != null && deviceName != null 36 | && deviceName.equalsIgnoreCase(bluetoothLeDevice.getName().trim())) { 37 | tempDevice = bluetoothLeDevice; 38 | } 39 | } 40 | } else if (deviceMacList != null && deviceMacList.size() > 0) { 41 | for (String deviceMac : deviceMacList) { 42 | if (bluetoothLeDevice != null && bluetoothLeDevice.getAddress() != null && deviceMac != null 43 | && deviceMac.equalsIgnoreCase(bluetoothLeDevice.getAddress().trim())) { 44 | tempDevice = bluetoothLeDevice; 45 | } 46 | } 47 | } 48 | return tempDevice; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/exception/handler/BleExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.exception.handler; 2 | 3 | import com.vise.baseble.exception.BleException; 4 | import com.vise.baseble.exception.ConnectException; 5 | import com.vise.baseble.exception.GattException; 6 | import com.vise.baseble.exception.InitiatedException; 7 | import com.vise.baseble.exception.OtherException; 8 | import com.vise.baseble.exception.TimeoutException; 9 | 10 | /** 11 | * @Description: 异常处理 12 | * @author: DAWI 13 | * @date: 16/8/14 10:35. 14 | */ 15 | public abstract class BleExceptionHandler { 16 | public BleExceptionHandler handleException(BleException exception) { 17 | if (exception != null) { 18 | if (exception instanceof ConnectException) { 19 | onConnectException((ConnectException) exception); 20 | } else if (exception instanceof GattException) { 21 | onGattException((GattException) exception); 22 | } else if (exception instanceof TimeoutException) { 23 | onTimeoutException((TimeoutException) exception); 24 | } else if (exception instanceof InitiatedException) { 25 | onInitiatedException((InitiatedException) exception); 26 | } else { 27 | onOtherException((OtherException) exception); 28 | } 29 | } 30 | return this; 31 | } 32 | 33 | /** 34 | * connect failed 35 | */ 36 | protected abstract void onConnectException(ConnectException e); 37 | 38 | /** 39 | * gatt error status 40 | */ 41 | protected abstract void onGattException(GattException e); 42 | 43 | /** 44 | * operation timeout 45 | */ 46 | protected abstract void onTimeoutException(TimeoutException e); 47 | 48 | /** 49 | * operation inititiated error 50 | */ 51 | protected abstract void onInitiatedException(InitiatedException e); 52 | 53 | /** 54 | * other exceptions 55 | */ 56 | protected abstract void onOtherException(OtherException e); 57 | } 58 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/RegularFilterScanCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.vise.baseble.model.BluetoothLeDevice; 6 | 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * @Description: 根据正则过滤扫描设备,这里设置的是根据一定信号范围内指定正则设备名称的过滤 12 | * @author: DAWI 13 | * @date: 17/9/12 22:19. 14 | */ 15 | public class RegularFilterScanCallback extends ScanCallback { 16 | private Pattern pattern; 17 | private Matcher matcher; 18 | private String regularDeviceName;//正则表达式表示的设备名称 19 | private int deviceRssi;//设备的信号 20 | 21 | public RegularFilterScanCallback(IScanCallback scanCallback) { 22 | super(scanCallback); 23 | pattern = Pattern.compile("^[\\x00-\\xff]*$"); 24 | } 25 | 26 | public RegularFilterScanCallback setRegularDeviceName(String regularDeviceName) { 27 | this.regularDeviceName = regularDeviceName; 28 | if (!TextUtils.isEmpty(this.regularDeviceName)) { 29 | pattern = Pattern.compile(this.regularDeviceName); 30 | } 31 | return this; 32 | } 33 | 34 | public RegularFilterScanCallback setDeviceRssi(int deviceRssi) { 35 | this.deviceRssi = deviceRssi; 36 | return this; 37 | } 38 | 39 | @Override 40 | public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { 41 | BluetoothLeDevice tempDevice = null; 42 | String tempName = bluetoothLeDevice.getName(); 43 | int tempRssi = bluetoothLeDevice.getRssi(); 44 | if (!TextUtils.isEmpty(tempName)) { 45 | matcher = pattern.matcher(tempName); 46 | if (this.deviceRssi < 0) { 47 | if (matcher.matches() && tempRssi >= this.deviceRssi) { 48 | tempDevice = bluetoothLeDevice; 49 | } 50 | } else { 51 | if (matcher.matches()) { 52 | tempDevice = bluetoothLeDevice; 53 | } 54 | } 55 | } 56 | return tempDevice; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyRhythm.m: -------------------------------------------------------------------------------- 1 | // 2 | // BabyBeats.m 3 | // BabyBluetoothAppDemo 4 | // 5 | // Created by ZTELiuyw on 15/9/15. 6 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 7 | // 8 | 9 | #import "BabyRhythm.h" 10 | #import "BabyDefine.h" 11 | 12 | @implementation BabyRhythm { 13 | BOOL isOver; 14 | BBBeatsBreakBlock blockOnBeatBreak; 15 | BBBeatsOverBlock blockOnBeatOver; 16 | } 17 | 18 | - (instancetype)init { 19 | self = [super init]; 20 | if (self) { 21 | //beatsInterval 22 | _beatsInterval = KBABYRHYTHM_BEATS_DEFAULT_INTERVAL; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)beats { 28 | 29 | if (isOver) { 30 | BabyLog(@">>>beats isOver"); 31 | return; 32 | } 33 | 34 | BabyLog(@">>>beats at :%@",[NSDate date]); 35 | if (self.beatsTimer) { 36 | [self.beatsTimer setFireDate: [[NSDate date]dateByAddingTimeInterval:self.beatsInterval]]; 37 | } 38 | else { 39 | self.beatsTimer = [NSTimer timerWithTimeInterval:self.beatsInterval target:self selector:@selector(beatsBreak) userInfo:nil repeats:YES]; 40 | [self.beatsTimer setFireDate: [[NSDate date]dateByAddingTimeInterval:self.beatsInterval]]; 41 | [[NSRunLoop currentRunLoop] addTimer:self.beatsTimer forMode:NSRunLoopCommonModes]; 42 | } 43 | } 44 | 45 | - (void)beatsBreak { 46 | BabyLog(@">>>beatsBreak :%@",[NSDate date]); 47 | [self.beatsTimer setFireDate:[NSDate distantFuture]]; 48 | if (blockOnBeatBreak) { 49 | blockOnBeatBreak(self); 50 | } 51 | } 52 | 53 | - (void)beatsOver { 54 | BabyLog(@">>>beatsOver :%@",[NSDate date]); 55 | [self.beatsTimer setFireDate:[NSDate distantFuture]]; 56 | isOver = YES; 57 | if (blockOnBeatOver) { 58 | blockOnBeatOver(self); 59 | } 60 | 61 | } 62 | 63 | - (void)beatsRestart { 64 | BabyLog(@">>>beatsRestart :%@",[NSDate date]); 65 | isOver = NO; 66 | [self beats]; 67 | } 68 | 69 | - (void)setBlockOnBeatsBreak:(void(^)(BabyRhythm *bry))block { 70 | blockOnBeatBreak = block; 71 | } 72 | 73 | - (void)setBlockOnBeatsOver:(void(^)(BabyRhythm *bry))block { 74 | blockOnBeatOver = block; 75 | } 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyPeripheralManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief 蓝牙外设模式实现类 7 | 8 | */ 9 | 10 | 11 | // Created by 刘彦玮 on 15/12/12. 12 | // Copyright © 2015年 刘彦玮. All rights reserved. 13 | // 14 | 15 | #import 16 | #import 17 | #import "BabyToy.h" 18 | #import "BabySpeaker.h" 19 | 20 | 21 | @interface BabyPeripheralManager : NSObject { 22 | 23 | @public 24 | //回叫方法 25 | BabySpeaker *babySpeaker; 26 | } 27 | 28 | /** 29 | 添加服务 30 | */ 31 | - (BabyPeripheralManager *(^)(NSArray *array))addServices; 32 | 33 | /** 34 | 启动广播 35 | */ 36 | - (BabyPeripheralManager *(^)())startAdvertising; 37 | 38 | //外设管理器 39 | @property (nonatomic, strong) CBPeripheralManager *peripheralManager; 40 | @property (nonatomic, copy) NSString *localName; 41 | @property (nonatomic, strong) NSMutableArray *services; 42 | 43 | @end 44 | 45 | 46 | /** 47 | * 构造Characteristic,并加入service 48 | * service:CBService 49 | 50 | * param`ter for properties :option 'r' | 'w' | 'n' or combination 51 | * r CBCharacteristicPropertyRead 52 | * w CBCharacteristicPropertyWrite 53 | * n CBCharacteristicPropertyNotify 54 | * default value is rw Read-Write 55 | 56 | * paramter for descriptor:be uesd descriptor for characteristic 57 | */ 58 | 59 | void makeCharacteristicToService(CBMutableService *service,NSString *UUID,NSString *properties,NSString *descriptor); 60 | 61 | /** 62 | * 构造一个包含初始值的Characteristic,并加入service,包含了初值的characteristic必须设置permissions和properties都为只读 63 | * make characteristic then add to service, a static characteristic mean it has a initial value .according apple rule, it must set properties and permissions to CBCharacteristicPropertyRead and CBAttributePermissionsReadable 64 | */ 65 | void makeStaticCharacteristicToService(CBMutableService *service,NSString *UUID,NSString *descriptor,NSData *data); 66 | /** 67 | 生成CBService 68 | */ 69 | CBMutableService* makeCBService(NSString *UUID); 70 | 71 | /** 72 | 生成UUID 73 | */ 74 | NSString* genUUID(); 75 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/model/BluetoothLeDeviceStore.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.Comparator; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * @Description: 设备信息集合 12 | * @author: DAWI 13 | * @date: 16/8/21 16:48. 14 | */ 15 | public class BluetoothLeDeviceStore { 16 | 17 | private final Map mDeviceMap; 18 | 19 | public BluetoothLeDeviceStore() { 20 | mDeviceMap = new HashMap<>(); 21 | } 22 | 23 | public void addDevice(BluetoothLeDevice device) { 24 | if (device == null) { 25 | return; 26 | } 27 | if (mDeviceMap.containsKey(device.getAddress())) { 28 | mDeviceMap.get(device.getAddress()).updateRssiReading(device.getTimestamp(), device.getRssi()); 29 | } else { 30 | mDeviceMap.put(device.getAddress(), device); 31 | } 32 | } 33 | 34 | public void removeDevice(BluetoothLeDevice device) { 35 | if (device == null) { 36 | return; 37 | } 38 | if (mDeviceMap.containsKey(device.getAddress())) { 39 | mDeviceMap.remove(device.getAddress()); 40 | } 41 | } 42 | 43 | public void clear() { 44 | mDeviceMap.clear(); 45 | } 46 | 47 | public Map getDeviceMap() { 48 | return mDeviceMap; 49 | } 50 | 51 | public List getDeviceList() { 52 | final List methodResult = new ArrayList<>(mDeviceMap.values()); 53 | 54 | Collections.sort(methodResult, new Comparator() { 55 | 56 | @Override 57 | public int compare(final BluetoothLeDevice arg0, final BluetoothLeDevice arg1) { 58 | return arg0.getAddress().compareToIgnoreCase(arg1.getAddress()); 59 | } 60 | }); 61 | 62 | return methodResult; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "BluetoothLeDeviceStore{" + 68 | "DeviceList=" + getDeviceList() + 69 | '}'; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/SingleFilterScanCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import com.vise.baseble.ViseBle; 4 | import com.vise.baseble.model.BluetoothLeDevice; 5 | 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | /** 9 | * @Description: 设置扫描指定的单个设备,一般是设备名称和Mac地址 10 | * @author: DAWI 11 | * @date: 17/9/12 22:16. 12 | */ 13 | public class SingleFilterScanCallback extends ScanCallback { 14 | private AtomicBoolean hasFound = new AtomicBoolean(false); 15 | private String deviceName;//指定设备名称 16 | private String deviceMac;//指定设备Mac地址 17 | 18 | public SingleFilterScanCallback(IScanCallback scanCallback) { 19 | super(scanCallback); 20 | } 21 | 22 | public ScanCallback setDeviceName(String deviceName) { 23 | this.deviceName = deviceName; 24 | return this; 25 | } 26 | 27 | public ScanCallback setDeviceMac(String deviceMac) { 28 | this.deviceMac = deviceMac; 29 | return this; 30 | } 31 | 32 | @Override 33 | public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { 34 | BluetoothLeDevice tempDevice = null; 35 | if (!hasFound.get()) { 36 | if (bluetoothLeDevice != null && bluetoothLeDevice.getAddress() != null && deviceMac != null 37 | && deviceMac.equalsIgnoreCase(bluetoothLeDevice.getAddress().trim())) { 38 | hasFound.set(true); 39 | isScanning = false; 40 | removeHandlerMsg(); 41 | ViseBle.getInstance().stopScan(SingleFilterScanCallback.this); 42 | tempDevice = bluetoothLeDevice; 43 | bluetoothLeDeviceStore.addDevice(bluetoothLeDevice); 44 | scanCallback.onScanFinish(bluetoothLeDeviceStore); 45 | } else if (bluetoothLeDevice != null && bluetoothLeDevice.getName() != null && deviceName != null 46 | && deviceName.equalsIgnoreCase(bluetoothLeDevice.getName().trim())) { 47 | hasFound.set(true); 48 | isScanning = false; 49 | removeHandlerMsg(); 50 | ViseBle.getInstance().stopScan(SingleFilterScanCallback.this); 51 | tempDevice = bluetoothLeDevice; 52 | bluetoothLeDeviceStore.addDevice(bluetoothLeDevice); 53 | scanCallback.onScanFinish(bluetoothLeDeviceStore); 54 | } 55 | } 56 | return tempDevice; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyCentralManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief 蓝牙中心模式实现类 7 | 8 | */ 9 | 10 | // Created by 刘彦玮 on 15/7/30. 11 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 12 | // 13 | 14 | #import 15 | #import 16 | #import "BabyToy.h" 17 | #import "BabySpeaker.h" 18 | #import "BabyDefine.h" 19 | 20 | 21 | 22 | @interface BabyCentralManager : NSObject { 23 | 24 | @public 25 | 26 | //方法是否处理 27 | BOOL needScanForPeripherals;//是否扫描Peripherals 28 | BOOL needConnectPeripheral;//是否连接Peripherals 29 | BOOL needDiscoverServices;//是否发现Services 30 | BOOL needDiscoverCharacteristics;//是否获取Characteristics 31 | BOOL needReadValueForCharacteristic;//是否获取(更新)Characteristics的值 32 | BOOL needDiscoverDescriptorsForCharacteristic;//是否获取Characteristics的描述 33 | BOOL needReadValueForDescriptors;//是否获取Descriptors的值 34 | 35 | //一次性处理 36 | BOOL oneReadValueForDescriptors; 37 | 38 | //方法执行时间 39 | int executeTime; 40 | NSTimer *connectTimer; 41 | //pocket 42 | NSMutableDictionary *pocket; 43 | 44 | //主设备 45 | CBCentralManager *centralManager; 46 | //回叫方法 47 | BabySpeaker *babySpeaker; 48 | 49 | @private 50 | //已经连接的设备 51 | NSMutableArray *connectedPeripherals; 52 | //已经连接的设备 53 | NSMutableArray *discoverPeripherals; 54 | //需要自动重连的外设 55 | NSMutableArray *reConnectPeripherals; 56 | //无需扫描即可恢复的设备 57 | NSMutableArray *reStorePeripherals; 58 | } 59 | 60 | 61 | 62 | 63 | //扫描Peripherals 64 | - (void)scanPeripherals; 65 | //连接Peripherals 66 | - (void)connectToPeripheral:(CBPeripheral *)peripheral; 67 | //断开设备连接 68 | - (void)cancelPeripheralConnection:(CBPeripheral *)peripheral; 69 | //断开所有已连接的设备 70 | - (void)cancelAllPeripheralsConnection; 71 | //停止扫描 72 | - (void)cancelScan; 73 | 74 | //获取当前连接的peripherals 75 | - (NSArray *)findConnectedPeripherals; 76 | 77 | - (NSArray *)findReStorePeripherals; 78 | 79 | //获取当前连接的peripheral 80 | - (CBPeripheral *)findConnectedPeripheral:(NSString *)peripheralName; 81 | 82 | /** 83 | sometimes ever,sometimes never. 相聚有时,后会无期 84 | 85 | this is center with peripheral's story 86 | **/ 87 | 88 | //sometimes ever:添加断开重连接的设备 89 | - (void)sometimes_ever:(CBPeripheral *)peripheral ; 90 | //sometimes never:删除需要重连接的设备 91 | - (void)sometimes_never:(CBPeripheral *)peripheral ; 92 | 93 | @end 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth.xcodeproj/xcshareddata/xcschemes/sand-plugin-bluetooth.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyToy.m: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | */ 6 | 7 | // Created by 刘彦玮 on 15/8/1. 8 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 9 | // 10 | 11 | #import "BabyToy.h" 12 | 13 | @implementation BabyToy 14 | 15 | 16 | //十六进制转换为普通字符串的。 17 | + (NSString *)ConvertHexStringToString:(NSString *)hexString { 18 | 19 | char *myBuffer = (char *)malloc((int)[hexString length] / 2 + 1); 20 | bzero(myBuffer, [hexString length] / 2 + 1); 21 | for (int i = 0; i < [hexString length] - 1; i += 2) { 22 | unsigned int anInt; 23 | NSString * hexCharStr = [hexString substringWithRange:NSMakeRange(i, 2)]; 24 | NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr]; 25 | [scanner scanHexInt:&anInt]; 26 | myBuffer[i / 2] = (char)anInt; 27 | } 28 | NSString *unicodeString = [NSString stringWithCString:myBuffer encoding:4]; 29 | // BabyLog(@"===字符串===%@",unicodeString); 30 | return unicodeString; 31 | } 32 | 33 | //普通字符串转换为十六进制 34 | + (NSString *)ConvertStringToHexString:(NSString *)string { 35 | NSData *myD = [string dataUsingEncoding:NSUTF8StringEncoding]; 36 | Byte *bytes = (Byte *)[myD bytes]; 37 | //下面是Byte 转换为16进制。 38 | NSString *hexStr=@""; 39 | for (int i=0;i<[myD length];i++) { 40 | NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff];///16进制数 41 | 42 | if ([newHexStr length]==1) { 43 | hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr]; 44 | } 45 | else{ 46 | hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr]; 47 | } 48 | 49 | } 50 | return hexStr; 51 | } 52 | 53 | 54 | //int转data 55 | + (NSData *)ConvertIntToData:(int)i { 56 | 57 | NSData *data = [NSData dataWithBytes: &i length: sizeof(i)]; 58 | return data; 59 | } 60 | 61 | //data转int 62 | + (int)ConvertDataToInt:(NSData *)data { 63 | int i; 64 | [data getBytes:&i length:sizeof(i)]; 65 | return i; 66 | } 67 | 68 | //十六进制转换为普通字符串的。 69 | + (NSData *)ConvertHexStringToData:(NSString *)hexString { 70 | 71 | NSData *data = [[BabyToy ConvertHexStringToString:hexString] dataUsingEncoding:NSUTF8StringEncoding]; 72 | return data; 73 | } 74 | 75 | 76 | //根据UUIDString查找CBCharacteristic 77 | + (CBCharacteristic *)findCharacteristicFormServices:(NSMutableArray *)services 78 | UUIDString:(NSString *)UUIDString { 79 | for (CBService *s in services) { 80 | for (CBCharacteristic *c in s.characteristics) { 81 | if ([c.UUID.UUIDString isEqualToString:UUIDString]) { 82 | return c; 83 | } 84 | } 85 | } 86 | return nil; 87 | } 88 | 89 | @end 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyDefine.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief 预定义一些库的执行行为和配置 7 | 8 | */ 9 | 10 | // Created by 刘彦玮 on 6/4/19. 11 | // Copyright © 2016年 liuyanwei. All rights reserved. 12 | // 13 | 14 | #import 15 | 16 | 17 | # pragma mark - baby 行为定义 18 | 19 | //Baby if show log 是否打印日志,默认1:打印 ,0:不打印 20 | #define KBABY_IS_SHOW_LOG 1 21 | 22 | //CBcentralManager等待设备打开次数 23 | # define KBABY_CENTRAL_MANAGER_INIT_WAIT_TIMES 5 24 | 25 | //CBcentralManager等待设备打开间隔时间 26 | # define KBABY_CENTRAL_MANAGER_INIT_WAIT_SECOND 2.0 27 | 28 | //BabyRhythm默认心跳时间间隔 29 | #define KBABYRHYTHM_BEATS_DEFAULT_INTERVAL 3; 30 | 31 | //Baby默认链式方法channel名称 32 | #define KBABY_DETAULT_CHANNEL @"babyDefault" 33 | 34 | # pragma mark - baby通知 35 | 36 | //蓝牙系统通知 37 | //centralManager status did change notification 38 | #define BabyNotificationAtCentralManagerDidUpdateState @"BabyNotificationAtCentralManagerDidUpdateState" 39 | //did discover peripheral notification 40 | #define BabyNotificationAtDidDiscoverPeripheral @"BabyNotificationAtDidDiscoverPeripheral" 41 | //did connection peripheral notification 42 | #define BabyNotificationAtDidConnectPeripheral @"BabyNotificationAtDidConnectPeripheral" 43 | //did filed connect peripheral notification 44 | #define BabyNotificationAtDidFailToConnectPeripheral @"BabyNotificationAtDidFailToConnectPeripheral" 45 | //did disconnect peripheral notification 46 | #define BabyNotificationAtDidDisconnectPeripheral @"BabyNotificationAtDidDisconnectPeripheral" 47 | //did discover service notification 48 | #define BabyNotificationAtDidDiscoverServices @"BabyNotificationAtDidDiscoverServices" 49 | //did discover characteristics notification 50 | #define BabyNotificationAtDidDiscoverCharacteristicsForService @"BabyNotificationAtDidDiscoverCharacteristicsForService" 51 | //did read or notify characteristic when received value notification 52 | #define BabyNotificationAtDidUpdateValueForCharacteristic @"BabyNotificationAtDidUpdateValueForCharacteristic" 53 | //did write characteristic and response value notification 54 | #define BabyNotificationAtDidWriteValueForCharacteristic @"BabyNotificationAtDidWriteValueForCharacteristic" 55 | //did change characteristis notify status notification 56 | #define BabyNotificationAtDidUpdateNotificationStateForCharacteristic @"BabyNotificationAtDidUpdateNotificationStateForCharacteristic" 57 | //did read rssi and receiced value notification 58 | #define BabyNotificationAtDidReadRSSI @"BabyNotificationAtDidReadRSSI" 59 | 60 | //蓝牙扩展通知 61 | // did centralManager enable notification 62 | #define BabyNotificationAtCentralManagerEnable @"BabyNotificationAtCentralManagerEnable" 63 | 64 | 65 | 66 | # pragma mark - baby 定义的方法 67 | 68 | //Baby log 69 | #define BabyLog(fmt, ...) if(KBABY_IS_SHOW_LOG) { NSLog(fmt,##__VA_ARGS__); } 70 | 71 | 72 | 73 | 74 | 75 | @interface BabyDefine : NSObject 76 | 77 | @end 78 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyOptions.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief babybluetooth 封装蓝牙外设模式的运行时参数,可以实现后台模式,重复接收广播,查找service参数,查找characteristic参数 7 | 8 | */ 9 | 10 | // Created by 刘彦玮 on 15/9/27. 11 | // Copyright © 2015年 刘彦玮. All rights reserved. 12 | // 13 | 14 | #import 15 | #import 16 | 17 | @interface BabyOptions : NSObject 18 | 19 | #pragma mark - 属性 20 | /*! 21 | * 扫描参数,centralManager:scanForPeripheralsWithServices:self.scanForPeripheralsWithServices options:self.scanForPeripheralsWithOptions 22 | * @param An optional dictionary specifying options for the scan. 23 | * @see centralManager:scanForPeripheralsWithServices 24 | * @seealso CBCentralManagerScanOptionAllowDuplicatesKey :忽略同一个Peripheral端的多个发现事件被聚合成一个发现事件 25 | * @seealso CBCentralManagerScanOptionSolicitedServiceUUIDsKey 26 | */ 27 | @property (nonatomic, copy) NSDictionary *scanForPeripheralsWithOptions; 28 | 29 | /*! 30 | * 连接设备的参数 31 | * @method connectPeripheral:options: 32 | * @param An optional dictionary specifying connection behavior options. 33 | * @see centralManager:didConnectPeripheral: 34 | * @see centralManager:didFailToConnectPeripheral:error: 35 | * @seealso CBConnectPeripheralOptionNotifyOnConnectionKey 36 | * @seealso CBConnectPeripheralOptionNotifyOnDisconnectionKey 37 | * @seealso CBConnectPeripheralOptionNotifyOnNotificationKey 38 | */ 39 | @property (nonatomic, copy) NSDictionary *connectPeripheralWithOptions; 40 | 41 | 42 | 43 | /*! 44 | * 扫描参数,centralManager:scanForPeripheralsWithServices:self.scanForPeripheralsWithServices options:self.scanForPeripheralsWithOptions 45 | *@param serviceUUIDs A list of CBUUID objects representing the service(s) to scan for. 46 | *@see centralManager:scanForPeripheralsWithServices 47 | */ 48 | @property (nonatomic, copy) NSArray *scanForPeripheralsWithServices; 49 | 50 | // [peripheral discoverServices:self.discoverWithServices]; 51 | @property (nonatomic, copy) NSArray *discoverWithServices; 52 | 53 | // [peripheral discoverCharacteristics:self.discoverWithCharacteristics forService:service]; 54 | @property (nonatomic, copy) NSArray *discoverWithCharacteristics; 55 | 56 | 57 | #pragma mark - 构造方法 58 | - (instancetype)initWithscanForPeripheralsWithOptions:(NSDictionary *)scanForPeripheralsWithOptions 59 | connectPeripheralWithOptions:(NSDictionary *)connectPeripheralWithOptions; 60 | 61 | - (instancetype)initWithscanForPeripheralsWithOptions:(NSDictionary *)scanForPeripheralsWithOptions 62 | connectPeripheralWithOptions:(NSDictionary *)connectPeripheralWithOptions 63 | scanForPeripheralsWithServices:(NSArray *)scanForPeripheralsWithServices 64 | discoverWithServices:(NSArray *)discoverWithServices 65 | discoverWithCharacteristics:(NSArray *)discoverWithCharacteristics; 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabySpeaker.m: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | */ 6 | 7 | // Created by 刘彦玮 on 15/9/2. 8 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 9 | // 10 | 11 | #import "BabySpeaker.h" 12 | #import "BabyDefine.h" 13 | 14 | 15 | typedef NS_ENUM(NSUInteger, BabySpeakerType) { 16 | BabySpeakerTypeDiscoverPeripherals, 17 | BabySpeakerTypeConnectedPeripheral, 18 | BabySpeakerTypeDiscoverPeripheralsFailToConnect, 19 | BabySpeakerTypeDiscoverPeripheralsDisconnect, 20 | BabySpeakerTypeDiscoverPeripheralsDiscoverServices, 21 | BabySpeakerTypeDiscoverPeripheralsDiscoverCharacteristics, 22 | BabySpeakerTypeDiscoverPeripheralsReadValueForCharacteristic, 23 | BabySpeakerTypeDiscoverPeripheralsDiscoverDescriptorsForCharacteristic, 24 | BabySpeakerTypeDiscoverPeripheralsReadValueForDescriptorsBlock 25 | }; 26 | 27 | 28 | @implementation BabySpeaker { 29 | //所有委托频道 30 | NSMutableDictionary *channels; 31 | //当前委托频道 32 | NSString *currChannel; 33 | //notifyList 34 | NSMutableDictionary *notifyList; 35 | } 36 | 37 | - (instancetype)init { 38 | self = [super init]; 39 | if (self) { 40 | BabyCallback *defaultCallback = [[BabyCallback alloc]init]; 41 | notifyList = [[NSMutableDictionary alloc]init]; 42 | channels = [[NSMutableDictionary alloc]init]; 43 | currChannel = KBABY_DETAULT_CHANNEL; 44 | [channels setObject:defaultCallback forKey:KBABY_DETAULT_CHANNEL]; 45 | } 46 | return self; 47 | } 48 | 49 | - (BabyCallback *)callback { 50 | return [channels objectForKey:KBABY_DETAULT_CHANNEL]; 51 | } 52 | 53 | - (BabyCallback *)callbackOnCurrChannel { 54 | return [self callbackOnChnnel:currChannel]; 55 | } 56 | 57 | - (BabyCallback *)callbackOnChnnel:(NSString *)channel { 58 | if (!channel) { 59 | [self callback]; 60 | } 61 | return [channels objectForKey:channel]; 62 | } 63 | 64 | - (BabyCallback *)callbackOnChnnel:(NSString *)channel 65 | createWhenNotExist:(BOOL)createWhenNotExist { 66 | 67 | BabyCallback *callback = [channels objectForKey:channel]; 68 | if (!callback && createWhenNotExist) { 69 | callback = [[BabyCallback alloc]init]; 70 | [channels setObject:callback forKey:channel]; 71 | } 72 | 73 | return callback; 74 | } 75 | 76 | - (void)switchChannel:(NSString *)channel { 77 | if (channel) { 78 | if ([self callbackOnChnnel:channel]) { 79 | currChannel = channel; 80 | BabyLog(@">>>已切换到%@",channel); 81 | } 82 | else { 83 | BabyLog(@">>>所要切换的channel不存在"); 84 | } 85 | } 86 | else { 87 | currChannel = KBABY_DETAULT_CHANNEL; 88 | BabyLog(@">>>已切换到默认频道"); 89 | } 90 | } 91 | 92 | //添加到notify list 93 | - (void)addNotifyCallback:(CBCharacteristic *)c 94 | withBlock:(void(^)(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error))block { 95 | [notifyList setObject:block forKey:c.UUID.description]; 96 | } 97 | 98 | //添加到notify list 99 | - (void)removeNotifyCallback:(CBCharacteristic *)c { 100 | [notifyList removeObjectForKey:c.UUID.description]; 101 | } 102 | 103 | //获取notify list 104 | - (NSMutableDictionary *)notifyCallBackList { 105 | return notifyList; 106 | } 107 | 108 | //获取notityBlock 109 | - (void(^)(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error))notifyCallback:(CBCharacteristic *)c { 110 | return [notifyList objectForKey:c.UUID.description]; 111 | } 112 | @end 113 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/utils/AdRecordUtil.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.utils; 2 | 3 | import android.util.SparseArray; 4 | 5 | import com.vise.baseble.model.adrecord.AdRecord; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @Description: 广播包解析工具类 16 | * @author: DAWI 17 | * @date: 16/8/7 21:56. 18 | */ 19 | public class AdRecordUtil { 20 | private AdRecordUtil() { 21 | // TO AVOID INSTANTIATION 22 | } 23 | 24 | public static String getRecordDataAsString(final AdRecord nameRecord) { 25 | if (nameRecord == null) { 26 | return ""; 27 | } 28 | return new String(nameRecord.getData()); 29 | } 30 | 31 | public static byte[] getServiceData(final AdRecord serviceData) { 32 | if (serviceData == null) { 33 | return null; 34 | } 35 | if (serviceData.getType() != AdRecord.BLE_GAP_AD_TYPE_SERVICE_DATA) return null; 36 | 37 | final byte[] raw = serviceData.getData(); 38 | //Chop out the uuid 39 | return Arrays.copyOfRange(raw, 2, raw.length); 40 | } 41 | 42 | public static int getServiceDataUuid(final AdRecord serviceData) { 43 | if (serviceData == null) { 44 | return -1; 45 | } 46 | if (serviceData.getType() != AdRecord.BLE_GAP_AD_TYPE_SERVICE_DATA) return -1; 47 | 48 | final byte[] raw = serviceData.getData(); 49 | //Find UUID data in byte array 50 | int uuid = (raw[1] & 0xFF) << 8; 51 | uuid += (raw[0] & 0xFF); 52 | 53 | return uuid; 54 | } 55 | 56 | /* 57 | * Read out all the AD structures from the raw scan record 58 | */ 59 | public static List parseScanRecordAsList(final byte[] scanRecord) { 60 | final List records = new ArrayList<>(); 61 | 62 | int index = 0; 63 | while (index < scanRecord.length) { 64 | final int length = scanRecord[index++]; 65 | //Done once we run out of records 66 | if (length == 0) break; 67 | 68 | final int type = scanRecord[index] & 0xFF; 69 | 70 | //Done if our record isn't a valid type 71 | if (type == 0) break; 72 | 73 | final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); 74 | 75 | records.add(new AdRecord(length, type, data)); 76 | 77 | //Advance 78 | index += length; 79 | } 80 | 81 | return Collections.unmodifiableList(records); 82 | } 83 | 84 | public static Map parseScanRecordAsMap(final byte[] scanRecord) { 85 | final Map records = new HashMap<>(); 86 | 87 | int index = 0; 88 | while (index < scanRecord.length) { 89 | final int length = scanRecord[index++]; 90 | //Done once we run out of records 91 | if (length == 0) break; 92 | 93 | final int type = scanRecord[index] & 0xFF; 94 | 95 | //Done if our record isn't a valid type 96 | if (type == 0) break; 97 | 98 | final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); 99 | 100 | records.put(type, new AdRecord(length, type, data)); 101 | 102 | //Advance 103 | index += length; 104 | } 105 | 106 | return Collections.unmodifiableMap(records); 107 | } 108 | 109 | public static SparseArray parseScanRecordAsSparseArray(final byte[] scanRecord) { 110 | final SparseArray records = new SparseArray<>(); 111 | 112 | int index = 0; 113 | while (index < scanRecord.length) { 114 | final int length = scanRecord[index++]; 115 | //Done once we run out of records 116 | if (length == 0) break; 117 | 118 | final int type = scanRecord[index] & 0xFF; 119 | 120 | //Done if our record isn't a valid type 121 | if (type == 0) break; 122 | 123 | final byte[] data = Arrays.copyOfRange(scanRecord, index + 1, index + length); 124 | 125 | records.put(type, new AdRecord(length, type, data)); 126 | 127 | //Advance 128 | index += length; 129 | } 130 | 131 | return records; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/core/BluetoothGattChannel.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.core; 2 | 3 | import android.bluetooth.BluetoothGatt; 4 | import android.bluetooth.BluetoothGattCharacteristic; 5 | import android.bluetooth.BluetoothGattDescriptor; 6 | import android.bluetooth.BluetoothGattService; 7 | 8 | import com.vise.baseble.common.PropertyType; 9 | 10 | import java.util.UUID; 11 | 12 | /** 13 | * @Description: BluetoothGatt 相关信息 14 | * @author: DAWI 15 | * @date: 2017/10/17 16:25 16 | */ 17 | public class BluetoothGattChannel { 18 | 19 | private BluetoothGatt bluetoothGatt; 20 | private BluetoothGattService service; 21 | private BluetoothGattCharacteristic characteristic; 22 | private BluetoothGattDescriptor descriptor; 23 | private String gattInfoKey; 24 | private PropertyType propertyType; 25 | private UUID serviceUUID; 26 | private UUID characteristicUUID; 27 | private UUID descriptorUUID; 28 | 29 | private BluetoothGattChannel(BluetoothGatt bluetoothGatt, PropertyType propertyType, UUID serviceUUID, UUID characteristicUUID, UUID descriptorUUID) { 30 | this.bluetoothGatt = bluetoothGatt; 31 | this.propertyType = propertyType; 32 | this.serviceUUID = serviceUUID; 33 | this.characteristicUUID = characteristicUUID; 34 | this.descriptorUUID = descriptorUUID; 35 | StringBuilder stringBuilder = new StringBuilder(); 36 | if (propertyType != null) { 37 | stringBuilder.append(propertyType.getPropertyValue()); 38 | } 39 | if (serviceUUID != null && bluetoothGatt != null) { 40 | service = bluetoothGatt.getService(serviceUUID); 41 | stringBuilder.append(serviceUUID.toString()); 42 | } 43 | if (service != null && characteristicUUID != null) { 44 | characteristic = service.getCharacteristic(characteristicUUID); 45 | stringBuilder.append(characteristicUUID.toString()); 46 | } 47 | if (characteristic != null && descriptorUUID != null) { 48 | descriptor = characteristic.getDescriptor(descriptorUUID); 49 | stringBuilder.append(descriptorUUID.toString()); 50 | } 51 | gattInfoKey = stringBuilder.toString(); 52 | } 53 | 54 | public BluetoothGatt getBluetoothGatt() { 55 | return bluetoothGatt; 56 | } 57 | 58 | public BluetoothGattCharacteristic getCharacteristic() { 59 | return characteristic; 60 | } 61 | 62 | public BluetoothGattDescriptor getDescriptor() { 63 | return descriptor; 64 | } 65 | 66 | public BluetoothGattService getService() { 67 | return service; 68 | } 69 | 70 | public BluetoothGattChannel setDescriptor(BluetoothGattDescriptor descriptor) { 71 | this.descriptor = descriptor; 72 | return this; 73 | } 74 | 75 | public String getGattInfoKey() { 76 | return gattInfoKey; 77 | } 78 | 79 | public UUID getCharacteristicUUID() { 80 | return characteristicUUID; 81 | } 82 | 83 | public UUID getDescriptorUUID() { 84 | return descriptorUUID; 85 | } 86 | 87 | public PropertyType getPropertyType() { 88 | return propertyType; 89 | } 90 | 91 | public UUID getServiceUUID() { 92 | return serviceUUID; 93 | } 94 | 95 | public static class Builder { 96 | private BluetoothGatt bluetoothGatt; 97 | private PropertyType propertyType; 98 | private UUID serviceUUID; 99 | private UUID characteristicUUID; 100 | private UUID descriptorUUID; 101 | 102 | public Builder() { 103 | } 104 | 105 | public Builder setBluetoothGatt(BluetoothGatt bluetoothGatt) { 106 | this.bluetoothGatt = bluetoothGatt; 107 | return this; 108 | } 109 | 110 | public Builder setCharacteristicUUID(UUID characteristicUUID) { 111 | this.characteristicUUID = characteristicUUID; 112 | return this; 113 | } 114 | 115 | public Builder setDescriptorUUID(UUID descriptorUUID) { 116 | this.descriptorUUID = descriptorUUID; 117 | return this; 118 | } 119 | 120 | public Builder setPropertyType(PropertyType propertyType) { 121 | this.propertyType = propertyType; 122 | return this; 123 | } 124 | 125 | public Builder setServiceUUID(UUID serviceUUID) { 126 | this.serviceUUID = serviceUUID; 127 | return this; 128 | } 129 | 130 | public BluetoothGattChannel builder() { 131 | return new BluetoothGattChannel(bluetoothGatt, propertyType, serviceUUID, characteristicUUID, descriptorUUID); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/model/adrecord/AdRecordStore.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.model.adrecord; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | import android.util.SparseArray; 7 | 8 | import com.vise.baseble.utils.AdRecordUtil; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.Collections; 13 | 14 | /** 15 | * @Description: 广播包解析仓库 16 | * @author: DAWI 17 | * @date: 16/8/7 21:54. 18 | */ 19 | public class AdRecordStore implements Parcelable { 20 | 21 | public static final Creator CREATOR = new Creator() { 22 | public AdRecordStore createFromParcel(final Parcel in) { 23 | return new AdRecordStore(in); 24 | } 25 | 26 | public AdRecordStore[] newArray(final int size) { 27 | return new AdRecordStore[size]; 28 | } 29 | }; 30 | private static final String RECORDS_ARRAY = "records_array"; 31 | private static final String LOCAL_NAME_COMPLETE = "local_name_complete"; 32 | private static final String LOCAL_NAME_SHORT = "local_name_short"; 33 | private final SparseArray mAdRecords; 34 | private final String mLocalNameComplete; 35 | private final String mLocalNameShort; 36 | 37 | public AdRecordStore(final Parcel in) { 38 | final Bundle b = in.readBundle(getClass().getClassLoader()); 39 | mAdRecords = b.getSparseParcelableArray(RECORDS_ARRAY); 40 | mLocalNameComplete = b.getString(LOCAL_NAME_COMPLETE); 41 | mLocalNameShort = b.getString(LOCAL_NAME_SHORT); 42 | } 43 | 44 | /** 45 | * Instantiates a new Bluetooth LE device Ad Record Store. 46 | * 47 | * @param adRecords the ad records 48 | */ 49 | public AdRecordStore(final SparseArray adRecords) { 50 | mAdRecords = adRecords; 51 | mLocalNameComplete = AdRecordUtil.getRecordDataAsString(mAdRecords.get(AdRecord.BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME)); 52 | mLocalNameShort = AdRecordUtil.getRecordDataAsString(mAdRecords.get(AdRecord.BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME)); 53 | 54 | } 55 | 56 | /* (non-Javadoc) 57 | * @see android.os.Parcelable#describeContents() 58 | */ 59 | @Override 60 | public int describeContents() { 61 | return 0; 62 | } 63 | 64 | /** 65 | * Gets the short local device name. 66 | * 67 | * @return the local name complete 68 | */ 69 | public String getLocalNameComplete() { 70 | return mLocalNameComplete; 71 | } 72 | 73 | /** 74 | * Gets the complete local device name. 75 | * 76 | * @return the local name short 77 | */ 78 | public String getLocalNameShort() { 79 | return mLocalNameShort; 80 | } 81 | 82 | /** 83 | * retrieves an individual record. 84 | * 85 | * @param record the record 86 | * @return the record 87 | */ 88 | public AdRecord getRecord(final int record) { 89 | return mAdRecords.get(record); 90 | } 91 | 92 | /** 93 | * Gets the record data as string. 94 | * 95 | * @param record the record 96 | * @return the record data as string 97 | */ 98 | public String getRecordDataAsString(final int record) { 99 | return AdRecordUtil.getRecordDataAsString(mAdRecords.get(record)); 100 | } 101 | 102 | /** 103 | * Gets the record as collection. 104 | * 105 | * @return the records as collection 106 | */ 107 | public Collection getRecordsAsCollection() { 108 | return Collections.unmodifiableCollection(asList(mAdRecords)); 109 | } 110 | 111 | /** 112 | * Checks if is record present. 113 | * 114 | * @param record the record 115 | * @return true, if is record present 116 | */ 117 | public boolean isRecordPresent(final int record) { 118 | return mAdRecords.indexOfKey(record) >= 0; 119 | } 120 | 121 | /* (non-Javadoc) 122 | * @see java.lang.Object#toString() 123 | */ 124 | @Override 125 | public String toString() { 126 | return "AdRecordStore [mLocalNameComplete=" + mLocalNameComplete + ", mLocalNameShort=" + mLocalNameShort + "]"; 127 | } 128 | 129 | /* (non-Javadoc) 130 | * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) 131 | */ 132 | @Override 133 | public void writeToParcel(final Parcel parcel, final int arg1) { 134 | final Bundle b = new Bundle(); 135 | b.putString(LOCAL_NAME_COMPLETE, mLocalNameComplete); 136 | b.putString(LOCAL_NAME_SHORT, mLocalNameShort); 137 | b.putSparseParcelableArray(RECORDS_ARRAY, mAdRecords); 138 | parcel.writeBundle(b); 139 | } 140 | 141 | /** 142 | * As list. 143 | * 144 | * @param the generic type 145 | * @param sparseArray the sparse array 146 | * @return the collection 147 | */ 148 | public static Collection asList(final SparseArray sparseArray) { 149 | if (sparseArray == null) return null; 150 | final Collection arrayList = new ArrayList<>(sparseArray.size()); 151 | for (int i = 0; i < sparseArray.size(); i++) { 152 | arrayList.add(sparseArray.valueAt(i)); 153 | } 154 | return arrayList; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/callback/scan/ScanCallback.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.callback.scan; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import com.vise.baseble.ViseBle; 8 | import com.vise.baseble.common.BleConfig; 9 | import com.vise.baseble.model.BluetoothLeDevice; 10 | import com.vise.baseble.model.BluetoothLeDeviceStore; 11 | 12 | /** 13 | * @Description: 扫描设备回调 14 | * @author: DAWI 15 | * @date: 17/8/1 22:58. 16 | */ 17 | public class ScanCallback implements BluetoothAdapter.LeScanCallback, IScanFilter { 18 | protected Handler handler = new Handler(Looper.getMainLooper()); 19 | protected boolean isScan = true;//是否开始扫描 20 | protected boolean isScanning = false;//是否正在扫描 21 | protected BluetoothLeDeviceStore bluetoothLeDeviceStore;//用来存储扫描到的设备 22 | protected IScanCallback scanCallback;//扫描结果回调 23 | 24 | public ScanCallback(IScanCallback scanCallback) { 25 | this.scanCallback = scanCallback; 26 | if (scanCallback == null) { 27 | throw new NullPointerException("this scanCallback is null!"); 28 | } 29 | bluetoothLeDeviceStore = new BluetoothLeDeviceStore(); 30 | } 31 | 32 | public ScanCallback setScan(boolean scan) { 33 | isScan = scan; 34 | return this; 35 | } 36 | 37 | public boolean isScanning() { 38 | return isScanning; 39 | } 40 | 41 | public void scan() { 42 | if (isScan) { 43 | if (isScanning) { 44 | return; 45 | } 46 | bluetoothLeDeviceStore.clear(); 47 | if (BleConfig.getInstance().getScanTimeout() > 0) { 48 | handler.postDelayed(new Runnable() { 49 | @Override 50 | public void run() { 51 | isScanning = false; 52 | 53 | if (ViseBle.getInstance().getBluetoothAdapter() != null) { 54 | ViseBle.getInstance().getBluetoothAdapter().stopLeScan(ScanCallback.this); 55 | } 56 | 57 | if (bluetoothLeDeviceStore.getDeviceMap() != null 58 | && bluetoothLeDeviceStore.getDeviceMap().size() > 0) { 59 | scanCallback.onScanFinish(bluetoothLeDeviceStore); 60 | } else { 61 | scanCallback.onScanTimeout(); 62 | } 63 | } 64 | }, BleConfig.getInstance().getScanTimeout()); 65 | }else if (BleConfig.getInstance().getScanRepeatInterval() > 0){ 66 | //如果超时时间设置为一直扫描(即 <= 0),则判断是否设置重复扫描间隔 67 | handler.postDelayed(new Runnable() { 68 | @Override 69 | public void run() { 70 | isScanning = false; 71 | 72 | if (ViseBle.getInstance().getBluetoothAdapter() != null) { 73 | ViseBle.getInstance().getBluetoothAdapter().stopLeScan(ScanCallback.this); 74 | } 75 | 76 | if (bluetoothLeDeviceStore.getDeviceMap() != null 77 | && bluetoothLeDeviceStore.getDeviceMap().size() > 0) { 78 | scanCallback.onScanFinish(bluetoothLeDeviceStore); 79 | } else { 80 | scanCallback.onScanTimeout(); 81 | } 82 | isScanning = true; 83 | if (ViseBle.getInstance().getBluetoothAdapter() != null) { 84 | ViseBle.getInstance().getBluetoothAdapter().startLeScan(ScanCallback.this); 85 | } 86 | handler.postDelayed(this,BleConfig.getInstance().getScanRepeatInterval()); 87 | } 88 | }, BleConfig.getInstance().getScanRepeatInterval()); 89 | } 90 | isScanning = true; 91 | if (ViseBle.getInstance().getBluetoothAdapter() != null) { 92 | ViseBle.getInstance().getBluetoothAdapter().startLeScan(ScanCallback.this); 93 | } 94 | } else { 95 | isScanning = false; 96 | if (ViseBle.getInstance().getBluetoothAdapter() != null) { 97 | ViseBle.getInstance().getBluetoothAdapter().stopLeScan(ScanCallback.this); 98 | } 99 | } 100 | } 101 | 102 | public ScanCallback removeHandlerMsg() { 103 | handler.removeCallbacksAndMessages(null); 104 | bluetoothLeDeviceStore.clear(); 105 | return this; 106 | } 107 | 108 | @Override 109 | public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) { 110 | BluetoothLeDevice bluetoothLeDevice = new BluetoothLeDevice(bluetoothDevice, rssi, scanRecord, System.currentTimeMillis()); 111 | BluetoothLeDevice filterDevice = onFilter(bluetoothLeDevice); 112 | if (filterDevice != null) { 113 | bluetoothLeDeviceStore.addDevice(filterDevice); 114 | scanCallback.onDeviceFound(filterDevice); 115 | } 116 | } 117 | 118 | @Override 119 | public BluetoothLeDevice onFilter(BluetoothLeDevice bluetoothLeDevice) { 120 | if(bluetoothLeDevice.getName()!=null&&!bluetoothLeDevice.getName().isEmpty()){ 121 | return bluetoothLeDevice; 122 | } 123 | return null; 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/common/BleConfig.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.common; 2 | 3 | import static com.vise.baseble.common.BleConstant.DEFAULT_CONN_TIME; 4 | import static com.vise.baseble.common.BleConstant.DEFAULT_MAX_CONNECT_COUNT; 5 | import static com.vise.baseble.common.BleConstant.DEFAULT_OPERATE_TIME; 6 | import static com.vise.baseble.common.BleConstant.DEFAULT_RETRY_COUNT; 7 | import static com.vise.baseble.common.BleConstant.DEFAULT_RETRY_INTERVAL; 8 | import static com.vise.baseble.common.BleConstant.DEFAULT_SCAN_REPEAT_INTERVAL; 9 | import static com.vise.baseble.common.BleConstant.DEFAULT_SCAN_TIME; 10 | 11 | /** 12 | * @Description: 蓝牙通信相关配置 13 | * @author: DAWI 14 | * @date: 2017/10/16 11:46 15 | */ 16 | public class BleConfig { 17 | private static BleConfig instance; 18 | 19 | private int scanTimeout = DEFAULT_SCAN_TIME;//扫描超时时间(毫秒) 20 | private int connectTimeout = DEFAULT_CONN_TIME;//连接超时时间(毫秒) 21 | private int operateTimeout = DEFAULT_OPERATE_TIME;//数据操作超时时间(毫秒) 22 | private int connectRetryCount = DEFAULT_RETRY_COUNT;//连接重试次数 23 | private int connectRetryInterval = DEFAULT_RETRY_INTERVAL;//连接重试间隔(毫秒) 24 | private int operateRetryCount = DEFAULT_RETRY_COUNT;//数据操作重试次数 25 | private int operateRetryInterval = DEFAULT_RETRY_INTERVAL;//数据操作重试间隔时间(毫秒) 26 | private int maxConnectCount = DEFAULT_MAX_CONNECT_COUNT;//最大连接数量 27 | 28 | //yankee 29 | private int scanRepeatInterval = DEFAULT_SCAN_REPEAT_INTERVAL;//每隔X时间重复扫描 (毫秒) 30 | 31 | private BleConfig() { 32 | } 33 | 34 | public static BleConfig getInstance() { 35 | if (instance == null) { 36 | synchronized (BleConfig.class) { 37 | if (instance == null) { 38 | instance = new BleConfig(); 39 | } 40 | } 41 | } 42 | return instance; 43 | } 44 | 45 | /** 46 | * 获取发送数据超时时间 47 | * 48 | * @return 返回发送数据超时时间 49 | */ 50 | public int getOperateTimeout() { 51 | return operateTimeout; 52 | } 53 | 54 | /** 55 | * 设置发送数据超时时间 56 | * 57 | * @param operateTimeout 发送数据超时时间 58 | * @return 返回ViseBle 59 | */ 60 | public BleConfig setOperateTimeout(int operateTimeout) { 61 | this.operateTimeout = operateTimeout; 62 | return this; 63 | } 64 | 65 | /** 66 | * 获取连接超时时间 67 | * 68 | * @return 返回连接超时时间 69 | */ 70 | public int getConnectTimeout() { 71 | return connectTimeout; 72 | } 73 | 74 | /** 75 | * 设置连接超时时间 76 | * 77 | * @param connectTimeout 连接超时时间 78 | * @return 返回ViseBle 79 | */ 80 | public BleConfig setConnectTimeout(int connectTimeout) { 81 | this.connectTimeout = connectTimeout; 82 | return this; 83 | } 84 | 85 | /** 86 | * 获取扫描超时时间 87 | * 88 | * @return 返回扫描超时时间 89 | */ 90 | public int getScanTimeout() { 91 | return scanTimeout; 92 | } 93 | 94 | /** 95 | * 设置扫描超时时间 96 | * 97 | * @param scanTimeout 扫描超时时间 98 | * @return 返回ViseBle 99 | */ 100 | public BleConfig setScanTimeout(int scanTimeout) { 101 | this.scanTimeout = scanTimeout; 102 | return this; 103 | } 104 | 105 | /** 106 | * 获取连接重试次数 107 | * 108 | * @return 109 | */ 110 | public int getConnectRetryCount() { 111 | return connectRetryCount; 112 | } 113 | 114 | /** 115 | * 设置连接重试次数 116 | * 117 | * @param connectRetryCount 118 | * @return 119 | */ 120 | public BleConfig setConnectRetryCount(int connectRetryCount) { 121 | this.connectRetryCount = connectRetryCount; 122 | return this; 123 | } 124 | 125 | /** 126 | * 获取连接重试间隔时间 127 | * 128 | * @return 129 | */ 130 | public int getConnectRetryInterval() { 131 | return connectRetryInterval; 132 | } 133 | 134 | /** 135 | * 设置连接重试间隔时间 136 | * 137 | * @param connectRetryInterval 138 | * @return 139 | */ 140 | public BleConfig setConnectRetryInterval(int connectRetryInterval) { 141 | this.connectRetryInterval = connectRetryInterval; 142 | return this; 143 | } 144 | 145 | /** 146 | * 获取最大连接数量 147 | * 148 | * @return 149 | */ 150 | public int getMaxConnectCount() { 151 | return maxConnectCount; 152 | } 153 | 154 | /** 155 | * 设置最大连接数量 156 | * 157 | * @param maxConnectCount 158 | * @return 159 | */ 160 | public BleConfig setMaxConnectCount(int maxConnectCount) { 161 | this.maxConnectCount = maxConnectCount; 162 | return this; 163 | } 164 | 165 | /** 166 | * 获取操作数据重试次数 167 | * 168 | * @return 169 | */ 170 | public int getOperateRetryCount() { 171 | return operateRetryCount; 172 | } 173 | 174 | /** 175 | * 设置操作数据重试次数 176 | * 177 | * @param operateRetryCount 178 | * @return 179 | */ 180 | public BleConfig setOperateRetryCount(int operateRetryCount) { 181 | this.operateRetryCount = operateRetryCount; 182 | return this; 183 | } 184 | 185 | /** 186 | * 获取操作数据重试间隔时间 187 | * 188 | * @return 189 | */ 190 | public int getOperateRetryInterval() { 191 | return operateRetryInterval; 192 | } 193 | 194 | /** 195 | * 设置操作数据重试间隔时间 196 | * 197 | * @param operateRetryInterval 198 | * @return 199 | */ 200 | public BleConfig setOperateRetryInterval(int operateRetryInterval) { 201 | this.operateRetryInterval = operateRetryInterval; 202 | return this; 203 | } 204 | 205 | /** 206 | * 获取扫描间隔时间 207 | * @return 208 | */ 209 | public int getScanRepeatInterval() { 210 | return scanRepeatInterval; 211 | } 212 | 213 | /** 214 | * 设置每隔多少时间重复扫描一次 215 | * 设置扫描间隔时间 (毫秒) 216 | * @param scanRepeatInterval 217 | * @return 218 | */ 219 | public BleConfig setScanRepeatInterval(int scanRepeatInterval) { 220 | this.scanRepeatInterval = scanRepeatInterval; 221 | return this; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/core/DeviceMirrorPool.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.core; 2 | 3 | import com.vise.baseble.common.BleConfig; 4 | import com.vise.baseble.common.ConnectState; 5 | import com.vise.baseble.model.BluetoothLeDevice; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.Comparator; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * @Description: 设备镜像池,用来管理多个设备连接后的操作 15 | * @author: DAWI 16 | * @date: 17/8/1 23:18. 17 | */ 18 | public class DeviceMirrorPool { 19 | private final LruHashMap DEVICE_MIRROR_MAP; 20 | 21 | public DeviceMirrorPool() { 22 | DEVICE_MIRROR_MAP = new LruHashMap<>(BleConfig.getInstance().getMaxConnectCount()); 23 | } 24 | 25 | public DeviceMirrorPool(int deviceMirrorSize) { 26 | DEVICE_MIRROR_MAP = new LruHashMap<>(deviceMirrorSize); 27 | } 28 | 29 | /** 30 | * 添加设备镜像 31 | * 32 | * @param bluetoothLeDevice 33 | */ 34 | public synchronized void addDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { 35 | if (bluetoothLeDevice == null) { 36 | return; 37 | } 38 | String key = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); 39 | if (!DEVICE_MIRROR_MAP.containsKey(key)) { 40 | DEVICE_MIRROR_MAP.put(key, new DeviceMirror(bluetoothLeDevice)); 41 | } 42 | } 43 | 44 | /** 45 | * 添加设备镜像 46 | * 47 | * @param deviceMirror 48 | */ 49 | public synchronized void addDeviceMirror(DeviceMirror deviceMirror) { 50 | if (deviceMirror == null) { 51 | return; 52 | } 53 | if (!DEVICE_MIRROR_MAP.containsKey(deviceMirror.getUniqueSymbol())) { 54 | DEVICE_MIRROR_MAP.put(deviceMirror.getUniqueSymbol(), deviceMirror); 55 | } 56 | } 57 | 58 | /** 59 | * 删除设备镜像 60 | * 61 | * @param bluetoothLeDevice 62 | */ 63 | public synchronized void removeDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { 64 | if (bluetoothLeDevice == null) { 65 | return; 66 | } 67 | String key = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); 68 | if (DEVICE_MIRROR_MAP.containsKey(key)) { 69 | DEVICE_MIRROR_MAP.remove(key); 70 | } 71 | } 72 | 73 | /** 74 | * 删除设备镜像 75 | * 76 | * @param deviceMirror 77 | */ 78 | public synchronized void removeDeviceMirror(DeviceMirror deviceMirror) { 79 | if (deviceMirror == null) { 80 | return; 81 | } 82 | if (DEVICE_MIRROR_MAP.containsKey(deviceMirror.getUniqueSymbol())) { 83 | deviceMirror.clear(); 84 | DEVICE_MIRROR_MAP.remove(deviceMirror.getUniqueSymbol()); 85 | } 86 | } 87 | 88 | /** 89 | * 判断是否包含设备镜像 90 | * 91 | * @param deviceMirror 92 | * @return 93 | */ 94 | public synchronized boolean isContainDevice(DeviceMirror deviceMirror) { 95 | if (deviceMirror == null || !DEVICE_MIRROR_MAP.containsKey(deviceMirror.getUniqueSymbol())) { 96 | return false; 97 | } 98 | return true; 99 | } 100 | 101 | /** 102 | * 判断是否包含设备镜像 103 | * 104 | * @param bluetoothLeDevice 105 | * @return 106 | */ 107 | public synchronized boolean isContainDevice(BluetoothLeDevice bluetoothLeDevice) { 108 | if (bluetoothLeDevice == null || !DEVICE_MIRROR_MAP.containsKey(bluetoothLeDevice.getAddress() + 109 | bluetoothLeDevice.getName())) { 110 | return false; 111 | } 112 | return true; 113 | } 114 | 115 | /** 116 | * 获取连接池中该设备镜像的连接状态,如果没有连接则返回CONNECT_DISCONNECT。 117 | * 118 | * @param bluetoothLeDevice 119 | * @return 120 | */ 121 | public synchronized ConnectState getConnectState(BluetoothLeDevice bluetoothLeDevice) { 122 | DeviceMirror deviceMirror = getDeviceMirror(bluetoothLeDevice); 123 | if (deviceMirror != null) { 124 | return deviceMirror.getConnectState(); 125 | } 126 | return ConnectState.CONNECT_DISCONNECT; 127 | } 128 | 129 | /** 130 | * 获取连接池中的设备镜像,如果没有连接则返回空 131 | * 132 | * @param bluetoothLeDevice 133 | * @return 134 | */ 135 | public synchronized DeviceMirror getDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { 136 | if (bluetoothLeDevice != null) { 137 | String key = bluetoothLeDevice.getAddress() + bluetoothLeDevice.getName(); 138 | if (DEVICE_MIRROR_MAP.containsKey(key)) { 139 | return DEVICE_MIRROR_MAP.get(key); 140 | } 141 | } 142 | return null; 143 | } 144 | 145 | /** 146 | * 断开连接池中某一个设备 147 | * 148 | * @param bluetoothLeDevice 149 | */ 150 | public synchronized void disconnect(BluetoothLeDevice bluetoothLeDevice) { 151 | if (isContainDevice(bluetoothLeDevice)) { 152 | getDeviceMirror(bluetoothLeDevice).disconnect(); 153 | } 154 | } 155 | 156 | /** 157 | * 断开连接池中所有设备 158 | */ 159 | public synchronized void disconnect() { 160 | for (Map.Entry stringDeviceMirrorEntry : DEVICE_MIRROR_MAP.entrySet()) { 161 | stringDeviceMirrorEntry.getValue().disconnect(); 162 | } 163 | DEVICE_MIRROR_MAP.clear(); 164 | } 165 | 166 | /** 167 | * 清除连接池 168 | */ 169 | public synchronized void clear() { 170 | for (Map.Entry stringDeviceMirrorEntry : DEVICE_MIRROR_MAP.entrySet()) { 171 | stringDeviceMirrorEntry.getValue().clear(); 172 | } 173 | DEVICE_MIRROR_MAP.clear(); 174 | } 175 | 176 | /** 177 | * 获取连接池设备镜像Map集合 178 | * 179 | * @return 180 | */ 181 | public Map getDeviceMirrorMap() { 182 | return DEVICE_MIRROR_MAP; 183 | } 184 | 185 | /** 186 | * 获取连接池设备镜像List集合 187 | * 188 | * @return 189 | */ 190 | public synchronized List getDeviceMirrorList() { 191 | final List deviceMirrors = new ArrayList<>(DEVICE_MIRROR_MAP.values()); 192 | Collections.sort(deviceMirrors, new Comparator() { 193 | @Override 194 | public int compare(final DeviceMirror lhs, final DeviceMirror rhs) { 195 | return lhs.getUniqueSymbol().compareToIgnoreCase(rhs.getUniqueSymbol()); 196 | } 197 | }); 198 | return deviceMirrors; 199 | } 200 | 201 | /** 202 | * 获取连接池设备详细信息List集合 203 | * 204 | * @return 205 | */ 206 | public synchronized List getDeviceList() { 207 | final List deviceList = new ArrayList<>(); 208 | for (DeviceMirror deviceMirror : getDeviceMirrorList()) { 209 | if (deviceMirror != null) { 210 | deviceList.add(deviceMirror.getBluetoothLeDevice()); 211 | } 212 | } 213 | return deviceList; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyCallback.h: -------------------------------------------------------------------------------- 1 | /* 2 | BabyBluetooth 3 | 简单易用的蓝牙ble库,基于CoreBluetooth 作者:刘彦玮 4 | https://github.com/coolnameismy/BabyBluetooth 5 | 6 | @brief babybluetooth 的block定义和储存 7 | 8 | */ 9 | 10 | // Created by 刘彦玮 on 15/9/2. 11 | // Copyright (c) 2015年 刘彦玮. All rights reserved. 12 | // 13 | 14 | #import 15 | #import "BabyOptions.h" 16 | 17 | 18 | //设备状态改变的委托 19 | typedef void (^BBCentralManagerDidUpdateStateBlock)(CBCentralManager *central); 20 | //找到设备的委托 21 | typedef void (^BBDiscoverPeripheralsBlock)(CBCentralManager *central,CBPeripheral *peripheral,NSDictionary *advertisementData, NSNumber *RSSI); 22 | //连接设备成功的block 23 | typedef void (^BBConnectedPeripheralBlock)(CBCentralManager *central,CBPeripheral *peripheral); 24 | //连接设备失败的block 25 | typedef void (^BBFailToConnectBlock)(CBCentralManager *central,CBPeripheral *peripheral,NSError *error); 26 | //断开设备连接的bock 27 | typedef void (^BBDisconnectBlock)(CBCentralManager *central,CBPeripheral *peripheral,NSError *error); 28 | //找到服务的block 29 | typedef void (^BBDiscoverServicesBlock)(CBPeripheral *peripheral,NSError *error); 30 | //找到Characteristics的block 31 | typedef void (^BBDiscoverCharacteristicsBlock)(CBPeripheral *peripheral,CBService *service,NSError *error); 32 | //更新(获取)Characteristics的value的block 33 | typedef void (^BBReadValueForCharacteristicBlock)(CBPeripheral *peripheral,CBCharacteristic *characteristic,NSError *error); 34 | //获取Characteristics的名称 35 | typedef void (^BBDiscoverDescriptorsForCharacteristicBlock)(CBPeripheral *peripheral,CBCharacteristic *service,NSError *error); 36 | //获取Descriptors的值 37 | typedef void (^BBReadValueForDescriptorsBlock)(CBPeripheral *peripheral,CBDescriptor *descriptor,NSError *error); 38 | 39 | //babyBluettooth cancelScanBlock方法调用后的回调 40 | typedef void (^BBCancelScanBlock)(CBCentralManager *centralManager); 41 | //babyBluettooth cancelAllPeripheralsConnection 方法调用后的回调 42 | typedef void (^BBCancelAllPeripheralsConnectionBlock)(CBCentralManager *centralManager); 43 | 44 | 45 | typedef void (^BBDidWriteValueForCharacteristicBlock)(CBCharacteristic *characteristic,NSError *error); 46 | 47 | typedef void (^BBDidWriteValueForDescriptorBlock)(CBDescriptor *descriptor,NSError *error); 48 | 49 | typedef void (^BBDidUpdateNotificationStateForCharacteristicBlock)(CBCharacteristic *characteristic,NSError *error); 50 | 51 | typedef void (^BBDidReadRSSIBlock)(CBPeripheral *peripheral,NSNumber *RSSI,NSError *error); 52 | 53 | typedef void (^BBDidDiscoverIncludedServicesForServiceBlock)(CBService *service,NSError *error); 54 | 55 | typedef void (^BBDidUpdateNameBlock)(CBPeripheral *peripheral); 56 | 57 | typedef void (^BBDidModifyServicesBlock)(CBPeripheral *peripheral,NSArray *invalidatedServices); 58 | 59 | 60 | //peripheral model 61 | typedef void (^BBPeripheralModelDidUpdateState)(CBPeripheralManager *peripheral); 62 | typedef void (^BBPeripheralModelDidAddService)(CBPeripheralManager *peripheral,CBService *service,NSError *error); 63 | typedef void (^BBPeripheralModelDidStartAdvertising)(CBPeripheralManager *peripheral,NSError *error); 64 | typedef void (^BBPeripheralModelDidReceiveReadRequest)(CBPeripheralManager *peripheral,CBATTRequest *request); 65 | typedef void (^BBPeripheralModelDidReceiveWriteRequests)(CBPeripheralManager *peripheral,NSArray *requests); 66 | typedef void (^BBPeripheralModelDidSubscribeToCharacteristic)(CBPeripheralManager *peripheral,CBCentral *central,CBCharacteristic *characteristic); 67 | typedef void (^BBPeripheralModelDidUnSubscribeToCharacteristic)(CBPeripheralManager *peripheral,CBCentral *central,CBCharacteristic *characteristic); 68 | 69 | 70 | 71 | @interface BabyCallback : NSObject 72 | 73 | #pragma mark - callback block 74 | //设备状态改变的委托 75 | @property (nonatomic, copy) BBCentralManagerDidUpdateStateBlock blockOnCentralManagerDidUpdateState; 76 | //发现peripherals 77 | @property (nonatomic, copy) BBDiscoverPeripheralsBlock blockOnDiscoverPeripherals; 78 | //连接callback 79 | @property (nonatomic, copy) BBConnectedPeripheralBlock blockOnConnectedPeripheral; 80 | //连接设备失败的block 81 | @property (nonatomic, copy) BBFailToConnectBlock blockOnFailToConnect; 82 | //断开设备连接的bock 83 | @property (nonatomic, copy) BBDisconnectBlock blockOnDisconnect; 84 | //发现services 85 | @property (nonatomic, copy) BBDiscoverServicesBlock blockOnDiscoverServices; 86 | //发现Characteristics 87 | @property (nonatomic, copy) BBDiscoverCharacteristicsBlock blockOnDiscoverCharacteristics; 88 | //发现更新Characteristics的 89 | @property (nonatomic, copy) BBReadValueForCharacteristicBlock blockOnReadValueForCharacteristic; 90 | //获取Characteristics的名称 91 | @property (nonatomic, copy) BBDiscoverDescriptorsForCharacteristicBlock blockOnDiscoverDescriptorsForCharacteristic; 92 | //获取Descriptors的值 93 | @property (nonatomic,copy) BBReadValueForDescriptorsBlock blockOnReadValueForDescriptors; 94 | 95 | @property (nonatomic, copy) BBDidWriteValueForCharacteristicBlock blockOnDidWriteValueForCharacteristic; 96 | 97 | @property (nonatomic, copy) BBDidWriteValueForDescriptorBlock blockOnDidWriteValueForDescriptor; 98 | 99 | @property (nonatomic, copy) BBDidUpdateNotificationStateForCharacteristicBlock blockOnDidUpdateNotificationStateForCharacteristic; 100 | 101 | @property (nonatomic, copy) BBDidReadRSSIBlock blockOnDidReadRSSI; 102 | 103 | @property (nonatomic, copy) BBDidDiscoverIncludedServicesForServiceBlock blockOnDidDiscoverIncludedServicesForService; 104 | 105 | @property (nonatomic, copy) BBDidUpdateNameBlock blockOnDidUpdateName; 106 | 107 | @property (nonatomic, copy) BBDidModifyServicesBlock blockOnDidModifyServices; 108 | 109 | 110 | //babyBluettooth stopScan方法调用后的回调 111 | @property(nonatomic,copy) BBCancelScanBlock blockOnCancelScan; 112 | //babyBluettooth stopConnectAllPerihperals 方法调用后的回调 113 | @property(nonatomic,copy) BBCancelAllPeripheralsConnectionBlock blockOnCancelAllPeripheralsConnection; 114 | //babyBluettooth 蓝牙使用的参数参数 115 | @property(nonatomic,strong) BabyOptions *babyOptions; 116 | 117 | 118 | #pragma mark - 过滤器Filter 119 | //发现peripherals规则 120 | @property (nonatomic, copy) BOOL (^filterOnDiscoverPeripherals)(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI); 121 | //连接peripherals规则 122 | @property (nonatomic, copy) BOOL (^filterOnconnectToPeripherals)(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI); 123 | 124 | 125 | #pragma mark - peripheral model 126 | 127 | //peripheral model 128 | 129 | @property (nonatomic, copy) BBPeripheralModelDidUpdateState blockOnPeripheralModelDidUpdateState; 130 | @property (nonatomic, copy) BBPeripheralModelDidAddService blockOnPeripheralModelDidAddService; 131 | @property (nonatomic, copy) BBPeripheralModelDidStartAdvertising blockOnPeripheralModelDidStartAdvertising; 132 | @property (nonatomic, copy) BBPeripheralModelDidReceiveReadRequest blockOnPeripheralModelDidReceiveReadRequest; 133 | @property (nonatomic, copy) BBPeripheralModelDidReceiveWriteRequests blockOnPeripheralModelDidReceiveWriteRequests; 134 | @property (nonatomic, copy) BBPeripheralModelDidSubscribeToCharacteristic blockOnPeripheralModelDidSubscribeToCharacteristic; 135 | @property (nonatomic, copy) BBPeripheralModelDidUnSubscribeToCharacteristic blockOnPeripheralModelDidUnSubscribeToCharacteristic; 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/model/resolver/BluetoothClassResolver.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.model.resolver; 2 | 3 | import android.bluetooth.BluetoothClass; 4 | 5 | /** 6 | * @Description: 蓝牙设备类别 7 | * @author: DAWI 8 | * @date: 16/8/7 21:48. 9 | */ 10 | public class BluetoothClassResolver { 11 | public static String resolveDeviceClass(final int btClass) { 12 | switch (btClass) { 13 | case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: 14 | return "A/V, Camcorder"; 15 | case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 16 | return "A/V, Car Audio"; 17 | case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 18 | return "A/V, Handsfree"; 19 | case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: 20 | return "A/V, Headphones"; 21 | case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: 22 | return "A/V, HiFi Audio"; 23 | case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: 24 | return "A/V, Loudspeaker"; 25 | case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE: 26 | return "A/V, Microphone"; 27 | case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: 28 | return "A/V, Portable Audio"; 29 | case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX: 30 | return "A/V, Set Top Box"; 31 | case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED: 32 | return "A/V, Uncategorized"; 33 | case BluetoothClass.Device.AUDIO_VIDEO_VCR: 34 | return "A/V, VCR"; 35 | case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: 36 | return "A/V, Video Camera"; 37 | case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: 38 | return "A/V, Video Conferencing"; 39 | case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: 40 | return "A/V, Video Display and Loudspeaker"; 41 | case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY: 42 | return "A/V, Video Gaming Toy"; 43 | case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR: 44 | return "A/V, Video Monitor"; 45 | case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 46 | return "A/V, Video Wearable Headset"; 47 | case BluetoothClass.Device.COMPUTER_DESKTOP: 48 | return "Computer, Desktop"; 49 | case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA: 50 | return "Computer, Handheld PC/PDA"; 51 | case BluetoothClass.Device.COMPUTER_LAPTOP: 52 | return "Computer, Laptop"; 53 | case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA: 54 | return "Computer, Palm Size PC/PDA"; 55 | case BluetoothClass.Device.COMPUTER_SERVER: 56 | return "Computer, Server"; 57 | case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: 58 | return "Computer, Uncategorized"; 59 | case BluetoothClass.Device.COMPUTER_WEARABLE: 60 | return "Computer, Wearable"; 61 | case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE: 62 | return "Health, Blood Pressure"; 63 | case BluetoothClass.Device.HEALTH_DATA_DISPLAY: 64 | return "Health, Data Display"; 65 | case BluetoothClass.Device.HEALTH_GLUCOSE: 66 | return "Health, Glucose"; 67 | case BluetoothClass.Device.HEALTH_PULSE_OXIMETER: 68 | return "Health, Pulse Oximeter"; 69 | case BluetoothClass.Device.HEALTH_PULSE_RATE: 70 | return "Health, Pulse Rate"; 71 | case BluetoothClass.Device.HEALTH_THERMOMETER: 72 | return "Health, Thermometer"; 73 | case BluetoothClass.Device.HEALTH_UNCATEGORIZED: 74 | return "Health, Uncategorized"; 75 | case BluetoothClass.Device.HEALTH_WEIGHING: 76 | return "Health, Weighting"; 77 | case BluetoothClass.Device.PHONE_CELLULAR: 78 | return "Phone, Cellular"; 79 | case BluetoothClass.Device.PHONE_CORDLESS: 80 | return "Phone, Cordless"; 81 | case BluetoothClass.Device.PHONE_ISDN: 82 | return "Phone, ISDN"; 83 | case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY: 84 | return "Phone, Modem or Gateway"; 85 | case BluetoothClass.Device.PHONE_SMART: 86 | return "Phone, Smart"; 87 | case BluetoothClass.Device.PHONE_UNCATEGORIZED: 88 | return "Phone, Uncategorized"; 89 | case BluetoothClass.Device.TOY_CONTROLLER: 90 | return "Toy, Controller"; 91 | case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE: 92 | return "Toy, Doll/Action Figure"; 93 | case BluetoothClass.Device.TOY_GAME: 94 | return "Toy, Game"; 95 | case BluetoothClass.Device.TOY_ROBOT: 96 | return "Toy, Robot"; 97 | case BluetoothClass.Device.TOY_UNCATEGORIZED: 98 | return "Toy, Uncategorized"; 99 | case BluetoothClass.Device.TOY_VEHICLE: 100 | return "Toy, Vehicle"; 101 | case BluetoothClass.Device.WEARABLE_GLASSES: 102 | return "Wearable, Glasses"; 103 | case BluetoothClass.Device.WEARABLE_HELMET: 104 | return "Wearable, Helmet"; 105 | case BluetoothClass.Device.WEARABLE_JACKET: 106 | return "Wearable, Jacket"; 107 | case BluetoothClass.Device.WEARABLE_PAGER: 108 | return "Wearable, Pager"; 109 | case BluetoothClass.Device.WEARABLE_UNCATEGORIZED: 110 | return "Wearable, Uncategorized"; 111 | case BluetoothClass.Device.WEARABLE_WRIST_WATCH: 112 | return "Wearable, Wrist Watch"; 113 | default: 114 | return "Unknown, Unknown (class=" + btClass + ")"; 115 | } 116 | } 117 | 118 | public static String resolveMajorDeviceClass(final int majorBtClass) { 119 | switch (majorBtClass) { 120 | case BluetoothClass.Device.Major.AUDIO_VIDEO: 121 | return "Audio/ Video"; 122 | case BluetoothClass.Device.Major.COMPUTER: 123 | return "Computer"; 124 | case BluetoothClass.Device.Major.HEALTH: 125 | return "Health"; 126 | case BluetoothClass.Device.Major.IMAGING: 127 | return "Imaging"; 128 | case BluetoothClass.Device.Major.MISC: 129 | return "Misc"; 130 | case BluetoothClass.Device.Major.NETWORKING: 131 | return "Networking"; 132 | case BluetoothClass.Device.Major.PERIPHERAL: 133 | return "Peripheral"; 134 | case BluetoothClass.Device.Major.PHONE: 135 | return "Phone"; 136 | case BluetoothClass.Device.Major.TOY: 137 | return "Toy"; 138 | case BluetoothClass.Device.Major.UNCATEGORIZED: 139 | return "Uncategorized"; 140 | case BluetoothClass.Device.Major.WEARABLE: 141 | return "Wearable"; 142 | default: 143 | return "Unknown (" + majorBtClass + ")"; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /ios/sand-plugin-bluetooth-ios/sand-plugin-bluetooth/objc/BabyPeripheralManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // BabyPeripheralManager.m 3 | // BluetoothStubOnIOS 4 | // 5 | // Created by 刘彦玮 on 15/12/12. 6 | // Copyright © 2015年 刘彦玮. All rights reserved. 7 | // 8 | 9 | #import "BabyPeripheralManager.h" 10 | #import "BabyDefine.h" 11 | 12 | #define callbackBlock(...) if ([[babySpeaker callback] __VA_ARGS__]) [[babySpeaker callback] __VA_ARGS__ ] 13 | 14 | @implementation BabyPeripheralManager { 15 | int PERIPHERAL_MANAGER_INIT_WAIT_TIMES; 16 | int didAddServices; 17 | NSTimer *addServiceTask; 18 | } 19 | 20 | - (instancetype)init { 21 | self = [super init]; 22 | if (self) { 23 | _localName = @"baby-default-name"; 24 | _peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil options:nil]; 25 | } 26 | return self; 27 | } 28 | 29 | 30 | - (BabyPeripheralManager *(^)())startAdvertising { 31 | return ^BabyPeripheralManager *() { 32 | 33 | if ([self canStartAdvertising]) { 34 | PERIPHERAL_MANAGER_INIT_WAIT_TIMES = 0; 35 | NSMutableArray *UUIDS = [NSMutableArray array]; 36 | for (CBMutableService *s in _services) { 37 | [UUIDS addObject:s.UUID]; 38 | } 39 | //启动广播 40 | [_peripheralManager startAdvertising: 41 | @{ 42 | CBAdvertisementDataServiceUUIDsKey : UUIDS 43 | ,CBAdvertisementDataLocalNameKey : _localName 44 | }]; 45 | } 46 | else { 47 | PERIPHERAL_MANAGER_INIT_WAIT_TIMES++; 48 | if (PERIPHERAL_MANAGER_INIT_WAIT_TIMES > 5) { 49 | BabyLog(@">>>error: 第%d次等待peripheralManager打开任然失败,请检查蓝牙设备是否可用",PERIPHERAL_MANAGER_INIT_WAIT_TIMES); 50 | } 51 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC); 52 | dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 53 | self.startAdvertising(); 54 | }); 55 | BabyLog(@">>> 第%d次等待peripheralManager打开",PERIPHERAL_MANAGER_INIT_WAIT_TIMES); 56 | } 57 | 58 | return self; 59 | }; 60 | } 61 | 62 | - (BOOL)canStartAdvertising { 63 | if (_peripheralManager.state != CBPeripheralManagerStatePoweredOn) { 64 | return NO; 65 | } 66 | if (didAddServices != _services.count) { 67 | return NO; 68 | } 69 | return YES; 70 | } 71 | 72 | - (BOOL)isPoweredOn { 73 | if (_peripheralManager.state != CBPeripheralManagerStatePoweredOn) { 74 | return NO; 75 | } 76 | return YES; 77 | } 78 | 79 | - (BabyPeripheralManager *(^)(NSArray *array))addServices { 80 | return ^BabyPeripheralManager*(NSArray *array) { 81 | _services = [NSMutableArray arrayWithArray:array]; 82 | [self addServicesToPeripheral]; 83 | return self; 84 | }; 85 | } 86 | 87 | - (void)addServicesToPeripheral { 88 | if ([self isPoweredOn]) { 89 | for (CBMutableService *s in _services) { 90 | [_peripheralManager addService:s]; 91 | } 92 | } 93 | else { 94 | [addServiceTask setFireDate:[NSDate distantPast]]; 95 | addServiceTask = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(addServicesToPeripheral) userInfo:nil repeats:NO]; 96 | } 97 | } 98 | 99 | #pragma mark - peripheralManager delegate 100 | 101 | - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { 102 | switch (peripheral.state) { 103 | case CBPeripheralManagerStateUnknown: 104 | BabyLog(@">>>CBPeripheralManagerStateUnknown"); 105 | break; 106 | case CBPeripheralManagerStateResetting: 107 | BabyLog(@">>>CBPeripheralManagerStateResetting"); 108 | break; 109 | case CBPeripheralManagerStateUnsupported: 110 | BabyLog(@">>>CBPeripheralManagerStateUnsupported"); 111 | break; 112 | case CBPeripheralManagerStateUnauthorized: 113 | BabyLog(@">>>CBPeripheralManagerStateUnauthorized"); 114 | break; 115 | case CBPeripheralManagerStatePoweredOff: 116 | BabyLog(@">>>CBPeripheralManagerStatePoweredOff"); 117 | break; 118 | case CBPeripheralManagerStatePoweredOn: 119 | BabyLog(@">>>CBPeripheralManagerStatePoweredOn"); 120 | //发送centralManagerDidUpdateState通知 121 | [[NSNotificationCenter defaultCenter]postNotificationName:@"CBPeripheralManagerStatePoweredOn" object:nil]; 122 | break; 123 | default: 124 | break; 125 | } 126 | 127 | // if([babySpeaker callback] blockOnPeripheralModelDidUpdateState) { 128 | // [currChannel blockOnCancelScan](centralManager); 129 | // } 130 | callbackBlock(blockOnPeripheralModelDidUpdateState)(peripheral); 131 | } 132 | 133 | 134 | - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error { 135 | didAddServices++; 136 | callbackBlock(blockOnPeripheralModelDidAddService)(peripheral,service,error); 137 | } 138 | 139 | - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error { 140 | callbackBlock(blockOnPeripheralModelDidStartAdvertising)(peripheral,error); 141 | } 142 | 143 | - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request { 144 | callbackBlock(blockOnPeripheralModelDidReceiveReadRequest)(peripheral, request); 145 | } 146 | 147 | - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests { 148 | callbackBlock(blockOnPeripheralModelDidReceiveWriteRequests)(peripheral,requests); 149 | } 150 | 151 | - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic { 152 | callbackBlock(blockOnPeripheralModelDidSubscribeToCharacteristic)(peripheral,central,characteristic); 153 | } 154 | 155 | - (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic { 156 | callbackBlock(blockOnPeripheralModelDidUnSubscribeToCharacteristic)(peripheral,central,characteristic); 157 | } 158 | 159 | 160 | @end 161 | 162 | void makeCharacteristicToService(CBMutableService *service,NSString *UUID,NSString *properties,NSString *descriptor) { 163 | 164 | //paramter for properties 165 | CBCharacteristicProperties prop = 0x00; 166 | if ([properties containsString:@"r"]) { 167 | prop = prop | CBCharacteristicPropertyRead; 168 | } 169 | if ([properties containsString:@"w"]) { 170 | prop = prop | CBCharacteristicPropertyWrite; 171 | } 172 | if ([properties containsString:@"n"]) { 173 | prop = prop | CBCharacteristicPropertyNotify; 174 | } 175 | if (properties == nil || [properties isEqualToString:@""]) { 176 | prop = CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite; 177 | } 178 | 179 | CBMutableCharacteristic *c = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:UUID] properties:prop value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable]; 180 | 181 | //paramter for descriptor 182 | if (!(descriptor == nil || [descriptor isEqualToString:@""])) { 183 | //c设置description对应的haracteristics字段描述 184 | CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString]; 185 | CBMutableDescriptor *desc = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:descriptor]; 186 | [c setDescriptors:@[desc]]; 187 | } 188 | 189 | if (!service.characteristics) { 190 | service.characteristics = @[]; 191 | } 192 | NSMutableArray *cs = [service.characteristics mutableCopy]; 193 | [cs addObject:c]; 194 | service.characteristics = [cs copy]; 195 | } 196 | void makeStaticCharacteristicToService(CBMutableService *service,NSString *UUID,NSString *descriptor,NSData *data) { 197 | 198 | CBMutableCharacteristic *c = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:UUID] properties:CBCharacteristicPropertyRead value:data permissions:CBAttributePermissionsReadable]; 199 | 200 | //paramter for descriptor 201 | if (!(descriptor == nil || [descriptor isEqualToString:@""])) { 202 | //c设置description对应的haracteristics字段描述 203 | CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString]; 204 | CBMutableDescriptor *desc = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:descriptor]; 205 | [c setDescriptors:@[desc]]; 206 | } 207 | 208 | if (!service.characteristics) { 209 | service.characteristics = @[]; 210 | } 211 | NSMutableArray *cs = [service.characteristics mutableCopy]; 212 | [cs addObject:c]; 213 | service.characteristics = [cs copy]; 214 | } 215 | 216 | 217 | CBMutableService* makeCBService(NSString *UUID) 218 | { 219 | CBMutableService *s = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:UUID] primary:YES]; 220 | return s; 221 | } 222 | 223 | NSString * genUUID() 224 | { 225 | CFUUIDRef uuid_ref = CFUUIDCreate(NULL); 226 | CFStringRef uuid_string_ref= CFUUIDCreateString(NULL, uuid_ref); 227 | 228 | CFRelease(uuid_ref); 229 | NSString *uuid = [NSString stringWithString:(__bridge NSString*)uuid_string_ref]; 230 | 231 | CFRelease(uuid_string_ref); 232 | return uuid; 233 | } 234 | 235 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/model/adrecord/AdRecord.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.model.adrecord; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @Description: 广播包解析model 11 | * 参考:https://www.bluetooth.com/zh-cn/specifications/assigned-numbers/generic-access-profile 12 | * @author: DAWI 13 | * @date: 16/8/7 21:53. 14 | */ 15 | public class AdRecord implements Parcelable { 16 | 17 | public static final int BLE_GAP_AD_TYPE_FLAGS = 0x01;//< Flags for discoverAbility. 18 | public static final int BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE = 0x02;//< Partial list of 16 bit service UUIDs. 19 | public static final int BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE = 0x03;//< Complete list of 16 bit service UUIDs. 20 | public static final int BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE = 0x04;//< Partial list of 32 bit service UUIDs. 21 | public static final int BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE = 0x05;//< Complete list of 32 bit service UUIDs. 22 | public static final int BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE = 0x06;//< Partial list of 128 bit service UUIDs. 23 | public static final int BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE = 0x07;//< Complete list of 128 bit service UUIDs. 24 | public static final int BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME = 0x08;//< Short local device name. 25 | public static final int BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME = 0x09;//< Complete local device name. 26 | public static final int BLE_GAP_AD_TYPE_TX_POWER_LEVEL = 0x0A;//< Transmit power level. 27 | public static final int BLE_GAP_AD_TYPE_CLASS_OF_DEVICE = 0x0D;//< Class of device. 28 | public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C = 0x0E;//< Simple Pairing Hash C. 29 | public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F;//< Simple Pairing Randomizer R. 30 | public static final int BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE = 0x10;//< Security Manager TK Value. 31 | public static final int BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11;//< Security Manager Out Of Band Flags. 32 | public static final int BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE = 0x12;//< Slave Connection Interval Range. 33 | public static final int BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT = 0x14;//< List of 16-bit Service Solicitation UUIDs. 34 | public static final int BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT = 0x15;//< List of 128-bit Service Solicitation UUIDs. 35 | public static final int BLE_GAP_AD_TYPE_SERVICE_DATA = 0x16;//< Service Data - 16-bit UUID. 36 | public static final int BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS = 0x17;//< Public Target Address. 37 | public static final int BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS = 0x18;//< Random Target Address. 38 | public static final int BLE_GAP_AD_TYPE_APPEARANCE = 0x19;//< Appearance. 39 | public static final int BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL = 0x1A;//< Advertising Interval. 40 | public static final int BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B;//< LE Bluetooth Device Address. 41 | public static final int BLE_GAP_AD_TYPE_LE_ROLE = 0x1C;//< LE Role. 42 | public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256 = 0x1D;//< Simple Pairing Hash C-256. 43 | public static final int BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256 = 0x1E;//< Simple Pairing Randomizer R-256. 44 | public static final int BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID = 0x20;//< Service Data - 32-bit UUID. 45 | public static final int BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID = 0x21;//< Service Data - 128-bit UUID. 46 | public static final int BLE_GAP_AD_TYPE_3D_INFORMATION_DATA = 0x3D;//< 3D Information Data. 47 | public static final int BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;//< Manufacturer Specific Data. 48 | 49 | public static final Creator CREATOR = new Creator() { 50 | public AdRecord createFromParcel(final Parcel in) { 51 | return new AdRecord(in); 52 | } 53 | 54 | public AdRecord[] newArray(final int size) { 55 | return new AdRecord[size]; 56 | } 57 | }; 58 | private static final String PARCEL_RECORD_DATA = "record_data"; 59 | private static final String PARCEL_RECORD_TYPE = "record_type"; 60 | private static final String PARCEL_RECORD_LENGTH = "record_length"; 61 | /* Model Object Definition */ 62 | private final int mLength; 63 | private final int mType; 64 | private final byte[] mData; 65 | 66 | public AdRecord(final int length, final int type, final byte[] data) { 67 | mLength = length; 68 | mType = type; 69 | mData = data; 70 | } 71 | 72 | public AdRecord(final Parcel in) { 73 | final Bundle b = in.readBundle(getClass().getClassLoader()); 74 | mLength = b.getInt(PARCEL_RECORD_LENGTH); 75 | mType = b.getInt(PARCEL_RECORD_TYPE); 76 | mData = b.getByteArray(PARCEL_RECORD_DATA); 77 | } 78 | 79 | @Override 80 | public int describeContents() { 81 | return 0; 82 | } 83 | 84 | public byte[] getData() { 85 | return mData; 86 | } 87 | 88 | public String getHumanReadableType() { 89 | return getHumanReadableAdType(mType); 90 | } 91 | 92 | public int getLength() { 93 | return mLength; 94 | } 95 | 96 | public int getType() { 97 | return mType; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "AdRecord [mLength=" + mLength + ", mType=" + mType + ", mData=" + Arrays.toString(mData) + ", getHumanReadableType()=" + 103 | getHumanReadableType() + "]"; 104 | } 105 | 106 | @Override 107 | public void writeToParcel(final Parcel parcel, final int arg1) { 108 | final Bundle b = new Bundle(getClass().getClassLoader()); 109 | 110 | b.putInt(PARCEL_RECORD_LENGTH, mLength); 111 | b.putInt(PARCEL_RECORD_TYPE, mType); 112 | b.putByteArray(PARCEL_RECORD_DATA, mData); 113 | 114 | parcel.writeBundle(b); 115 | } 116 | 117 | private static String getHumanReadableAdType(final int type) { 118 | switch (type) { 119 | case BLE_GAP_AD_TYPE_FLAGS: 120 | return "Flags for discoverAbility."; 121 | case BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE: 122 | return "Partial list of 16 bit service UUIDs."; 123 | case BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE: 124 | return "Complete list of 16 bit service UUIDs."; 125 | case BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE: 126 | return "Partial list of 32 bit service UUIDs."; 127 | case BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE: 128 | return "Complete list of 32 bit service UUIDs."; 129 | case BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE: 130 | return "Partial list of 128 bit service UUIDs."; 131 | case BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE: 132 | return "Complete list of 128 bit service UUIDs."; 133 | case BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME: 134 | return "Short local device name."; 135 | case BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME: 136 | return "Complete local device name."; 137 | case BLE_GAP_AD_TYPE_TX_POWER_LEVEL: 138 | return "Transmit power level."; 139 | case BLE_GAP_AD_TYPE_CLASS_OF_DEVICE: 140 | return "Class of device."; 141 | case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C: 142 | return "Simple Pairing Hash C."; 143 | case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R: 144 | return "Simple Pairing Randomizer R."; 145 | case BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE: 146 | return "Security Manager TK Value."; 147 | case BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS: 148 | return "Security Manager Out Of Band Flags."; 149 | case BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE: 150 | return "Slave Connection Interval Range."; 151 | case BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT: 152 | return "List of 16-bit Service Solicitation UUIDs."; 153 | case BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT: 154 | return "List of 128-bit Service Solicitation UUIDs."; 155 | case BLE_GAP_AD_TYPE_SERVICE_DATA: 156 | return "Service Data - 16-bit UUID."; 157 | case BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS: 158 | return "Public Target Address."; 159 | case BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS: 160 | return "Random Target Address."; 161 | case BLE_GAP_AD_TYPE_APPEARANCE: 162 | return "Appearance."; 163 | case BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL: 164 | return "Advertising Interval."; 165 | case BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS: 166 | return "LE Bluetooth Device Address."; 167 | case BLE_GAP_AD_TYPE_LE_ROLE: 168 | return "LE Role."; 169 | case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256: 170 | return "Simple Pairing Hash C-256."; 171 | case BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256: 172 | return "Simple Pairing Randomizer R-256."; 173 | case BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID: 174 | return "Service Data - 32-bit UUID."; 175 | case BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID: 176 | return "Service Data - 128-bit UUID."; 177 | case BLE_GAP_AD_TYPE_3D_INFORMATION_DATA: 178 | return "3D Information Data."; 179 | case BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA: 180 | return "Manufacturer Specific Data."; 181 | default: 182 | return "Unknown AdRecord Structure: " + type; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/utils/HexUtil.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.utils; 2 | 3 | import com.vise.log.ViseLog; 4 | 5 | /** 6 | * @Description: 十六进制转换类 7 | * @author: DAWI 8 | * @date: 16/8/7 21:57. 9 | */ 10 | public class HexUtil { 11 | /** 12 | * 用于建立十六进制字符的输出的小写字符数组 13 | */ 14 | private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 15 | 16 | /** 17 | * 用于建立十六进制字符的输出的大写字符数组 18 | */ 19 | private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 20 | 21 | /** 22 | * 将字节数组转换为十六进制字符数组 23 | * 24 | * @param data byte[] 25 | * @return 十六进制char[] 26 | */ 27 | public static char[] encodeHex(byte[] data) { 28 | return encodeHex(data, true); 29 | } 30 | 31 | /** 32 | * 将字节数组转换为十六进制字符数组 33 | * 34 | * @param data byte[] 35 | * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 36 | * @return 十六进制char[] 37 | */ 38 | public static char[] encodeHex(byte[] data, boolean toLowerCase) { 39 | return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 40 | } 41 | 42 | /** 43 | * 将字节数组转换为十六进制字符数组 44 | * 45 | * @param data byte[] 46 | * @param toDigits 用于控制输出的char[] 47 | * @return 十六进制char[] 48 | */ 49 | protected static char[] encodeHex(byte[] data, char[] toDigits) { 50 | int l = data.length; 51 | char[] out = new char[l << 1]; 52 | // two characters form the hex value. 53 | for (int i = 0, j = 0; i < l; i++) { 54 | out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; 55 | out[j++] = toDigits[0x0F & data[i]]; 56 | } 57 | return out; 58 | } 59 | 60 | /** 61 | * 将字节数组转换为十六进制字符串 62 | * 63 | * @param data byte[] 64 | * @return 十六进制String 65 | */ 66 | public static String encodeHexStr(byte[] data) { 67 | return encodeHexStr(data, true); 68 | } 69 | 70 | /** 71 | * 将字节数组转换为十六进制字符串 72 | * 73 | * @param data byte[] 74 | * @param toLowerCase true 传换成小写格式 , false 传换成大写格式 75 | * @return 十六进制String 76 | */ 77 | public static String encodeHexStr(byte[] data, boolean toLowerCase) { 78 | return encodeHexStr(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); 79 | } 80 | 81 | /** 82 | * 将字节数组转换为十六进制字符串 83 | * 84 | * @param data byte[] 85 | * @param toDigits 用于控制输出的char[] 86 | * @return 十六进制String 87 | */ 88 | protected static String encodeHexStr(byte[] data, char[] toDigits) { 89 | if (data == null) { 90 | ViseLog.e("this data is null."); 91 | return ""; 92 | } 93 | return new String(encodeHex(data, toDigits)); 94 | } 95 | 96 | /** 97 | * 将十六进制字符串转换为字节数组 98 | * 99 | * @param data 100 | * @return 101 | */ 102 | public static byte[] decodeHex(String data) { 103 | if (data == null) { 104 | ViseLog.e("this data is null."); 105 | return new byte[0]; 106 | } 107 | return decodeHex(data.toCharArray()); 108 | } 109 | 110 | /** 111 | * 将十六进制字符数组转换为字节数组 112 | * 113 | * @param data 十六进制char[] 114 | * @return byte[] 115 | * @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常 116 | */ 117 | public static byte[] decodeHex(char[] data) { 118 | 119 | int len = data.length; 120 | 121 | if ((len & 0x01) != 0) { 122 | throw new RuntimeException("Odd number of characters."); 123 | } 124 | 125 | byte[] out = new byte[len >> 1]; 126 | 127 | // two characters form the hex value. 128 | for (int i = 0, j = 0; j < len; i++) { 129 | int f = toDigit(data[j], j) << 4; 130 | j++; 131 | f = f | toDigit(data[j], j); 132 | j++; 133 | out[i] = (byte) (f & 0xFF); 134 | } 135 | 136 | return out; 137 | } 138 | 139 | /** 140 | * 将十六进制字符转换成一个整数 141 | * 142 | * @param ch 十六进制char 143 | * @param index 十六进制字符在字符数组中的位置 144 | * @return 一个整数 145 | * @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常 146 | */ 147 | protected static int toDigit(char ch, int index) { 148 | int digit = Character.digit(ch, 16); 149 | if (digit == -1) { 150 | throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index); 151 | } 152 | return digit; 153 | } 154 | 155 | /** 156 | * 截取字节数组 157 | * 158 | * @param src byte [] 数组源 这里填16进制的 数组 159 | * @param begin 起始位置 源数组的起始位置。0位置有效 160 | * @param count 截取长度 161 | * @return 162 | */ 163 | public static byte[] subBytes(byte[] src, int begin, int count) { 164 | byte[] bs = new byte[count]; 165 | System.arraycopy(src, begin, bs, 0, count); // bs 目的数组 0 截取后存放的数值起始位置。0位置有效 166 | return bs; 167 | } 168 | 169 | /** 170 | * int转byte数组 171 | * 172 | * @param bb 173 | * @param x 174 | * @param index 第几位开始 175 | * @param flag 标识高低位顺序,高位在前为true,低位在前为false 176 | */ 177 | public static void intToByte(byte[] bb, int x, int index, boolean flag) { 178 | if (flag) { 179 | bb[index + 0] = (byte) (x >> 24); 180 | bb[index + 1] = (byte) (x >> 16); 181 | bb[index + 2] = (byte) (x >> 8); 182 | bb[index + 3] = (byte) (x >> 0); 183 | } else { 184 | bb[index + 3] = (byte) (x >> 24); 185 | bb[index + 2] = (byte) (x >> 16); 186 | bb[index + 1] = (byte) (x >> 8); 187 | bb[index + 0] = (byte) (x >> 0); 188 | } 189 | } 190 | 191 | /** 192 | * byte数组转int 193 | * 194 | * @param bb 195 | * @param index 第几位开始 196 | * @param flag 标识高低位顺序,高位在前为true,低位在前为false 197 | * @return 198 | */ 199 | public static int byteToInt(byte[] bb, int index, boolean flag) { 200 | if (flag) { 201 | return (int) ((((bb[index + 0] & 0xff) << 24) 202 | | ((bb[index + 1] & 0xff) << 16) 203 | | ((bb[index + 2] & 0xff) << 8) 204 | | ((bb[index + 3] & 0xff) << 0))); 205 | } else { 206 | return (int) ((((bb[index + 3] & 0xff) << 24) 207 | | ((bb[index + 2] & 0xff) << 16) 208 | | ((bb[index + 1] & 0xff) << 8) 209 | | ((bb[index + 0] & 0xff) << 0))); 210 | } 211 | } 212 | 213 | 214 | /** 215 | * 字节数组逆序 216 | * 217 | * @param data 218 | * @return 219 | */ 220 | public static byte[] reverse(byte[] data) { 221 | byte[] reverseData = new byte[data.length]; 222 | for (int i = 0; i < data.length; i++) { 223 | reverseData[i] = data[data.length - 1 - i]; 224 | } 225 | return reverseData; 226 | } 227 | 228 | /** 229 | * 蓝牙传输 16进制 高低位 读数的 转换 230 | * 231 | * @param data 截取数据源,字节数组 232 | * @param index 截取数据开始位置 233 | * @param count 截取数据长度,只能为2、4、8个字节 234 | * @param flag 标识高低位顺序,高位在前为true,低位在前为false 235 | * @return 236 | */ 237 | public static long byteToLong(byte[] data, int index, int count, boolean flag) { 238 | long lg = 0; 239 | if (flag) { 240 | switch (count) { 241 | case 2: 242 | lg = ((((long) data[index + 0] & 0xff) << 8) 243 | | (((long) data[index + 1] & 0xff) << 0)); 244 | break; 245 | 246 | case 4: 247 | lg = ((((long) data[index + 0] & 0xff) << 24) 248 | | (((long) data[index + 1] & 0xff) << 16) 249 | | (((long) data[index + 2] & 0xff) << 8) 250 | | (((long) data[index + 3] & 0xff) << 0)); 251 | break; 252 | 253 | case 8: 254 | lg = ((((long) data[index + 0] & 0xff) << 56) 255 | | (((long) data[index + 1] & 0xff) << 48) 256 | | (((long) data[index + 2] & 0xff) << 40) 257 | | (((long) data[index + 3] & 0xff) << 32) 258 | | (((long) data[index + 4] & 0xff) << 24) 259 | | (((long) data[index + 5] & 0xff) << 16) 260 | | (((long) data[index + 6] & 0xff) << 8) 261 | | (((long) data[index + 7] & 0xff) << 0)); 262 | break; 263 | } 264 | return lg; 265 | } else { 266 | switch (count) { 267 | case 2: 268 | lg = ((((long) data[index + 1] & 0xff) << 8) 269 | | (((long) data[index + 0] & 0xff) << 0)); 270 | break; 271 | case 4: 272 | lg = ((((long) data[index + 3] & 0xff) << 24) 273 | | (((long) data[index + 2] & 0xff) << 16) 274 | | (((long) data[index + 1] & 0xff) << 8) 275 | | (((long) data[index + 0] & 0xff) << 0)); 276 | break; 277 | case 8: 278 | lg = ((((long) data[index + 7] & 0xff) << 56) 279 | | (((long) data[index + 6] & 0xff) << 48) 280 | | (((long) data[index + 5] & 0xff) << 40) 281 | | (((long) data[index + 4] & 0xff) << 32) 282 | | (((long) data[index + 3] & 0xff) << 24) 283 | | (((long) data[index + 2] & 0xff) << 16) 284 | | (((long) data[index + 1] & 0xff) << 8) 285 | | (((long) data[index + 0] & 0xff) << 0)); 286 | break; 287 | } 288 | return lg; 289 | } 290 | } 291 | 292 | } 293 | -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/ViseBle.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothManager; 5 | import android.content.Context; 6 | import android.os.Handler; 7 | import android.os.Looper; 8 | import android.text.TextUtils; 9 | 10 | import com.vise.baseble.callback.IConnectCallback; 11 | import com.vise.baseble.callback.scan.IScanCallback; 12 | import com.vise.baseble.callback.scan.ScanCallback; 13 | import com.vise.baseble.callback.scan.SingleFilterScanCallback; 14 | import com.vise.baseble.common.BleConfig; 15 | import com.vise.baseble.common.ConnectState; 16 | import com.vise.baseble.core.DeviceMirror; 17 | import com.vise.baseble.core.DeviceMirrorPool; 18 | import com.vise.baseble.exception.TimeoutException; 19 | import com.vise.baseble.model.BluetoothLeDevice; 20 | import com.vise.baseble.model.BluetoothLeDeviceStore; 21 | import com.vise.log.ViseLog; 22 | 23 | /** 24 | * @Description: BLE设备操作入口 25 | * @author: DAWI 26 | * @date: 17/8/1 22:24. 27 | */ 28 | public class ViseBle { 29 | private Context context;//上下文 30 | private BluetoothManager bluetoothManager;//蓝牙管理 31 | private BluetoothAdapter bluetoothAdapter;//蓝牙适配器 32 | private DeviceMirrorPool deviceMirrorPool;//设备连接池 33 | private DeviceMirror lastDeviceMirror;//上次操作设备镜像 34 | 35 | private static ViseBle instance;//入口操作管理 36 | private static BleConfig bleConfig = BleConfig.getInstance(); 37 | 38 | /** 39 | * 单例方式获取蓝牙通信入口 40 | * 41 | * @return 返回ViseBluetooth 42 | */ 43 | public static ViseBle getInstance() { 44 | if (instance == null) { 45 | synchronized (ViseBle.class) { 46 | if (instance == null) { 47 | instance = new ViseBle(); 48 | } 49 | } 50 | } 51 | return instance; 52 | } 53 | 54 | private ViseBle() { 55 | } 56 | 57 | /** 58 | * 获取配置对象,可进行相关配置的修改 59 | * 60 | * @return 61 | */ 62 | public static BleConfig config() { 63 | return bleConfig; 64 | } 65 | 66 | /** 67 | * 初始化 68 | * 69 | * @param context 上下文 70 | */ 71 | public void init(Context context) { 72 | if (this.context == null && context != null) { 73 | this.context = context.getApplicationContext(); 74 | bluetoothManager = (BluetoothManager) this.context.getSystemService(Context.BLUETOOTH_SERVICE); 75 | bluetoothAdapter = bluetoothManager.getAdapter(); 76 | deviceMirrorPool = new DeviceMirrorPool(); 77 | } 78 | } 79 | 80 | /** 81 | * 开始扫描 82 | * 83 | * @param scanCallback 自定义回调 84 | */ 85 | public void startScan(ScanCallback scanCallback) { 86 | if (scanCallback == null) { 87 | throw new IllegalArgumentException("this ScanCallback is Null!"); 88 | } 89 | scanCallback.setScan(true).scan(); 90 | } 91 | 92 | /** 93 | * 停止扫描 94 | * 95 | * @param scanCallback 自定义回调 96 | */ 97 | public void stopScan(ScanCallback scanCallback) { 98 | if (scanCallback == null) { 99 | throw new IllegalArgumentException("this ScanCallback is Null!"); 100 | } 101 | scanCallback.setScan(false).removeHandlerMsg().scan(); 102 | } 103 | 104 | /** 105 | * 连接设备 106 | * 107 | * @param bluetoothLeDevice 108 | * @param connectCallback 109 | */ 110 | public void connect(BluetoothLeDevice bluetoothLeDevice, IConnectCallback connectCallback) { 111 | if (bluetoothLeDevice == null || connectCallback == null) { 112 | ViseLog.e("This bluetoothLeDevice or connectCallback is null."); 113 | return; 114 | } 115 | if (deviceMirrorPool != null && !deviceMirrorPool.isContainDevice(bluetoothLeDevice)) { 116 | DeviceMirror deviceMirror = new DeviceMirror(bluetoothLeDevice); 117 | if (lastDeviceMirror != null && !TextUtils.isEmpty(lastDeviceMirror.getUniqueSymbol()) 118 | && lastDeviceMirror.getUniqueSymbol().equals(deviceMirror.getUniqueSymbol())) { 119 | deviceMirror = lastDeviceMirror;//防止重复创建设备镜像 120 | } 121 | deviceMirror.connect(connectCallback); 122 | lastDeviceMirror = deviceMirror; 123 | } else { 124 | ViseLog.i("This device is connected."); 125 | } 126 | } 127 | 128 | /** 129 | * 连接指定mac地址的设备 130 | * 131 | * @param mac 设备mac地址 132 | * @param connectCallback 连接回调 133 | */ 134 | public void connectByMac(String mac, final IConnectCallback connectCallback) { 135 | if (mac == null || connectCallback == null) { 136 | ViseLog.e("This mac or connectCallback is null."); 137 | return; 138 | } 139 | startScan(new SingleFilterScanCallback(new IScanCallback() { 140 | @Override 141 | public void onDeviceFound(BluetoothLeDevice bluetoothLeDevice) { 142 | 143 | } 144 | 145 | @Override 146 | public void onScanFinish(final BluetoothLeDeviceStore bluetoothLeDeviceStore) { 147 | if (bluetoothLeDeviceStore.getDeviceList().size() > 0) { 148 | new Handler(Looper.getMainLooper()).post(new Runnable() { 149 | @Override 150 | public void run() { 151 | connect(bluetoothLeDeviceStore.getDeviceList().get(0), connectCallback); 152 | } 153 | }); 154 | } else { 155 | connectCallback.onConnectFailure(null,new TimeoutException()); 156 | } 157 | } 158 | 159 | @Override 160 | public void onScanTimeout() { 161 | connectCallback.onConnectFailure(null,new TimeoutException()); 162 | } 163 | 164 | }).setDeviceMac(mac)); 165 | } 166 | 167 | /** 168 | * 连接指定设备名称的设备 169 | * 170 | * @param name 设备名称 171 | * @param connectCallback 连接回调 172 | */ 173 | public void connectByName(String name, final IConnectCallback connectCallback) { 174 | if (name == null || connectCallback == null) { 175 | ViseLog.e("This name or connectCallback is null."); 176 | return; 177 | } 178 | startScan(new SingleFilterScanCallback(new IScanCallback() { 179 | @Override 180 | public void onDeviceFound(BluetoothLeDevice bluetoothLeDevice) { 181 | 182 | } 183 | 184 | @Override 185 | public void onScanFinish(final BluetoothLeDeviceStore bluetoothLeDeviceStore) { 186 | if (bluetoothLeDeviceStore.getDeviceList().size() > 0) { 187 | new Handler(Looper.getMainLooper()).post(new Runnable() { 188 | @Override 189 | public void run() { 190 | connect(bluetoothLeDeviceStore.getDeviceList().get(0), connectCallback); 191 | } 192 | }); 193 | } else { 194 | connectCallback.onConnectFailure(null,new TimeoutException()); 195 | } 196 | } 197 | 198 | @Override 199 | public void onScanTimeout() { 200 | connectCallback.onConnectFailure(null,new TimeoutException()); 201 | } 202 | 203 | }).setDeviceName(name)); 204 | } 205 | 206 | /** 207 | * 获取连接池中的设备镜像,如果没有连接则返回空 208 | * 209 | * @param bluetoothLeDevice 210 | * @return 211 | */ 212 | public DeviceMirror getDeviceMirror(BluetoothLeDevice bluetoothLeDevice) { 213 | if (deviceMirrorPool != null) { 214 | return deviceMirrorPool.getDeviceMirror(bluetoothLeDevice); 215 | } 216 | return null; 217 | } 218 | 219 | /** 220 | * 获取该设备连接状态 221 | * 222 | * @param bluetoothLeDevice 223 | * @return 224 | */ 225 | public ConnectState getConnectState(BluetoothLeDevice bluetoothLeDevice) { 226 | if (deviceMirrorPool != null) { 227 | return deviceMirrorPool.getConnectState(bluetoothLeDevice); 228 | } 229 | return ConnectState.CONNECT_DISCONNECT; 230 | } 231 | 232 | /** 233 | * 判断该设备是否已连接 234 | * 235 | * @param bluetoothLeDevice 236 | * @return 237 | */ 238 | public boolean isConnect(BluetoothLeDevice bluetoothLeDevice) { 239 | if (deviceMirrorPool != null) { 240 | return deviceMirrorPool.isContainDevice(bluetoothLeDevice); 241 | } 242 | return false; 243 | } 244 | 245 | /** 246 | * 断开某一个设备 247 | * 248 | * @param bluetoothLeDevice 249 | */ 250 | public void disconnect(BluetoothLeDevice bluetoothLeDevice) { 251 | if (deviceMirrorPool != null) { 252 | deviceMirrorPool.disconnect(bluetoothLeDevice); 253 | } 254 | } 255 | 256 | /** 257 | * 断开所有设备 258 | */ 259 | public void disconnect() { 260 | if (deviceMirrorPool != null) { 261 | deviceMirrorPool.disconnect(); 262 | } 263 | } 264 | 265 | /** 266 | * 清除资源,在退出应用时调用 267 | */ 268 | public void clear() { 269 | if (deviceMirrorPool != null) { 270 | deviceMirrorPool.clear(); 271 | } 272 | } 273 | 274 | /** 275 | * 获取Context 276 | * 277 | * @return 返回Context 278 | */ 279 | public Context getContext() { 280 | return context; 281 | } 282 | 283 | /** 284 | * 获取蓝牙管理 285 | * 286 | * @return 返回蓝牙管理 287 | */ 288 | public BluetoothManager getBluetoothManager() { 289 | return bluetoothManager; 290 | } 291 | 292 | /** 293 | * 获取蓝牙适配器 294 | * 295 | * @return 返回蓝牙适配器 296 | */ 297 | public BluetoothAdapter getBluetoothAdapter() { 298 | return bluetoothAdapter; 299 | } 300 | 301 | /** 302 | * 获取设备镜像池 303 | * 304 | * @return 305 | */ 306 | public DeviceMirrorPool getDeviceMirrorPool() { 307 | return deviceMirrorPool; 308 | } 309 | 310 | /** 311 | * 获取当前连接失败重试次数 312 | * 313 | * @return 314 | */ 315 | public int getConnectRetryCount() { 316 | if (lastDeviceMirror == null) { 317 | return 0; 318 | } 319 | return lastDeviceMirror.getConnectRetryCount(); 320 | } 321 | 322 | /** 323 | * 获取当前读取数据失败重试次数 324 | * 325 | * @return 326 | */ 327 | public int getReadDataRetryCount() { 328 | if (lastDeviceMirror == null) { 329 | return 0; 330 | } 331 | return lastDeviceMirror.getReadDataRetryCount(); 332 | } 333 | 334 | /** 335 | * 获取当前使能数据失败重试次数 336 | * 337 | * @return 338 | */ 339 | public int getReceiveDataRetryCount() { 340 | if (lastDeviceMirror == null) { 341 | return 0; 342 | } 343 | return lastDeviceMirror.getReceiveDataRetryCount(); 344 | } 345 | 346 | /** 347 | * 获取当前写入数据失败重试次数 348 | * 349 | * @return 350 | */ 351 | public int getWriteDataRetryCount() { 352 | if (lastDeviceMirror == null) { 353 | return 0; 354 | } 355 | return lastDeviceMirror.getWriteDataRetryCount(); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /ble.js: -------------------------------------------------------------------------------- 1 | const _oble = uni.requireNativePlugin('sand-plugin-bluetooth'); 2 | 3 | class BLE { 4 | //返回数据正常 5 | static STATUS_OK="2500"; 6 | 7 | //订阅蓝牙可用性变更 dicovering属性好像没用 8 | static onBluetoothAdapterStateChange(callback) { 9 | _oble.onBluetoothAdapterStateChange({},(res)=>{ 10 | if(res.status==BLE.STATUS_OK){ 11 | callback && callback(res); 12 | } 13 | }); 14 | } 15 | 16 | static onBluetoothDeviceFound(callback) { 17 | _oble.onBluetoothDeviceFound({},(res)=>{ 18 | if(res.status==BLE.STATUS_OK){ 19 | if(res.devices) { 20 | res.devices = JSON.parse(res.devices); 21 | } 22 | callback && callback(res); 23 | } 24 | }); 25 | } 26 | 27 | static onBLEConnectionStateChange(callback) { 28 | _oble.onBLEConnectionStateChange({},(res)=>{ 29 | if(res.status==BLE.STATUS_OK){ 30 | callback && callback(res); 31 | } 32 | }); 33 | } 34 | 35 | static onBLECharacteristicValueChange(callback) { 36 | _oble.onBLECharacteristicValueChange({},(res)=>{ 37 | if(res.status==BLE.STATUS_OK){ 38 | callback && callback(res); 39 | } 40 | }); 41 | } 42 | 43 | static startBluetoothDevicesDiscovery(options) { 44 | _oble.startBluetoothDevicesDiscovery({},(res)=>{ 45 | if(res.status==BLE.STATUS_OK) { 46 | if(options && options.success) { 47 | options.success(res); 48 | } 49 | } 50 | if(options && options.complete) { 51 | options.complete(); 52 | } 53 | }); 54 | } 55 | 56 | static stopBluetoothDevicesDiscovery(options) { 57 | _oble.stopBluetoothDevicesDiscovery({},(res)=>{ 58 | if(res.status==BLE.STATUS_OK) { 59 | if(options && options.success) { 60 | options.success(res); 61 | } 62 | } 63 | if(options && options.complete) { 64 | options.complete(); 65 | } 66 | }); 67 | } 68 | 69 | static closeBLEConnection(options) { 70 | _oble.closeBLEConnection({ 71 | deviceId : options.deviceId, 72 | },(res)=>{ 73 | if(res.status==BLE.STATUS_OK) { 74 | if(options && options.success) { 75 | options.success(res); 76 | } 77 | } 78 | if(options && options.complete) { 79 | options.complete(); 80 | } 81 | }); 82 | } 83 | 84 | static createBLEConnection(options) { 85 | _oble.createBLEConnection({ 86 | deviceId : options.deviceId, 87 | mtu : options.mtu, 88 | },(res)=>{ 89 | res.code = res.status; 90 | if(res.status==BLE.STATUS_OK) { 91 | if(options && options.success) { 92 | options.success(res); 93 | } 94 | } else { 95 | if(options && options.fail) { 96 | options.fail(res); 97 | } 98 | } 99 | if(options && options.complete) { 100 | options.complete(); 101 | } 102 | }); 103 | } 104 | 105 | static addAutoReconnect(options) { 106 | _oble.addAutoReconnect({ 107 | deviceId : options.deviceId, 108 | },(res)=>{ 109 | res.code = res.status; 110 | if(res.status==BLE.STATUS_OK) { 111 | if(options && options.success) { 112 | options.success(res); 113 | } 114 | } else { 115 | if(options && options.fail) { 116 | options.fail(res); 117 | } 118 | } 119 | if(options && options.complete) { 120 | options.complete(); 121 | } 122 | }); 123 | } 124 | 125 | static removeAutoReconnect(options) { 126 | _oble.removeAutoReconnect({ 127 | deviceId : options.deviceId 128 | },(res)=>{ 129 | res.code = res.status; 130 | if(res.status==BLE.STATUS_OK) { 131 | if(options && options.success) { 132 | options.success(res); 133 | } 134 | } else { 135 | if(options && options.fail) { 136 | options.fail(res); 137 | } 138 | } 139 | if(options && options.complete) { 140 | options.complete(); 141 | } 142 | }); 143 | } 144 | 145 | static getBLEDeviceServices(options) { 146 | _oble.getBLEDeviceServices({ 147 | deviceId : options.deviceId 148 | },(res)=>{ 149 | res.code = res.status; 150 | if(res.status==BLE.STATUS_OK) { 151 | res.services = JSON.parse(res.services); 152 | if(options && options.success) { 153 | options.success(res); 154 | } 155 | } else { 156 | if(options && options.fail) { 157 | options.fail(res); 158 | } 159 | } 160 | if(options && options.complete) { 161 | options.complete(); 162 | } 163 | }); 164 | } 165 | 166 | static getBLEDeviceCharacteristics(options) { 167 | _oble.getBLEDeviceCharacteristics({ 168 | deviceId : options.deviceId, 169 | serviceId : options.serviceId 170 | },(res)=>{ 171 | res.code = res.status; 172 | if(res.status==BLE.STATUS_OK) { 173 | res.characteristics = JSON.parse(res.characteristics); 174 | if(options && options.success) { 175 | options.success(res); 176 | } 177 | } else { 178 | if(options && options.fail) { 179 | options.fail(res); 180 | } 181 | } 182 | if(options && options.complete) { 183 | options.complete(); 184 | } 185 | }); 186 | } 187 | 188 | static getBLEDeviceRSSI(options) { 189 | _oble.getBLEDeviceRSSI({ 190 | deviceId : options.deviceId 191 | },(res)=>{ 192 | res.code = res.status; 193 | if(res.status==BLE.STATUS_OK) { 194 | if(options && options.success) { 195 | options.success(res); 196 | } 197 | } else { 198 | if(options && options.fail) { 199 | options.fail(res); 200 | } 201 | } 202 | if(options && options.complete) { 203 | options.complete(); 204 | } 205 | }); 206 | } 207 | 208 | static notifyBLECharacteristicValueChange(options) { 209 | _oble.notifyBLECharacteristicValueChange({ 210 | deviceId : options.deviceId, 211 | serviceId : options.serviceId, 212 | characteristicId : options.characteristicId 213 | },(res)=>{ 214 | res.code = res.status; 215 | if(res.status==BLE.STATUS_OK) { 216 | if(options && options.success) { 217 | options.success(res); 218 | } 219 | } else { 220 | if(options && options.fail) { 221 | options.fail(res); 222 | } 223 | } 224 | if(options && options.complete) { 225 | options.complete(); 226 | } 227 | }); 228 | } 229 | 230 | static cancelNotifyBLECharacteristicValueChange(options) { 231 | _oble.cancelNotifyBLECharacteristicValueChange({ 232 | deviceId : options.deviceId, 233 | serviceId : options.serviceId, 234 | characteristicId : options.characteristicId 235 | },(res)=>{ 236 | res.code = res.status; 237 | if(res.status==BLE.STATUS_OK) { 238 | if(options && options.success) { 239 | options.success(res); 240 | } 241 | } else { 242 | if(options && options.fail) { 243 | options.fail(res); 244 | } 245 | } 246 | if(options && options.complete) { 247 | options.complete(); 248 | } 249 | }); 250 | } 251 | 252 | static writeBLECharacteristicValue(options) { 253 | _oble.writeBLECharacteristicValue({ 254 | deviceId : options.deviceId, 255 | serviceId : options.serviceId, 256 | characteristicId : options.characteristicId, 257 | value : options.value 258 | },(res)=>{ 259 | res.code = res.status; 260 | if(res.status==BLE.STATUS_OK) { 261 | if(options && options.success) { 262 | options.success(res); 263 | } 264 | } else { 265 | if(options && options.fail) { 266 | options.fail(res); 267 | } 268 | } 269 | if(options && options.complete) { 270 | options.complete(); 271 | } 272 | }); 273 | } 274 | 275 | static readBLECharacteristicValue(options) { 276 | _oble.readBLECharacteristicValue({ 277 | deviceId : options.deviceId, 278 | serviceId : options.serviceId, 279 | characteristicId : options.characteristicId 280 | },(res)=>{ 281 | res.code = res.status; 282 | if(res.status==BLE.STATUS_OK) { 283 | if(options && options.success) { 284 | options.success(res); 285 | } 286 | } else { 287 | if(options && options.fail) { 288 | options.fail(res); 289 | } 290 | } 291 | if(options && options.complete) { 292 | options.complete(); 293 | } 294 | }); 295 | } 296 | 297 | static getBluetoothDevices(options) { 298 | _oble.getBluetoothDevices({},(res)=>{ 299 | res.code = res.status; 300 | if(res.status==BLE.STATUS_OK) { 301 | if(res.devices) { 302 | res.devices = JSON.parse(res.devices); 303 | } 304 | if(options && options.success) { 305 | options.success(res); 306 | } 307 | } else { 308 | if(options && options.fail) { 309 | options.fail(res); 310 | } 311 | } 312 | if(options && options.complete) { 313 | options.complete(); 314 | } 315 | }); 316 | } 317 | 318 | static getBluetoothAdapterState(options) { 319 | _oble.getBluetoothAdapterState({},(res)=>{ 320 | res.code = res.status; 321 | if(res.status==BLE.STATUS_OK) { 322 | if(options && options.success) { 323 | options.success(res); 324 | } 325 | } else { 326 | if(options && options.fail) { 327 | options.fail(res); 328 | } 329 | } 330 | if(options && options.complete) { 331 | options.complete(); 332 | } 333 | }); 334 | } 335 | 336 | static openBluetoothAdapter(options) { 337 | _oble.openBluetoothAdapter({},(res)=>{ 338 | res.code = res.status; 339 | if(res.status==BLE.STATUS_OK) { 340 | if(options && options.success) { 341 | options.success(res); 342 | } 343 | } else { 344 | if(options && options.fail) { 345 | options.fail(res); 346 | } 347 | } 348 | if(options && options.complete) { 349 | options.complete(); 350 | } 351 | }); 352 | } 353 | 354 | static closeBluetoothAdapter(options) { 355 | _oble.closeBluetoothAdapter({},(res)=>{ 356 | res.code = res.status; 357 | if(res.status==BLE.STATUS_OK) { 358 | if(options && options.success) { 359 | options.success(res); 360 | } 361 | } else { 362 | if(options && options.fail) { 363 | options.fail(res); 364 | } 365 | } 366 | if(options && options.complete) { 367 | options.complete(); 368 | } 369 | }); 370 | } 371 | } 372 | 373 | export default BLE; -------------------------------------------------------------------------------- /android/uniplugin_module/src/main/java/com/vise/baseble/model/BluetoothLeDevice.java: -------------------------------------------------------------------------------- 1 | package com.vise.baseble.model; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | import android.os.Bundle; 5 | import android.os.Parcel; 6 | import android.os.Parcelable; 7 | 8 | import com.vise.baseble.common.BluetoothServiceType; 9 | import com.vise.baseble.model.adrecord.AdRecordStore; 10 | import com.vise.baseble.model.resolver.BluetoothClassResolver; 11 | import com.vise.baseble.utils.AdRecordUtil; 12 | import com.vise.baseble.utils.HexUtil; 13 | 14 | import java.io.Serializable; 15 | import java.util.Arrays; 16 | import java.util.Collections; 17 | import java.util.HashSet; 18 | import java.util.LinkedHashMap; 19 | import java.util.Map; 20 | import java.util.Set; 21 | 22 | /** 23 | * @Description: 设备信息 24 | * @author: DAWI 25 | * @date: 16/8/5 20:44. 26 | */ 27 | public class BluetoothLeDevice implements Parcelable { 28 | 29 | /** 30 | * The Constant CREATOR. 31 | */ 32 | public static final Creator CREATOR = new Creator() { 33 | public BluetoothLeDevice createFromParcel(final Parcel in) { 34 | return new BluetoothLeDevice(in); 35 | } 36 | 37 | public BluetoothLeDevice[] newArray(final int size) { 38 | return new BluetoothLeDevice[size]; 39 | } 40 | }; 41 | protected static final int MAX_RSSI_LOG_SIZE = 10; 42 | private static final String PARCEL_EXTRA_BLUETOOTH_DEVICE = "bluetooth_device"; 43 | private static final String PARCEL_EXTRA_CURRENT_RSSI = "current_rssi"; 44 | private static final String PARCEL_EXTRA_CURRENT_TIMESTAMP = "current_timestamp"; 45 | private static final String PARCEL_EXTRA_DEVICE_RSSI_LOG = "device_rssi_log"; 46 | private static final String PARCEL_EXTRA_DEVICE_SCANRECORD = "device_scanrecord"; 47 | private static final String PARCEL_EXTRA_DEVICE_SCANRECORD_STORE = "device_scanrecord_store"; 48 | private static final String PARCEL_EXTRA_FIRST_RSSI = "device_first_rssi"; 49 | private static final String PARCEL_EXTRA_FIRST_TIMESTAMP = "first_timestamp"; 50 | private static final long LOG_INVALIDATION_THRESHOLD = 10 * 1000; 51 | private final AdRecordStore mRecordStore; 52 | private final BluetoothDevice mDevice; 53 | private final Map mRssiLog; 54 | private final byte[] mScanRecord; 55 | private final int mFirstRssi; 56 | private final long mFirstTimestamp; 57 | private int mCurrentRssi; 58 | private long mCurrentTimestamp; 59 | private transient Set mServiceSet; 60 | private boolean autoReconnect=false; 61 | private int mtu = 247; 62 | 63 | /** 64 | * Instantiates a new Bluetooth LE device. 65 | * 66 | * @param device a standard android Bluetooth device 67 | * @param rssi the RSSI value of the Bluetooth device 68 | * @param scanRecord the scan record of the device 69 | * @param timestamp the timestamp of the RSSI reading 70 | */ 71 | public BluetoothLeDevice(final BluetoothDevice device, final int rssi, final byte[] scanRecord, final long timestamp) { 72 | mDevice = device; 73 | mFirstRssi = rssi; 74 | mFirstTimestamp = timestamp; 75 | mRecordStore = new AdRecordStore(AdRecordUtil.parseScanRecordAsSparseArray(scanRecord)); 76 | mScanRecord = scanRecord; 77 | mRssiLog = new LinkedHashMap<>(MAX_RSSI_LOG_SIZE); 78 | updateRssiReading(timestamp, rssi); 79 | } 80 | 81 | /** 82 | * Instantiates a new Bluetooth LE device. 83 | * 84 | * @param device the device 85 | */ 86 | public BluetoothLeDevice(final BluetoothLeDevice device) { 87 | mCurrentRssi = device.getRssi(); 88 | mCurrentTimestamp = device.getTimestamp(); 89 | mDevice = device.getDevice(); 90 | mFirstRssi = device.getFirstRssi(); 91 | mFirstTimestamp = device.getFirstTimestamp(); 92 | mRecordStore = new AdRecordStore(AdRecordUtil.parseScanRecordAsSparseArray(device.getScanRecord())); 93 | mRssiLog = device.getRssiLog(); 94 | mScanRecord = device.getScanRecord(); 95 | } 96 | 97 | /** 98 | * Instantiates a new bluetooth le device. 99 | * 100 | * @param in the in 101 | */ 102 | protected BluetoothLeDevice(final Parcel in) { 103 | final Bundle b = in.readBundle(getClass().getClassLoader()); 104 | 105 | mCurrentRssi = b.getInt(PARCEL_EXTRA_CURRENT_RSSI, 0); 106 | mCurrentTimestamp = b.getLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, 0); 107 | mDevice = b.getParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE); 108 | mFirstRssi = b.getInt(PARCEL_EXTRA_FIRST_RSSI, 0); 109 | mFirstTimestamp = b.getLong(PARCEL_EXTRA_FIRST_TIMESTAMP, 0); 110 | mRecordStore = b.getParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE); 111 | mRssiLog = (Map) b.getSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG); 112 | mScanRecord = b.getByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD); 113 | } 114 | 115 | /** 116 | * Adds the to rssi log. 117 | * 118 | * @param timestamp the timestamp 119 | * @param rssiReading the rssi reading 120 | */ 121 | private void addToRssiLog(final long timestamp, final int rssiReading) { 122 | synchronized (mRssiLog) { 123 | if (timestamp - mCurrentTimestamp > LOG_INVALIDATION_THRESHOLD) { 124 | mRssiLog.clear(); 125 | } 126 | 127 | mCurrentRssi = rssiReading; 128 | mCurrentTimestamp = timestamp; 129 | mRssiLog.put(timestamp, rssiReading); 130 | } 131 | } 132 | 133 | /* (non-Javadoc) 134 | * @see android.os.Parcelable#describeContents() 135 | */ 136 | @Override 137 | public int describeContents() { 138 | return 0; 139 | } 140 | 141 | /* (non-Javadoc) 142 | * @see java.lang.Object#equals(java.lang.Object) 143 | */ 144 | @Override 145 | public boolean equals(final Object obj) { 146 | if (this == obj) return true; 147 | if (obj == null) return false; 148 | if (getClass() != obj.getClass()) return false; 149 | final BluetoothLeDevice other = (BluetoothLeDevice) obj; 150 | if (mCurrentRssi != other.mCurrentRssi) return false; 151 | if (mCurrentTimestamp != other.mCurrentTimestamp) return false; 152 | if (mDevice == null) { 153 | if (other.mDevice != null) return false; 154 | } else if (!mDevice.equals(other.mDevice)) return false; 155 | if (mFirstRssi != other.mFirstRssi) return false; 156 | if (mFirstTimestamp != other.mFirstTimestamp) return false; 157 | if (mRecordStore == null) { 158 | if (other.mRecordStore != null) return false; 159 | } else if (!mRecordStore.equals(other.mRecordStore)) return false; 160 | if (mRssiLog == null) { 161 | if (other.mRssiLog != null) return false; 162 | } else if (!mRssiLog.equals(other.mRssiLog)) return false; 163 | if (Arrays.equals(mScanRecord, other.mScanRecord)) { 164 | return true; 165 | } 166 | return false; 167 | } 168 | 169 | /** 170 | * Gets the ad record store. 171 | * 172 | * @return the ad record store 173 | */ 174 | public AdRecordStore getAdRecordStore() { 175 | return mRecordStore; 176 | } 177 | 178 | /** 179 | * Gets the address. 180 | * 181 | * @return the address 182 | */ 183 | public String getAddress() { 184 | return mDevice.getAddress(); 185 | } 186 | 187 | /** 188 | * Gets the bluetooth device bond state. 189 | * 190 | * @return the bluetooth device bond state 191 | */ 192 | public String getBluetoothDeviceBondState() { 193 | return resolveBondingState(mDevice.getBondState()); 194 | } 195 | 196 | /** 197 | * Gets the bluetooth device class name. 198 | * 199 | * @return the bluetooth device class name 200 | */ 201 | public String getBluetoothDeviceClassName() { 202 | return BluetoothClassResolver.resolveDeviceClass(mDevice.getBluetoothClass().getDeviceClass()); 203 | } 204 | 205 | public Set getBluetoothDeviceKnownSupportedServices() { 206 | if (mServiceSet == null) { 207 | synchronized (this) { 208 | if (mServiceSet == null) { 209 | final Set serviceSet = new HashSet<>(); 210 | for (final BluetoothServiceType service : BluetoothServiceType.values()) { 211 | 212 | if (mDevice.getBluetoothClass().hasService(service.getCode())) { 213 | serviceSet.add(service); 214 | } 215 | } 216 | mServiceSet = Collections.unmodifiableSet(serviceSet); 217 | } 218 | } 219 | } 220 | 221 | return mServiceSet; 222 | } 223 | 224 | /** 225 | * Gets the bluetooth device major class name. 226 | * 227 | * @return the bluetooth device major class name 228 | */ 229 | public String getBluetoothDeviceMajorClassName() { 230 | return BluetoothClassResolver.resolveMajorDeviceClass(mDevice.getBluetoothClass().getMajorDeviceClass()); 231 | } 232 | 233 | /** 234 | * Gets the device. 235 | * 236 | * @return the device 237 | */ 238 | public BluetoothDevice getDevice() { 239 | return mDevice; 240 | } 241 | 242 | /** 243 | * Gets the first rssi. 244 | * 245 | * @return the first rssi 246 | */ 247 | public int getFirstRssi() { 248 | return mFirstRssi; 249 | } 250 | 251 | /** 252 | * Gets the first timestamp. 253 | * 254 | * @return the first timestamp 255 | */ 256 | public long getFirstTimestamp() { 257 | return mFirstTimestamp; 258 | } 259 | 260 | /** 261 | * Gets the name. 262 | * 263 | * @return the name 264 | */ 265 | public String getName() { 266 | return mDevice.getName(); 267 | } 268 | 269 | /** 270 | * Gets the rssi. 271 | * 272 | * @return the rssi 273 | */ 274 | public int getRssi() { 275 | return mCurrentRssi; 276 | } 277 | 278 | /** 279 | * Gets the rssi log. 280 | * 281 | * @return the rssi log 282 | */ 283 | protected Map getRssiLog() { 284 | synchronized (mRssiLog) { 285 | return mRssiLog; 286 | } 287 | } 288 | 289 | /** 290 | * Gets the running average rssi. 291 | * 292 | * @return the running average rssi 293 | */ 294 | public double getRunningAverageRssi() { 295 | int sum = 0; 296 | int count = 0; 297 | 298 | synchronized (mRssiLog) { 299 | 300 | for (final Long aLong : mRssiLog.keySet()) { 301 | count++; 302 | sum += mRssiLog.get(aLong); 303 | } 304 | } 305 | 306 | if (count > 0) { 307 | return sum / count; 308 | } else { 309 | return 0; 310 | } 311 | 312 | } 313 | 314 | /** 315 | * 获取major 316 | * @return major 317 | */ 318 | public long getMajor(){ 319 | return HexUtil.byteToLong(HexUtil.subBytes(mScanRecord,25,2),0,2,true); 320 | } 321 | 322 | /** 323 | * 获取minor 324 | * @return minor 325 | */ 326 | public long minor(){ 327 | return HexUtil.byteToLong(HexUtil.subBytes(mScanRecord,27,2),0,2,true); 328 | } 329 | 330 | /** 331 | * Gets the scan record. 332 | * 333 | * @return the scan record 334 | */ 335 | public byte[] getScanRecord() { 336 | return mScanRecord; 337 | } 338 | 339 | /** 340 | * Gets the timestamp. 341 | * 342 | * @return the timestamp 343 | */ 344 | public long getTimestamp() { 345 | return mCurrentTimestamp; 346 | } 347 | 348 | /* (non-Javadoc) 349 | * @see java.lang.Object#hashCode() 350 | */ 351 | @Override 352 | public int hashCode() { 353 | final int prime = 31; 354 | int result = 1; 355 | result = prime * result + mCurrentRssi; 356 | result = prime * result + (int) (mCurrentTimestamp ^ (mCurrentTimestamp >>> 32)); 357 | result = prime * result + ((mDevice == null) ? 0 : mDevice.hashCode()); 358 | result = prime * result + mFirstRssi; 359 | result = prime * result + (int) (mFirstTimestamp ^ (mFirstTimestamp >>> 32)); 360 | result = prime * result + ((mRecordStore == null) ? 0 : mRecordStore.hashCode()); 361 | result = prime * result + ((mRssiLog == null) ? 0 : mRssiLog.hashCode()); 362 | result = prime * result + Arrays.hashCode(mScanRecord); 363 | return result; 364 | } 365 | 366 | /* (non-Javadoc) 367 | * @see java.lang.Object#toString() 368 | */ 369 | @Override 370 | public String toString() { 371 | return "BluetoothLeDevice [mDevice=" + mDevice + ", " + 372 | "mRssi=" + mFirstRssi + ", mScanRecord=" + HexUtil.encodeHexStr(mScanRecord) + 373 | ", mRecordStore=" + mRecordStore + ", getBluetoothDeviceBondState()=" + 374 | getBluetoothDeviceBondState() + ", getBluetoothDeviceClassName()=" + 375 | getBluetoothDeviceClassName() + "]"; 376 | } 377 | 378 | /** 379 | * Update rssi reading. 380 | * 381 | * @param timestamp the timestamp 382 | * @param rssiReading the rssi reading 383 | */ 384 | public void updateRssiReading(final long timestamp, final int rssiReading) { 385 | addToRssiLog(timestamp, rssiReading); 386 | } 387 | 388 | /* (non-Javadoc) 389 | * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int) 390 | */ 391 | @Override 392 | public void writeToParcel(final Parcel parcel, final int arg1) { 393 | final Bundle b = new Bundle(getClass().getClassLoader()); 394 | 395 | b.putByteArray(PARCEL_EXTRA_DEVICE_SCANRECORD, mScanRecord); 396 | 397 | b.putInt(PARCEL_EXTRA_FIRST_RSSI, mFirstRssi); 398 | b.putInt(PARCEL_EXTRA_CURRENT_RSSI, mCurrentRssi); 399 | 400 | b.putLong(PARCEL_EXTRA_FIRST_TIMESTAMP, mFirstTimestamp); 401 | b.putLong(PARCEL_EXTRA_CURRENT_TIMESTAMP, mCurrentTimestamp); 402 | 403 | b.putParcelable(PARCEL_EXTRA_BLUETOOTH_DEVICE, mDevice); 404 | b.putParcelable(PARCEL_EXTRA_DEVICE_SCANRECORD_STORE, mRecordStore); 405 | b.putSerializable(PARCEL_EXTRA_DEVICE_RSSI_LOG, (Serializable) mRssiLog); 406 | 407 | parcel.writeBundle(b); 408 | } 409 | 410 | /** 411 | * Resolve bonding state. 412 | * 413 | * @param bondState the bond state 414 | * @return the string 415 | */ 416 | private static String resolveBondingState(final int bondState) { 417 | switch (bondState) { 418 | case BluetoothDevice.BOND_BONDED://已配对 419 | return "Paired"; 420 | case BluetoothDevice.BOND_BONDING://配对中 421 | return "Pairing"; 422 | case BluetoothDevice.BOND_NONE://未配对 423 | return "UnBonded"; 424 | default: 425 | return "Unknown";//未知状态 426 | } 427 | } 428 | 429 | public boolean isAutoReconnect() { 430 | return autoReconnect; 431 | } 432 | 433 | public void setAutoReconnect(boolean autoReconnect) { 434 | this.autoReconnect = autoReconnect; 435 | } 436 | 437 | public void setMTU(int mtu) { 438 | this.mtu = mtu; 439 | } 440 | 441 | public int getMTU() { 442 | return this.mtu; 443 | } 444 | } 445 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uniapp-sand-plugin-bluetooth 2 | uniapp 低功耗蓝牙API原生插件
3 | 4 | 本项目Fork自 https://gitee.com/wangqianjiao/sand-plugin-bluetooth
5 | 6 | 由于此项目作者太久没更新了 不适用于现在MTU比较大的情况 (作者只是在发送数据时限制了数据大小)。
7 | 而且API格式也与uniapp的方式有些差异 所以有需要的人可自行引入 ble.js
8 | 注意 IOS不支持MTU更改 需要在硬件端做修改 手机会自协商
9 | 安卓端默认mtu现改为247 建立连接createBLEConnection时可传入mtu参数 在连接设备且发现services后自动更改mtu
10 | 11 |
 12 | BLE.createBLEConnection({
 13 |     deviceId : deviceId,
 14 |     mtu : mtu,
 15 |     success(res) {
 16 | 
 17 |     }
 18 | });
 19 | 
20 | 21 | ### API目录 22 | 23 | #### openBluetoothAdapter(options) 24 | 初始化蓝牙模块 25 | 26 | options 参数说明 27 | 28 | | 属性 | 类型 |是否必填| 说明 | 29 | | --- | --- | --- |---| 30 | 31 | callback 回调函数参数对象说明 32 | 33 | | 属性 | 类型 | 说明 | 34 | | --- | --- | --- | 35 | | code | String | 接口调用状态 | 36 | | message | String |状态说明| 37 | 38 | 示例代码 39 | 40 | ```javascript 41 | BLE.openBluetoothAdapter({ 42 | success(res){ 43 | 44 | } 45 | }) 46 | ``` 47 | 48 | 注意: 49 | 50 | - 大部分操作类API(监听类API除外)都需要在openBluetoothAdapter之后进行调用,否则会失败,status返回2501-蓝牙未初始化或未开启 51 | - 可通过 onBluetoothAdapterStateChange 监听手机蓝牙状态的改变 52 | 53 | --- 54 | #### startBluetoothDevicesDiscovery(options) 55 | 开始搜寻附近的蓝牙外围设备。此操作比较耗费系统资源,请在搜索并连接到设备后调用 stopBluetoothDevicesDiscovery 方法停止搜索。 56 | 57 | options 参数说明 58 | 59 | | 属性 | 类型 |是否必填| 说明 | 60 | | --- | --- | --- |---| 61 | 62 | callback 回调函数参数对象说明 63 | 64 | | 属性 | 类型 | 说明 | 65 | | --- | --- | --- | 66 | | code | String | 接口调用状态 | 67 | | message | String |状态说明| 68 | 69 | 示例代码 70 | 71 | ```javascript 72 | BLE.startBluetoothDevicesDiscovery({ 73 | success(res)=>{ 74 | 75 | } 76 | }) 77 | ``` 78 | 79 | 注意:开启后获取扫描到的设备需要使用 onBluetoothDeviceFound 进行异步获取,建议先进行监听再扫描 80 | 81 | --- 82 | #### onBluetoothDeviceFound(callback) 83 | 监听寻找到新设备的事件 84 | 85 | options 参数说明 86 | 87 | | 属性 | 类型 |是否必填| 说明 | 88 | | --- | --- | --- |---| 89 | 90 | callback 回调函数参数对象说明 91 | 92 | | 属性 | 类型 | 说明 | 93 | | --- | --- | --- | 94 | | code | String | 接口调用状态 | 95 | | message | String |状态说明| 96 | |devices|Array|设备信息JSON格式数组| 97 | 98 | devices的结构 99 | 100 | | 属性 | 类型 | 说明 | 101 | | --- | --- | --- | 102 | |name| string| 蓝牙设备名称,某些设备可能没有| 103 | |deviceId| string| 用于区分设备的 id| 104 | |RSSI| number |当前蓝牙设备的信号强度| 105 | |advertisData| string |当前蓝牙设备的广播数据段中的 ManufacturerData 数据段(十六进制字符串,每2个字符对应一个字节)| 106 | |advertisServiceUUIDs| Array| 当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段| 107 | |localName| string| 当前蓝牙设备的广播数据段中的 LocalName 数据段| 108 | 109 | 示例代码 110 | 111 | ```javascript 112 | BLE.onBluetoothDeviceFound(function(res){ 113 | let devices = res.devices; 114 | console.log(res.devices); 115 | }) 116 | ``` 117 | 注意: 118 | 119 | - 受限于uni-app原生插件对返回类型的要求,第二层数据只能采用String类型,需要使用JSON.parse方式进行对象解析 120 | - 监听在插件中只会保存一份,即最后一次调用 onBluetoothDeviceFound 的callback会触发回调事件 121 | 122 | --- 123 | #### stopBluetoothDevicesDiscovery(options) 124 | 停止搜寻附近的蓝牙外围设备。若已经找到需要的蓝牙设备并不需要继续搜索时,建议调用该接口停止蓝牙搜索。 125 | 126 | options 参数说明 127 | 128 | | 属性 | 类型 |是否必填| 说明 | 129 | | --- | --- | --- |---| 130 | 131 | callback 回调函数参数对象说明 132 | 133 | | 属性 | 类型 | 说明 | 134 | | --- | --- | --- | 135 | | code | String | 接口调用状态 | 136 | | message | String |状态说明| 137 | 138 | 示例代码 139 | 140 | ```javascript 141 | BLE.stopBluetoothDevicesDiscovery({ 142 | success(res){ 143 | 144 | } 145 | }) 146 | ``` 147 | 148 | --- 149 | 150 | #### onBluetoothAdapterStateChange(callback) 151 | 监听蓝牙适配器状态变化事件 152 | 153 | options 参数说明 154 | 155 | | 属性 | 类型 |是否必填| 说明 | 156 | | --- | --- | --- |---| 157 | 158 | callback 回调函数参数对象说明 159 | 160 | | 属性 | 类型 | 说明 | 161 | | --- | --- | --- | 162 | | status | String | 接口调用状态 | 163 | | message | String |状态说明| 164 | |available|boolean|蓝牙是否可用| 165 | 166 | 示例代码 167 | 168 | ```javascript 169 | BLE.onBluetoothAdapterStateChange(function(res){ 170 | 171 | }) 172 | ``` 173 | 174 | --- 175 | #### getBluetoothDevices(options) 176 | 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。 177 | 178 | options 参数说明 179 | 180 | | 属性 | 类型 |是否必填| 说明 | 181 | | --- | --- | --- |---| 182 | 183 | callback 回调函数参数对象说明 184 | 185 | | 属性 | 类型 | 说明 | 186 | | --- | --- | --- | 187 | | status | String | 接口调用状态 | 188 | | message | String |状态说明| 189 | |devices|Array|设备信息JSON格式数组| 190 | 191 | devices的结构 192 | 193 | | 属性 | 类型 | 说明 | 194 | | --- | --- | --- | 195 | |name| string| 蓝牙设备名称,某些设备可能没有| 196 | |deviceId| string| 用于区分设备的 id| 197 | |restore|boolean|是否是可以后台恢复的设备,true-可直接使用createBLEConnection进行恢复连接动作| 198 | |RSSI| number |当前蓝牙设备的信号强度| 199 | |advertisData| string |当前蓝牙设备的广播数据段中的 ManufacturerData 数据段(十六进制字符串,每2个字符对应一个字节)| 200 | |advertisServiceUUIDs| Array| 当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段| 201 | |localName| string| 当前蓝牙设备的广播数据段中的 LocalName 数据段| 202 | 203 | 示例代码 204 | 205 | ```javascript 206 | BLE.getBluetoothDevices({ 207 | success(res)=>{ 208 | //发现新设备 209 | let devices = res.devices; 210 | console.log(res.devices); 211 | } 212 | }) 213 | ``` 214 | --- 215 | #### getBluetoothAdapterState(options) 216 | 获取本机蓝牙适配器状态。 217 | 218 | options 参数说明 219 | 220 | | 属性 | 类型 |是否必填| 说明 | 221 | | --- | --- | --- |---| 222 | 223 | callback 回调函数参数对象说明 224 | 225 | | 属性 | 类型 | 说明 | 226 | | --- | --- | --- | 227 | | code | String | 接口调用状态 | 228 | | message | String |状态说明| 229 | |available|boolean|蓝牙是否可用| 230 | 231 | 示例代码 232 | 233 | ```javascript 234 | BLE.getBluetoothAdapterState({ 235 | success(res){ 236 | 237 | } 238 | }) 239 | ``` 240 | --- 241 | #### closeBluetoothAdapter(options) 242 | 关闭蓝牙模块。调用该方法将断开所有已建立的连接并释放系统资源。建议在使用蓝牙流程后,与 openBluetoothAdapter 成对调用。 243 | 244 | options 参数说明 245 | 246 | | 属性 | 类型 |是否必填| 说明 | 247 | | --- | --- | --- |---| 248 | 249 | callback 回调函数参数对象说明 250 | 251 | | 属性 | 类型 | 说明 | 252 | | --- | --- | --- | 253 | | code | String | 接口调用状态 | 254 | | message | String |状态说明| 255 | 256 | 示例代码 257 | 258 | ```javascript 259 | BLE.closeBluetoothAdapter({ 260 | success(res){ 261 | 262 | } 263 | }) 264 | ``` 265 | --- 266 | #### writeBLECharacteristicValue(options) 267 | 向低功耗蓝牙设备特征值中写入二进制数据。注意:必须设备的特征值支持 write 才可以成功调用。 268 | 269 | options 参数说明 270 | 271 | | 属性 | 类型 |是否必填| 说明 | 272 | | --- | --- | --- |---| 273 | |deviceId| string | 是 |蓝牙设备 id| 274 | |serviceId| string| 是 |蓝牙特征值对应服务的 uuid| 275 | |characteristicId| string | 是 |蓝牙特征值的 uuid| 276 | |value |string | 是| 蓝牙设备特征值二进制字节数组对应转换的十六进制字符串值| 277 | 278 | callback 回调函数参数对象说明 279 | 280 | | 属性 | 类型 | 说明 | 281 | | --- | --- | --- | 282 | | code | String | 接口调用状态 | 283 | | message | String |状态说明| 284 | 285 | 示例代码 286 | 287 | ```javascript 288 | BLE.writeBLECharacteristicValue({ 289 | deviceId:'DDCC-EE-AA-BB-CC', 290 | serviceId:'0000-1902-C503', 291 | characteristicId:'0000-1902-C503-0001', 292 | value:'0010', 293 | success(res){ 294 | 295 | } 296 | }) 297 | ``` 298 | --- 299 | #### readBLECharacteristicValue(options) 300 | 读取低功耗蓝牙设备的特征值的二进制数据值。注意:必须设备的特征值支持 read 才可以成功调用。 301 | 302 | options 参数说明 303 | 304 | | 属性 | 类型 |是否必填| 说明 | 305 | | --- | --- | --- |---| 306 | |deviceId| string | 是 |蓝牙设备 id| 307 | |serviceId| string| 是 |蓝牙特征值对应服务的 uuid| 308 | |characteristicId| string | 是 |蓝牙特征值的 uuid| 309 | 310 | callback 回调函数参数对象说明 311 | 312 | | 属性 | 类型 | 说明 | 313 | | --- | --- | --- | 314 | | code | String | 接口调用状态 | 315 | | message | String |状态说明| 316 | 317 | 示例代码 318 | 319 | ```javascript 320 | // 必须在这里的回调才能获取到值 321 | BLE.onBLECharacteristicValueChange( 322 | function(res){ 323 | console.log(res.deviceId); 324 | console.log(res.serviceId); 325 | console.log(res.characteristicId); 326 | console.log(res.value); 327 | } 328 | ); 329 | 330 | BLE.readBLECharacteristicValue({ 331 | deviceId:'DDCC-EE-AA-BB-CC', 332 | serviceId:'0000-1902-C503', 333 | characteristicId:'0000-1902-C503-0001', 334 | success(res){ 335 | 336 | } 337 | }); 338 | ``` 339 | 注意: 340 | 341 | - 接口读取到的信息需要在 onBLECharacteristicValueChange 方法注册的回调中获取。 342 | 343 | --- 344 | #### onBLEConnectionStateChange(callback) 345 | 监听低功耗蓝牙连接状态的改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等等 346 | 347 | options 参数说明 348 | 349 | | 属性 | 类型 |是否必填| 说明 | 350 | | --- | --- | --- |---| 351 | 352 | callback 回调函数参数对象说明 353 | 354 | | 属性 | 类型 | 说明 | 355 | | --- | --- | --- | 356 | | code | String | 接口调用状态 | 357 | | message | String |状态说明| 358 | |deviceId| string| 蓝牙设备ID| 359 | |connected |boolean| 是否处于已连接状态| 360 | 361 | 示例代码 362 | 363 | ```javascript 364 | ble.onBLEConnectionStateChange(function(res){ 365 | console.log(res.deviceId,'连接状态变化',res.connected); 366 | }) 367 | ``` 368 | --- 369 | #### onBLECharacteristicValueChange(callback) 370 | 监听低功耗蓝牙设备的特征值变化事件。必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。 371 | 372 | options 参数说明 373 | 374 | | 属性 | 类型 |是否必填| 说明 | 375 | | --- | --- | --- |---| 376 | 377 | callback 回调函数参数对象说明 378 | 379 | | 属性 | 类型 | 说明 | 380 | | --- | --- | --- | 381 | | code | String | 接口调用状态 | 382 | | message | String |状态说明| 383 | |deviceId| string |蓝牙设备 id| 384 | |serviceId| string|蓝牙特征值对应服务的 uuid| 385 | |characteristicId | String |蓝牙特征值的 uuid| 386 | |value|String|蓝牙设备特征值二进制字节数组对应转换的十六进制字符串值| 387 | 388 | 示例代码 389 | 390 | ```javascript 391 | ble.onBLECharacteristicValueChange(function(res){ 392 | console.log(res.deviceId); 393 | console.log(res.serviceId); 394 | console.log(res.characteristicId); 395 | console.log(res.value); 396 | }); 397 | ``` 398 | --- 399 | #### notifyBLECharacteristicValueChange(options) 400 | 启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。注意:必须设备的特征值支持 notify 或者 indicate 才可以成功调用。 401 | 402 | options 参数说明 403 | 404 | | 属性 | 类型 |是否必填| 说明 | 405 | | --- | --- | --- |---| 406 | |deviceId| string | 是 |蓝牙设备 id| 407 | |serviceId| string| 是 |蓝牙特征值对应服务的 uuid| 408 | |characteristicId| string | 是 |蓝牙特征值的 uuid| 409 | 410 | callback 回调函数参数对象说明 411 | 412 | | 属性 | 类型 | 说明 | 413 | | --- | --- | --- | 414 | | code | String | 接口调用状态 | 415 | | message | String |状态说明| 416 | 417 | 示例代码 418 | 419 | ```javascript 420 | // 必须在这里的回调才能获取到值 421 | BLE.onBLECharacteristicValueChange(function(res){ 422 | console.log(res.deviceId); 423 | console.log(res.serviceId); 424 | console.log(res.characteristicId); 425 | console.log(res.value); 426 | }); 427 | 428 | BLE.notifyBLECharacteristicValueChange({ 429 | deviceId:'DDCC-EE-AA-BB-CC', 430 | serviceId:'0000-1902-C503', 431 | characteristicId:'0000-1902-C503-0001', 432 | success(res){ 433 | //订阅API调用成功,请在onBLECharacteristicValueChange获取特征值数据 434 | } 435 | }); 436 | ``` 437 | --- 438 | #### cancelNotifyBLECharacteristicValueChange(options) 439 | 取消特征值订阅 440 | 441 | options 参数说明 442 | 443 | | 属性 | 类型 |是否必填| 说明 | 444 | | --- | --- | --- |---| 445 | |deviceId| string | 是 |蓝牙设备 id| 446 | |serviceId| string| 是 |蓝牙特征值对应服务的 uuid| 447 | |characteristicId| string | 是 |蓝牙特征值的 uuid| 448 | 449 | callback 回调函数参数对象说明 450 | 451 | | 属性 | 类型 | 说明 | 452 | | --- | --- | --- | 453 | | code | String | 接口调用状态 | 454 | | message | String |状态说明| 455 | 456 | 示例代码 457 | 458 | ```javascript 459 | ble.cancelNotifyBLECharacteristicValueChange({ 460 | deviceId:'DDCC-EE-AA-BB-CC', 461 | serviceId:'0000-1902-C503', 462 | characteristicId:'0000-1902-C503-0001', 463 | success(res){ 464 | //订阅API调用成功,已取消订阅 465 | } 466 | }); 467 | ``` 468 | --- 469 | #### getBLEDeviceServices(options) 470 | 获取蓝牙设备所有服务(service)。 471 | 472 | options 参数说明 473 | 474 | | 属性 | 类型 |是否必填| 说明 | 475 | | --- | --- | --- |---| 476 | |deviceId| string | 是 |蓝牙设备 id| 477 | 478 | callback 回调函数参数对象说明 479 | 480 | | 属性 | 类型 | 说明 | 481 | | --- | --- | --- | 482 | | code | String | 接口调用状态 | 483 | | message | String |状态说明| 484 | |services|Array|JSON数组| 485 | 486 | services内对象结构 487 | 488 | | 属性 | 类型 | 说明 | 489 | | --- | --- | --- | 490 | | uuid | string| 蓝牙设备服务的 uuid| 491 | | isPrimary | boolean| 该服务是否为主服务| 492 | 493 | 494 | 示例代码 495 | 496 | ```javascript 497 | BLE.getBLEDeviceServices({ 498 | deviceId:'DDCC-EE-AA-BB-CC', 499 | success(res){ 500 | let services = res.services; 501 | console.log(services); 502 | } 503 | }); 504 | ``` 505 | --- 506 | #### getBLEDeviceRSSI(options) 507 | 获取蓝牙设备的实时信号强度。 508 | 509 | options 参数说明 510 | 511 | | 属性 | 类型 |是否必填| 说明 | 512 | | --- | --- | --- |---| 513 | |deviceId| string | 是 |蓝牙设备 id| 514 | 515 | callback 回调函数参数对象说明 516 | 517 | | 属性 | 类型 | 说明 | 518 | | --- | --- | --- | 519 | | code | String | 接口调用状态 | 520 | | message | String |状态说明| 521 | |deviceId| string |蓝牙设备 id| 522 | |RSSI|number|信号强度值| 523 | 524 | 525 | 526 | 示例代码 527 | 528 | ```javascript 529 | ble.getBLEDeviceRSSI({ 530 | deviceId:'DDCC-EE-AA-BB-CC', 531 | success(res)=>{ 532 | console.log(res.RSSI); 533 | } 534 | }); 535 | ``` 536 | --- 537 | #### getBLEDeviceCharacteristics(options) 538 | 获取蓝牙设备某个服务中所有特征值(characteristic)。 539 | 540 | options 参数说明 541 | 542 | | 属性 | 类型 |是否必填| 说明 | 543 | | --- | --- | --- |---| 544 | |deviceId| string | 是 |蓝牙设备 id| 545 | |serviceId| string| 是 |蓝牙特征值对应服务的 uuid| 546 | 547 | callback 回调函数参数对象说明 548 | 549 | | 属性 | 类型 | 说明 | 550 | | --- | --- | --- | 551 | | code | String | 接口调用状态 | 552 | | message | String |状态说明| 553 | |characteristics|Array|JSON数组| 554 | 555 | characteristics内对象结构 556 | 557 | | 属性 | 类型 | 说明 | 558 | | --- | --- | --- | 559 | | uuid | string| 蓝牙设备特征值的 uuid| 560 | | properties| Object| 该特征值支持的操作类型| 561 | 562 | properties 的结构 563 | 564 | | 属性 | 类型 | 说明 | 565 | | --- | --- | --- | 566 | | read | boolean| 该特征值是否支持 read 操作| 567 | | write | boolean | 该特征值是否支持 write 操作| 568 | | notify| boolean| 该特征值是否支持 notify 操作| 569 | | indicate| boolean | 该特征值是否支持 indicate 操作| 570 | 571 | 572 | 示例代码 573 | 574 | ```javascript 575 | ble.getBLEDeviceCharacteristics({ 576 | deviceId:'DDCC-EE-AA-BB-CC', 577 | serviceId:'0000-1902-C503' 578 | success(res){ 579 | let characteristics = res.characteristics; 580 | console.log(characteristics); 581 | } 582 | }); 583 | ``` 584 | 585 | --- 586 | #### createBLEConnection(options) 587 | 连接低功耗蓝牙设备。 588 | 589 | 若APP在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需进行搜索操作。 590 | 591 | 注意: 此处回调成功不表示连接成功 需要监听 onBLEConnectionStateChange 592 | options 参数说明 593 | 594 | | 属性 | 类型 |是否必填| 说明 | 595 | | --- | --- | --- |---| 596 | |deviceId| string | 是 |蓝牙设备 id| 597 | |mtu| integer | 否 | mtu | 598 | 599 | callback 回调函数参数对象说明 600 | 601 | | 属性 | 类型 | 说明 | 602 | | --- | --- | --- | 603 | | code | String | 接口调用状态 | 604 | | message | String |状态说明| 605 | 606 | 607 | 示例代码 608 | 609 | ```javascript 610 | BLE.onBLEConnectionStateChange(function(res){ 611 | console.log(res.deviceId,'连接状态变化',res.connected); 612 | }); 613 | BLE.createBLEConnection({ 614 | deviceId:'DDCC-EE-AA-BB-CC', 615 | success(res){ 616 | //接口调用成功,请在onBLEConnectionStateChange 监听状态变化 617 | } 618 | }); 619 | ``` 620 | --- 621 | #### closeBLEConnection(options) 622 | 断开与低功耗蓝牙设备的连接。 623 | 624 | options 参数说明 625 | 626 | | 属性 | 类型 |是否必填| 说明 | 627 | | --- | --- | --- |---| 628 | |deviceId| string | 是 |蓝牙设备 id| 629 | 630 | callback 回调函数参数对象说明 631 | 632 | | 属性 | 类型 | 说明 | 633 | | --- | --- | --- | 634 | | code | String | 接口调用状态 | 635 | | message | String |状态说明| 636 | 637 | 638 | 示例代码 639 | 640 | ```javascript 641 | BLE.onBLEConnectionStateChange(function(res){ 642 | console.log(res.deviceId,'连接状态变化',res.connected); 643 | }); 644 | BLE.closeBLEConnection({ 645 | deviceId:'DDCC-EE-AA-BB-CC', 646 | success(res){ 647 | //接口调用成功,请在onBLEConnectionStateChange 监听状态变化 648 | } 649 | }); 650 | ``` 651 | --- 652 | #### addAutoReconnect(options) 653 | 将设备加入断线重连队列中,设备断开后将自动进行重连操作 654 | 655 | options 参数说明 656 | 657 | | 属性 | 类型 |是否必填| 说明 | 658 | | --- | --- | --- |---| 659 | |deviceId| string | 是 |蓝牙设备 id| 660 | 661 | callback 回调函数参数对象说明 662 | 663 | | 属性 | 类型 | 说明 | 664 | | --- | --- | --- | 665 | | code | String | 接口调用状态 | 666 | | message | String |状态说明| 667 | 668 | 669 | 示例代码 670 | 671 | ```javascript 672 | BLE.addAutoReconnect({ 673 | deviceId:'DDCC-EE-AA-BB-CC' 674 | success(res){ 675 | //接口调用成功,已成功加入断线重连队列 676 | } 677 | }); 678 | ``` 679 | --- 680 | #### removeAutoReconnect(options) 681 | 将设备从断线重连队列中移除,设备断开后不会自动重连,需要手动调用createBLEConnection重新连接 682 | 683 | options 参数说明 684 | 685 | | 属性 | 类型 |是否必填| 说明 | 686 | | --- | --- | --- |---| 687 | |deviceId| string | 是 |蓝牙设备 id| 688 | 689 | callback 回调函数参数对象说明 690 | 691 | | 属性 | 类型 | 说明 | 692 | | --- | --- | --- | 693 | | code | String | 接口调用状态 | 694 | | message | String |状态说明| 695 | 696 | 697 | 示例代码 698 | 699 | ```javascript 700 | BLE.removeAutoReconnect({ 701 | deviceId:'DDCC-EE-AA-BB-CC', 702 | success(res)=>{ 703 | //接口调用成功,已成功从断线重连队列移除 704 | } 705 | }); 706 | ``` 707 | 708 | ### 状态码status说明 709 | 710 | | status | 说明 | 711 | | --- |--- | 712 | |2500|操作成功| 713 | |2501|蓝牙不可用| 714 | |2502|设备未发现| 715 | |2503|服务或特征等未找到| 716 | |2504|不支持的操作| 717 | |2505|未知系统错误| 718 | 719 | --------------------------------------------------------------------------------