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 Listtrue 传换成小写格式 , 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
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