├── .gitignore
├── LICENSE
├── READAPI.md
├── README.md
├── example
├── antdemo
│ ├── .tea
│ │ └── entryFiles-development
│ │ │ ├── config$.js
│ │ │ ├── importScripts$.js
│ │ │ ├── index$.remote.worker.js
│ │ │ ├── index$.web.js
│ │ │ └── index$.worker.js
│ ├── app.acss
│ ├── app.js
│ ├── app.json
│ ├── images
│ │ └── arrow.png
│ ├── pages
│ │ ├── home
│ │ │ ├── home.acss
│ │ │ ├── home.axml
│ │ │ ├── home.js
│ │ │ └── home.json
│ │ └── index
│ │ │ ├── index.axml
│ │ │ ├── index.js
│ │ │ └── index.json
│ ├── snapshot.png
│ └── wx-ant-ble
│ │ ├── READAPI.md
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package.json
│ │ └── src
│ │ ├── bluetooth.js
│ │ ├── btmanager.js
│ │ ├── enum.js
│ │ ├── extends.js
│ │ ├── promisify.js
│ │ └── tools.js
└── wxdemo
│ ├── app.js
│ ├── app.json
│ ├── app.wxss
│ ├── images
│ └── arrow.png
│ ├── miniprogram_npm
│ └── wx-ant-ble
│ │ ├── index.js
│ │ └── index.js.map
│ ├── package-lock.json
│ ├── package.json
│ ├── pages
│ ├── home
│ │ ├── home.js
│ │ ├── home.json
│ │ ├── home.wxml
│ │ └── home.wxss
│ └── index
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── project.config.json
│ └── utils
│ └── util.js
├── index.js
├── package.json
├── resource
├── QRcode.jpg
└── powerpoint.gif
└── src
├── bluetooth.js
├── btmanager.js
├── enum.js
├── extends.js
├── promisify.js
└── tools.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # next.js build output
61 | .next
62 |
63 | .DS_Store
64 |
65 | lncp.sh
66 |
67 | .npmignore
68 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 赵大海
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/READAPI.md:
--------------------------------------------------------------------------------
1 | # wx-ant-ble 接口文档
2 |
3 | ### 目录
4 | - [初始化 `BTManager() `](#jump-init)
5 | - [扫描外设 `scan()`](#jump-scan)
6 | - [停止扫描 `stopScan()`](#jump-stopScan)
7 | - [连接外设 `connect()`](#jump-connect)
8 | - [断开连接 `disconnect()`](#jump-disconnect)
9 | - [读特征值 `read()`](#jump-read)
10 | - [写数据 `write()`](#jump-write)
11 | - [监听特征值 `notify()`](#jump-notify)
12 | - [注册状态更新回调 `registerDidUpdateConnectStatus()`](#jump-callback-connect-status)
13 | - [注册发现外设回调 `registerDidDiscoverDevice()`](#jump-callback-discover-device)
14 | - [注册特征值改变回调 `registerDidUpdateValueForCharacteristic()`](#jump-callback-value-update)
15 |
16 |
17 | ### 详细接口说明
18 | - #### 初始化 `BTManager(config)`
19 | > 参数:`config(Object)` 配置
20 | > 说明:初始化SDK管理实例,**单例模式**。
21 | >
22 | ```js
23 | this.bt = new BTManager({
24 | debug: false
25 | });
26 | ```
27 | | 参数字段 | 类型 | 必填 | 默认 | 说明 |
28 | | :-- | :--: | :--: | :--: | :-- |
29 | | debug | Boolean | 否 | false | 是否开启打印调试 |
30 |
31 |
32 | - #### 扫描外设 `scan(options)`
33 | > 参数:`options(Object)` 扫描参数
34 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
35 | > 说明:开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。
36 | > 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。
37 | > 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。
38 | >
39 | ```js
40 | this.bt.scan({
41 | services: [],
42 | allowDuplicatesKey: false,
43 | interval : 0,
44 | timeout: 15,
45 | deviceName: '',
46 | containName:''
47 | }).then(res => {
48 | console.log('scan success', res);
49 | }).catch(e => {
50 | console.log('scan fail', e);
51 | });
52 | ```
53 | | 参数字段 | 类型 | 必填 | 默认 | 说明 |
54 | | :-- | :-- | :--: | :--: | :-- |
55 | | services | Array | 否 | [] |主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备 |
56 | | allowDuplicatesKey | Boolean | 否 | false | 是否允许重复上报设备 |
57 | | interval | Number | 否 | 0 | 上报新设备的间隔 |
58 | | timeout | Number | 否 | 15000 | 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。-1表示无限超时。[超时回调](#jump-callback-discover-device) |
59 | | deviceName | String | 否 | "" | 通过蓝牙名称过滤,需要匹配的设备名称 |
60 | | containName | String | 否 | "" | 通过蓝牙名称过滤,需要包含的设备名称 |
61 |
62 |
63 | - #### 停止扫描 `stopScan()`
64 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
65 | > 说明:停止扫描,取消超时延时。
66 | ```js
67 | this.bt.stopScan()
68 | .then(res => {
69 | console.log('stopScan success', res);
70 | }).catch(e => {
71 | console.log('stopScan fail', e);
72 | })
73 | ```
74 |
75 | - #### 连接外设 `connect(device , timeout)`
76 | > 参数:`device(Object)` 设备对象
77 | > `timeout(Number)` 超时时间
78 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
79 | > 说明:连接指定的外设,需要传入外设对象。
80 | >
81 | ```js
82 | this.bt.connect(device)
83 | .then(res => {
84 | console.log('connect success', res);
85 | }).catch(e => {
86 | console.log('connect fail', e);
87 | });
88 | ```
89 | | 参数字段 | 类型 | 必填 | 默认 | 说明 |
90 | | :-- | :-- | :--: | :--: | :-- |
91 | | device | Object | 是 | -- | 指定连接的[外设对象](#jump-DeviceInfo),从[registerDidDiscoverDevice](#jump-callback-discover-device)注册的回调中得到 |
92 | | timeout | Number | 否 | 15000 | 连接超时时间,毫秒,支付宝小程序无效|
93 |
94 |
95 | - #### 断开连接 `disconnect()`
96 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
97 | >
98 | ```js
99 | this.bt.disconnect()
100 | .then(res => {
101 | console.log('disconnect success', res);
102 | }).catch(e => {
103 | console.log('disconnect fail', e);
104 | })
105 | ```
106 |
107 | - #### 读特征值 `read(params)`
108 | > 参数:`params(Object)` 参数
109 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
110 | > 说明:读某个特征值。
111 | >
112 | ```js
113 | this.bt.read({
114 | suuid: 'xxxx', // 特征对应的服务uuid
115 | cuuid: 'xxxx' // 特征uuid
116 | }).then(res => {
117 | console.log('read success', res);
118 | }).catch(e => {
119 | console.log('read fail', e);
120 | })
121 | ```
122 | | 参数字段 | 类型 | 必填 | 说明 |
123 | | :--: | :--: | :--: | :--: |
124 | | suuid | String | 是 | 特征对应的服务uuid |
125 | | cuuid | String | 是 | 特征uuid |
126 |
127 |
128 | - #### 写数据 `write(params)`
129 | > 参数:`params(Object)` 参数
130 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
131 | > 说明:向蓝牙模块写入数据。
132 | >
133 | ```js
134 | this.bt.write({
135 | suuid: 'xxxx', // 特征对应的服务uuid
136 | cuuid: 'xxxx', // 特征uuid
137 | value: 'FFFF' // 写入的数据
138 | }).then(res => {
139 | console.log('write success', res);
140 | }).catch(e => {
141 | console.log('write fail', e);
142 | })
143 | ```
144 | | 参数字段 | 类型 | 必填 | 说明 |
145 | | :--: | :--: | :--: | :--: |
146 | | suuid | String | 是 | 特征对应的服务uuid |
147 | | cuuid | String | 是 | 特征uuid |
148 | | value | String | 是 | 16进制字符串 |
149 |
150 |
151 | - #### 读特征值 `notify(params)`
152 | > 参数:`params(Object)` 参数
153 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
154 | > 说明:监听某个特征值变化。
155 | >
156 | ```js
157 | this.bt.notify({
158 | suuid: 'xxxx', // 特征对应的服务uuid
159 | cuuid: 'xxxx', // 特征uuid
160 | state: true/false // 是否监听
161 | }).then(res => {
162 | console.log('notify success', res);
163 | }).catch(e => {
164 | console.log('notify fail', e);
165 | })
166 | ```
167 | | 参数字段 | 类型 | 必填 | 说明 |
168 | | :--: | :--: | :--: | :--: |
169 | | suuid | String | 是 | 特征对应的服务uuid |
170 | | cuuid | String | 是 | 特征uuid |
171 | | state | Boolean | 是 | 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听 |
172 |
173 |
174 | - #### 注册状态更新回调 `registerDidUpdateConnectStatus(callback)`
175 | > 参数:`callback(Function)` 回调函数
176 | > 说明:注册状态更新时的回调函数,当状态connectStatus发生变化时回调已注册的函数。
177 | >
178 | ```js
179 | this.bt.registerDidUpdateConnectStatus(res => {
180 | // 参数结构注意查看log
181 | console.log('registerDidUpdateConnectStatus', res);
182 | if (res.connectStatus === ConnectStatus.connected) {
183 | this.setData({ isConnected: true });
184 | ...
185 | }else if (res.connectStatus === ConnectStatus.disconnected) {
186 | this.setData({ isConnected: false });
187 | ...
188 | }
189 | });
190 | ```
191 | **返回示例**
192 | ```js
193 | {
194 | code: 222,
195 | connectStatus: 2,
196 | message: '连接成功',
197 | device: {
198 | RSSI: -70, // 蓝牙信号强度,越大越强
199 | name: 'XXXX', // 设备名称
200 | advertisData: 'XXXX', // 广播数据
201 | deviceId: 'XXXX', // 设备id
202 | ...
203 | }
204 | }
205 | ```
206 | | 参数字段 | 类型 | 参考 | 必填 | 说明 |
207 | | :-- | :-- | :-- | :--: | :-- |
208 | | code | Number | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 连接状态码 |
209 | | connectStatus | ConnectStatus | [连接状态说明](#jump-ConnectStatus) | 是 | 连接状态 |
210 | | device | Object | [设备说明](#jump-DeviceInfo) | 否 | 设备信息,连接成功之后获取 |
211 | | message | String | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 状态描述 |
212 |
213 |
214 | - #### 注册发现外设回调 `registerDidDiscoverDevice(callback)`
215 | > 参数:`callback(Function)` 回调函数
216 | > 说明:当扫描到设备时回调,或者达到超时时间回调。
217 | >
218 | ```js
219 | this.bt.registerDidDiscoverDevice(res => {
220 | // 参数结构注意查看log
221 | console.log('registerDidDiscoverDevice', res);
222 | ...
223 | });
224 | ```
225 | **返回示例**
226 | ```js
227 | {
228 | code: 210,
229 | timeout: false,
230 | message: '发现外设',
231 | device: {
232 | RSSI: -70, // 蓝牙信号强度,越大越强
233 | name: 'XXXX', // 设备名称
234 | advertisData: 'XXXX', // 广播数据
235 | deviceId: 'XXXX', // 设备id
236 | ...
237 | }
238 | }
239 | ```
240 | | 参数字段 | 类型 | 参考 | 必填 | 说明 |
241 | | :-- | :-- | :-- | :--: | :-- |
242 | | code | Number | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 扫描状态码 |
243 | | timeout | Boolean | -- | 是 | 扫描是否超时 |
244 | | device | Object | [设备说明](#jump-DeviceInfo) | 否 | 设备信息 |
245 | | message | String | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 状态描述 |
246 |
247 |
248 | - #### 注册状态更新回调 `registerDidUpdateValueForCharacteristic(callback)`
249 | > 参数:`callback(Function)` 回调函数
250 | > 说明:当监听的特征值改变时回调,或者读特征值时回调。
251 | >
252 | ```js
253 | this.bt.registerDidUpdateValueForCharacteristic(res => {
254 | // 参数结构注意查看log
255 | console.log('registerDidUpdateValueForCharacteristic', res);
256 | ...
257 | });
258 | ```
259 | **返回示例**
260 | ```js
261 | {
262 | characteristic: 'XXXX',
263 | serviceId: 'XXXX',
264 | deviceId: 'XXXX',
265 | value: 'FFFF'
266 | }
267 | ```
268 | | 参数字段 | 类型 | 必填 | 说明 |
269 | | :-- | :--: | :--: | :-- |
270 | | characteristic | String | 是 | 特征uuid |
271 | | serviceId | String | 是 | 特征对应的服务uuid |
272 | | deviceId | String | 是 | 设备id |
273 | | value | String | 是 | 特征值 |
274 |
275 |
276 | ## 枚举类型说明
277 |
278 | - #### 设备信息
279 | | 属性字段 | 类型 | 必填 | 说明 |
280 | | :-- | :-- | :--: | :-- |
281 | | RSSI | Number | 是 | 设备信号强度 |
282 | | deviceId | String | 是 | 设备id |
283 | | name | String | 是 | 设备名称 |
284 | | localName | String | 否 | 设备名称 |
285 | | services | Array | 是 | 设备所有[服务](#jump-DeviceService) |
286 | | advertisData | String | 否 | 广播数据 |
287 | | advertisServiceUUIDs | Array | 否 | 广播中的服务UUID |
288 |
289 |
290 | - #### 服务
291 | | 属性字段 | 类型 | 必填 | 说明 |
292 | | :-- | :-- | :--: | :-- |
293 | | serviceId | String | 是 | 服务uuid |
294 | | characteristics | Array | 是 | 服务中的所有[特征](#jump-DeviceCharacteristic) |
295 |
296 |
297 | - #### 连接状态 `ConnectStatus`
298 | | 状态key | 状态值(Number)| 状态描述 |
299 | | :-- | :--: | :--: |
300 | | ConnectStatus.disconnected | 0 | 未连接或连接断开,允许连接 |
301 | | ConnectStatus.connecting | 1 | 正在连接,不允许再连接 |
302 | | ConnectStatus.connected | 2 | 已连接,不允许再连接 |
303 |
304 |
305 | - #### 特征
306 | | 属性字段 | 类型 | 必填 | 说明 |
307 | | :-- | :-- | :--: | :-- |
308 | | uuid | String | 是 | 特征uuid |
309 | | properties | Object | 是 | 特征的特性 indicate/notify/read/write |
310 |
311 |
312 | - #### 发现外设回调和连接状态改变回调成功事件 `SuccessCallbackEvent`
313 | | 事件码 | 事件key |事件描述 |
314 | | :--: | :-- | :-- |
315 | | 210 | SuccessCallbackEvent.Success_DiscoverDevice_CB_Discover | 发现外设 |
316 | | 211 | SuccessCallbackEvent.Success_DiscoverDevice_CB_ScanDone | 扫描完成 |
317 | | 220 | SuccessCallbackEvent.Success_ConnectStatus_CB_PowerOn | 蓝牙打开 |
318 | | 221 | SuccessCallbackEvent.Success_ConnectStatus_CB_Connecting| 正在连接 |
319 | | 222 | SuccessCallbackEvent.Success_ConnectStatus_CB_Connected | 连接成功 |
320 | | 223 | SuccessCallbackEvent.Success_ConnectStatus_CB_Stop | 断开成功 |
321 |
322 |
323 | - #### 发现外设回调和连接状态改变回调失败事件 `ErrorCallbackEvent`
324 | | 事件码 | 事件key |事件描述 |
325 | | :--: | :-- | :-- |
326 | | 410 | ErrorCallbackEvent.Error_DiscoverDevice_CB_Timeout | 扫描超时 |
327 | | 420 | ErrorCallbackEvent.Error_ConnectStatus_CB_PowerOff | 蓝牙关闭 |
328 | | 421 | ErrorCallbackEvent.Error_ConnectStatus_CB_ConnectFail | 连接失败 |
329 | | 422 | ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected | 连接断开 |
330 |
331 |
332 | - #### 接口调用成功事件`SuccessApiThen`
333 | | 事件码 | 事件key |事件描述 |
334 | | :--: | :-- | :-- |
335 | | 2010 | SuccessApiThen.Success_Scan | 扫描接口成功调用 |
336 | | 2020 | SuccessApiThen.Success_StopScan | 停止扫描接口成功调用 |
337 | | 2030 | SuccessApiThen.Success_Connect | 连接接口成功调用 |
338 | | 2040 | SuccessApiThen.Success_Disconnect | 断开接口成功调用 |
339 | | 2050 | SuccessApiThen.Success_Read | 读特征值接口成功调用 |
340 | | 2060 | SuccessApiThen.Success_Write | 写入数据接口成功调用 |
341 | | 2070 | SuccessApiThen.Success_Notify | 监听特征值接口成功调用 |
342 |
343 |
344 | - #### 接口调用失败事件`ErrorApiCatch`
345 | | 事件码 | 事件key |事件描述 |
346 | | :--: | :-- | :-- |
347 | | 4000 | ErrorApiCatch.Error_Low_Version | 当前基础库版本低,请更新微信版本 |
348 | | 4010 | ErrorApiCatch.Error_Scan_Failed | 扫描错误:扫描失败 |
349 | | 4011 | ErrorApiCatch.Error_Scan_PowerOff | 扫描错误:蓝牙被关闭 |
350 | | 4012 | ErrorApiCatch.Error_Scan_NoService | 扫描错误:没有找到指定服务 |
351 | | 4020 | ErrorApiCatch.Error_StopScan_Failed | 停止扫描错误:停止扫描失败 |
352 | | 4021 | ErrorApiCatch.Error_StopScan_PowerOff | 停止扫描错误:蓝牙被关闭 |
353 | | 4030 | ErrorApiCatch.Error_Connect_Failed | 连接错误:连接失败 |
354 | | 4031 | ErrorApiCatch.Error_Connect_PowerOff | 连接错误:蓝牙被关闭 |
355 | | 4032 | ErrorApiCatch.Error_Connect_AlreadyConnected | 连接错误:已经连接或正在连接 |
356 | | 4033 | ErrorApiCatch.Error_Connect_Timeout | 连接错误:连接超时 |
357 | | 4034 | ErrorApiCatch.Error_Connect_EmptyId | 连接错误:设备id不能为空 |
358 | | 4040 | ErrorApiCatch.Error_Disconnect_Failed | 断开错误:断开失败 |
359 | | 4050 | ErrorApiCatch.Error_Read_Failed | 读特征值错误:读特征值失败 |
360 | | 4051 | ErrorApiCatch.Error_Read_NotConnected | 读特征值错误:蓝牙未连接 |
361 | | 4052 | ErrorApiCatch.Error_Read_NotSupport | 读特征值错误:当前特征不支持读操作 |
362 | | 4053 | ErrorApiCatch.Error_Read_NoService | 读特征值错误:没有找到指定服务 |
363 | | 4054 | ErrorApiCatch.Error_Read_NoCharacteristic | 读特征值错误:没有找到指定特征值 |
364 | | 4060 | ErrorApiCatch.Error_Write_Failed | 写入数据错误:写入数据失败 |
365 | | 4061 | ErrorApiCatch.Error_Write_NotConnected | 写入数据错误:蓝牙未连接 |
366 | | 4062 | ErrorApiCatch.Error_Write_NotSupport | 写入数据错误:当前特征不支持写操作 |
367 | | 4063 | ErrorApiCatch.Error_Write_NoService | 写入数据错误:没有找到指定服务 |
368 | | 4064 | ErrorApiCatch.Error_Write_NoCharacteristic | 写入数据错误:没有找到指定特征值 |
369 | | 4070 | ErrorApiCatch.Error_Notify_Failed | 监听特征值错误:监听特征值错误失败 |
370 | | 4071 | ErrorApiCatch.Error_Notify_NotConnected | 监听特征值错误:蓝牙未连接 |
371 | | 4072 | ErrorApiCatch.Error_Notify_NotSupport | 监听特征值错误:当前特征不支持监听操作 |
372 | | 4073 | ErrorApiCatch.Error_Notify_NoService | 监听特征值错误:没有找到指定服务 |
373 | | 4074 | ErrorApiCatch.Error_Notify_NoCharacteristic | 监听特征值错误:没有找到指定特征值 |
374 |
375 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wx-ant-ble
2 |
3 | [](https://www.npmjs.com/package/wx-ant-ble)
4 |
5 | ### 微信、支付宝小程序BLE蓝牙SDK
6 |
7 | ### [详细接口说明](READAPI.md)
8 |
9 | ## Release Notes
10 |
11 | #### v1.1.0
12 | 2019/01/15
13 | 1、发布第一个可用版本。封装蓝牙接口,兼容微信和支付宝小程序。附有说明和demo。
14 |
15 | #### v1.1.1
16 | 2019/08/15
17 | 1、修复微信小程序安卓手机断开连接,状态更新不回调的bug。
18 | 2、已知另一个微信小程序蓝牙bug。安卓手机在设备异常断开时(比如断电),不会触发`onBLEConnectionStateChange`回调。除非在已经设置监听notify的情况下。
19 |
20 | ## Features
21 | - 兼容微信和支付宝小程序
22 | - 简洁但功能完整的Api,可以根据需求自由调用
23 | - 接口均有返回状态,判断是否调用成功
24 | - 单例模式
25 | - [完整例子](https://github.com/zhaodahai/wxble)
26 |
27 | ## Directory
28 | - ~/index.js SDK入口
29 | - ~/src SDK源码
30 | - ~/example 微信和支付宝小程序demo
31 |
32 | ## Installation
33 | - npm
34 |
35 | ```shell
36 | npm install wx-ant-ble
37 |
38 | // 微信小程序请查看npm文档,支付宝小程序仅下载
39 | ```
40 |
41 | - 直接下载或者使用git克隆
42 |
43 | ```shell
44 | git clone https://github.com/zhaodahai/wx-ant-ble.git
45 | ```
46 |
47 | ## Usage
48 | #### 1、引入SDK管理类和枚举
49 | ```js
50 | // 引入SDK管理类 微信小程序通过npm构建后
51 | import { BTManager, ConnectStatus } from 'wx-ant-ble';
52 | // 引入SDK管理类 支付宝小程序
53 | import { BTManager, ConnectStatus } from '../../wx-ant-ble/index.js';
54 | ```
55 |
56 | #### 2、初始化蓝牙管理器 & 设置用户信息
57 | ```js
58 | // 初始化蓝牙管理器 单例模式
59 | this.bt = new BTManager({
60 | debug: true // 是否开启打印调试
61 | });
62 | ```
63 |
64 | #### 3、注册回调
65 | ```js
66 | // 注册状态更新回调
67 | this.bt.registerDidUpdateConnectStatus(res => {
68 | // 参数结构注意查看log
69 | console.log('registerDidUpdateConnectStatus', res);
70 | if (res.connectStatus === ConnectStatus.connected) {
71 | this.setData({ isConnected: true });
72 | ...
73 | }else if (res.connectStatus === ConnectStatus.disconnected) {
74 | this.setData({ isConnected: false });
75 | ...
76 | }
77 | });
78 | ```
79 | ```js
80 | // 注册发现外设回调,当扫描到设备时回调,或者达到超时时间回调。
81 | this.bt.registerDidDiscoverDevice(res => {
82 | // 参数结构注意查看log
83 | console.log('registerDidDiscoverDevice', res);
84 | ...
85 | });
86 | ```
87 | ```js
88 | // 注册特征值改变回调,当监听的特征值改变时回调,或者读特征值时回调。
89 | this.bt.registerDidUpdateValueForCharacteristic(res => {
90 | // 参数结构注意查看log
91 | console.log('registerDidUpdateValueForCharacteristic', res);
92 | ...
93 | });
94 | ```
95 |
96 | #### 4、扫描 & 停止扫描
97 | ```js
98 | // 开始扫描
99 | this.bt.scan({
100 | services: [], // 主service的uuid列表
101 | allowDuplicatesKey: false, // 是否允许重复上报设备
102 | interval: 0, // 上报新设备的间隔,默认为0
103 | timeout: 15000, // 扫描超时时间,毫秒
104 | deviceName: '', // 需要匹配的设备名称
105 | containName: '' // 需要包含的设备名称
106 | }).then(res => {
107 | console.log('scan success', res);
108 | }).catch(e => {
109 | console.log('scan fail', e);
110 | });
111 | ```
112 | ```js
113 | // 停止扫描
114 | this.bt.stopScan()
115 | .then(res => {
116 | console.log('stopScan success', res);
117 | }).catch(e => {
118 | console.log('stopScan fail', e);
119 | })
120 | ```
121 | #### 5、连接 & 断开连接
122 | ```js
123 | // 连接设备
124 | this.bt.connect(device) // device应为registerDidDiscoverDevice注册的方法上报的设备对象
125 | .then(res => {
126 | console.log('connect success', res);
127 | }).catch(e => {
128 | console.log('connect fail', e);
129 | });
130 | ```
131 | ```js
132 | // 断开连接
133 | this.bt.disconnect()
134 | .then(res => {
135 | console.log('disconnect success', res);
136 | }).catch(e => {
137 | console.log('disconnect fail', e);
138 | })
139 | ```
140 | #### 5、Read & Notify & Write
141 | ```js
142 | // 读特征值,读到的值从registerDidUpdateValueForCharacteristic注册的方法上报
143 | this.bt.read({
144 | suuid: 'xxxx', // 特征对应的服务uuid
145 | cuuid: 'xxxx' // 特征uuid
146 | }).then(res => {
147 | console.log('read success', res);
148 | }).catch(e => {
149 | console.log('read fail', e);
150 | })
151 | ```
152 | ```js
153 | // 向蓝牙模块写入数据
154 | this.bt.write({
155 | suuid: 'xxxx', // 特征对应的服务uuid
156 | cuuid: 'xxxx', // 特征uuid
157 | value: 'FFFF' // 写入的数据
158 | }).then(res => {
159 | console.log('write success', res);
160 | }).catch(e => {
161 | console.log('write fail', e);
162 | })
163 | ```
164 | ```js
165 | // 监听/停止监听 特征值改变,改变特征值从registerDidUpdateValueForCharacteristic注册的方法上报
166 | this.bt.notify({
167 | suuid: 'xxxx', // 特征对应的服务uuid
168 | cuuid: 'xxxx', // 特征uuid
169 | state: true/false // 是否监听
170 | }).then(res => {
171 | console.log('notify success', res);
172 | }).catch(e => {
173 | console.log('notify fail', e);
174 | })
175 | ```
176 |
177 | ## WxApp
178 | [仓库地址](https://github.com/zhaodahai/wxble)
179 |
180 | 
181 |
182 |
183 |
184 | ## Notice
185 | - 具体使用请查看[demo](example)和[Api文档](READAPI.md)
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
--------------------------------------------------------------------------------
/example/antdemo/.tea/entryFiles-development/config$.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/.tea/entryFiles-development/config$.js
--------------------------------------------------------------------------------
/example/antdemo/.tea/entryFiles-development/importScripts$.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/.tea/entryFiles-development/importScripts$.js
--------------------------------------------------------------------------------
/example/antdemo/.tea/entryFiles-development/index$.remote.worker.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/.tea/entryFiles-development/index$.remote.worker.js
--------------------------------------------------------------------------------
/example/antdemo/.tea/entryFiles-development/index$.web.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/.tea/entryFiles-development/index$.web.js
--------------------------------------------------------------------------------
/example/antdemo/.tea/entryFiles-development/index$.worker.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/.tea/entryFiles-development/index$.worker.js
--------------------------------------------------------------------------------
/example/antdemo/app.acss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | page {
3 | flex: 1;
4 | display: flex;
5 | background: #f7f7f7;
6 | flex-direction: column;
7 | align-items: center;
8 | }
9 |
10 | .absTopLeft0 {
11 | position: absolute;
12 | top: 0rpx;
13 | left: 0rpx;
14 | }
15 |
16 | /* 居中显示 */
17 | .flex-center{
18 | display: flex;
19 | align-items: center;
20 | justify-content: center;
21 | }
22 |
23 | /* 水平居中显示 */
24 | .flex-row-center{
25 | display: flex;
26 | flex-direction: row;
27 | align-items: center;
28 | }
29 |
30 | /* 垂直居中显示 */
31 | .flex-column-center{
32 | display: flex;
33 | flex-direction: column;
34 | align-items: center;
35 | }
36 |
37 | /* 垂直靠左显示 */
38 | .flex-column-left{
39 | display: flex;
40 | flex-direction: column;
41 | align-items: flex-start;
42 | }
43 |
44 | /* 水平靠左显示 */
45 | .flex-row-left{
46 | display: flex;
47 | flex-direction: row;
48 | align-items: flex-start;
49 | justify-content: center
50 | }
51 |
52 | /* 垂直居下显示 */
53 | .flex-column-end{
54 | display: flex;
55 | flex-direction: column;
56 | justify-content: flex-end;
57 | }
58 |
59 | ::-webkit-scrollbar {
60 | width: 0;
61 | height: 0;
62 | color: transparent;
63 | }
--------------------------------------------------------------------------------
/example/antdemo/app.js:
--------------------------------------------------------------------------------
1 | App({
2 | onLaunch(options) {
3 | // 第一次打开
4 | // options.query == {number:1}
5 | console.info('App onLaunch');
6 | },
7 | onShow(options) {
8 | // 从后台被 scheme 重新打开
9 | // options.query == {number:1}
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/example/antdemo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/home/home",
4 | "pages/index/index"
5 |
6 | ],
7 | "window": {
8 | "defaultTitle": "My App"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example/antdemo/images/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/images/arrow.png
--------------------------------------------------------------------------------
/example/antdemo/pages/home/home.acss:
--------------------------------------------------------------------------------
1 | /* pages/home/home.wxss */
2 |
3 | .page {
4 | width: 100%;
5 | height: 100%;
6 | background: #eee;
7 | }
8 |
9 | .btn-view {
10 | margin-top: 30rpx;
11 | height: 100rpx;
12 | width: 95%;
13 | justify-content: space-around;
14 | }
15 |
16 | .btn-view .btn {
17 | width: 25%;
18 | height: 80%;
19 | background: #1E90FF;
20 | color: white;
21 | border-radius: 10rpx;
22 | }
23 |
24 | .status-view {
25 | height: 50rpx;
26 | width: 100%;
27 | margin-left: 50rpx;
28 | }
29 |
30 | .title-view {
31 | margin-top: 30rpx;
32 | height: 50rpx;
33 | width: 100%;
34 | margin-left: 50rpx;
35 | font-size: 40rpx;
36 | font-weight: 400;
37 | line-height: 40rpx;
38 | }
39 |
40 | scroll-view {
41 | height: 240rpx;
42 | width: 100%;
43 | background: white;
44 | }
45 |
46 | .cell {
47 | height: 80rpx;
48 | width: 100%;
49 | justify-content: space-between;
50 | }
51 |
52 | .cell .name{
53 | font-size: 40rpx;
54 | font-weight: 300;
55 | margin-left: 30rpx;
56 | }
57 |
58 | .cell image {
59 | margin-right: 20rpx;
60 | height: 30rpx;
61 | width: 30rpx;
62 | }
63 |
64 | .cell .uuid {
65 | font-size: 27rpx;
66 | margin-left: 30rpx;
67 | font-weight: 300;
68 | }
69 |
70 | .cell .btn {
71 | width: 100rpx;
72 | color: #1E90FF;
73 | font-size: 30rpx;
74 | }
75 |
76 | scroll-view .line {
77 | height: 2rpx;
78 | width: 100%;
79 | background:#e0e0e0;
80 | }
--------------------------------------------------------------------------------
/example/antdemo/pages/home/home.axml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 扫描
6 | 停止扫描
7 | 断开蓝牙
8 |
9 |
10 | {{connected?("Connected "+device.name):"Disconnected"}}
11 |
12 | Peripherals
13 |
14 |
15 |
16 | {{item.name}}
17 |
18 |
19 |
20 |
21 |
22 |
23 | Notify Characteristic
24 |
25 |
26 |
27 | {{item.cuuid}}
28 | {{item.listening?"Stop":"Notify"}}
29 |
30 |
31 |
32 |
33 |
34 | Read Characteristic
35 |
36 |
37 |
38 | {{item.cuuid}}
39 | Read
40 |
41 |
42 |
43 |
44 |
45 | Write Characteristic
46 |
47 |
48 |
49 | {{item.cuuid}}
50 | Write
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/example/antdemo/pages/home/home.js:
--------------------------------------------------------------------------------
1 |
2 | import { BTManager , ConnectStatus } from '../../wx-ant-ble/index.js';
3 |
4 | Page({
5 | data: {
6 | // 蓝牙是否连接
7 | connected: false,
8 | // 成功连接的设备
9 | device: {},
10 | // 扫描到的设备
11 | devices:[],
12 | // 设备能够notify的特征
13 | notifyUUIDs: [],
14 | // 设备能够read的特征
15 | readUUIDs: [],
16 | // 设备能够write的特征
17 | writeUUIDs: [],
18 | },
19 |
20 | onLoad: function (options) {
21 | // 初始化蓝牙管理器
22 | this.bt = new BTManager({
23 | debug: true
24 | });
25 | // 注册状态回调
26 | this.bt.registerDidUpdateConnectStatus(this.didUpdateConnectStatus.bind(this));
27 | // 注册发现外设回调
28 | this.bt.registerDidDiscoverDevice(this.didDiscoverDevice.bind(this));
29 | // 注册特征值改变回调
30 | this.bt.registerDidUpdateValueForCharacteristic(this.didUpdateValueForCharacteristic.bind(this));
31 | },
32 |
33 | // 状态改变回调
34 | didUpdateConnectStatus(res) {
35 | console.log('home registerDidUpdateConnectStatus', res);
36 | if (res.connectStatus === ConnectStatus.connected) {
37 | my.hideLoading();
38 | this.setData({connected: true , device:res.device});
39 | this.parseDeviceUUIDs(res.device);
40 | } else if (res.connectStatus === ConnectStatus.disconnected) {
41 | my.hideLoading();
42 | my.showToast({
43 | content: res.message,
44 | type: 'none'
45 | })
46 | this.setData({ connected: false, notifyUUIDs: [], readUUIDs: [], writeUUIDs:[]});
47 | }
48 | },
49 |
50 | // 发现外设回调
51 | didDiscoverDevice(res) {
52 | console.log('home didDiscoverDevice', res);
53 | if (res.timeout) {
54 | console.log('home didDiscoverDevice', '扫描超时');
55 | my.showToast({
56 | content: res.message,
57 | type: 'none'
58 | })
59 | } else {
60 | let device = res.device;
61 | let devices = this.data.devices;
62 | function checkDevice(d, ds) {
63 | for (let v of ds) {
64 | if (v.deviceId === d.deviceId) {
65 | return true;
66 | }
67 | }
68 | return false;
69 | }
70 | if (!checkDevice(device, devices)) {
71 | devices.push(device);
72 | }
73 | this.setData({ devices });
74 | }
75 | },
76 |
77 | // 特征值改变回调
78 | didUpdateValueForCharacteristic(res) {
79 | console.log('home registerDidUpdateValueForCharacteristic', res);
80 | },
81 |
82 | parseDeviceUUIDs(device) {
83 | let { notifyUUIDs, readUUIDs, writeUUIDs } = this.data;
84 | for (let service of device.services) {
85 | for (let char of service.characteristics) {
86 | if (char.properties.notify) {
87 | notifyUUIDs.push({
88 | suuid: service.serviceId,
89 | cuuid: char.characteristicId,
90 | listening: false
91 | })
92 | }
93 | if (char.properties.read) {
94 | readUUIDs.push({
95 | suuid: service.serviceId,
96 | cuuid: char.characteristicId,
97 | })
98 | }
99 | if (char.properties.write) {
100 | writeUUIDs.push({
101 | suuid: service.serviceId,
102 | cuuid: char.characteristicId,
103 | })
104 | }
105 | }
106 | }
107 | this.setData({ notifyUUIDs, readUUIDs, writeUUIDs });
108 | },
109 |
110 | // 扫描
111 | _scan() {
112 | this.bt.scan({
113 | services: [],
114 | allowDuplicatesKey: false,
115 | interval: 0,
116 | timeout: 15000,
117 | deviceName: '',
118 | containName: 'BIANLA'
119 | }).then(res => {
120 | console.log('home scan success', res);
121 | }).catch(e => {
122 | console.log('home scan fail', e);
123 | my.showToast({
124 | content: e.message,
125 | type: 'none'
126 | })
127 | });
128 | },
129 |
130 | // 停止扫描
131 | _stopScan() {
132 | this.bt.stopScan().then(res => {
133 | console.log('home stopScan success', res);
134 | }).catch(e => {
135 | console.log('home stopScan fail', e);
136 | })
137 | },
138 |
139 | // 连接
140 | _connect(e) {
141 | let index = e.currentTarget.id;
142 | this.bt.stopScan();
143 | let device = this.data.devices[index];
144 | this.bt.connect(device).then(res => {
145 | console.log('home connect success', res);
146 | }).catch(e => {
147 | my.showToast({
148 | content: e.message,
149 | type: 'none'
150 | })
151 | console.log('home connect fail', e);
152 | });
153 | my.showLoading({
154 | content: '连接' + device.name,
155 | });
156 | },
157 |
158 | // 断开连接
159 | _disconnect() {
160 | this.bt.disconnect().then(res => {
161 | console.log('home disconnect success', res);
162 | }).catch(e => {
163 | console.log('home disconnect fail', res);
164 | })
165 | },
166 |
167 | // 监听/停止监听
168 | _notify(e) {
169 | let index = e.currentTarget.id;
170 | let { suuid, cuuid, listening } = this.data.notifyUUIDs[index];
171 | this.bt.notify({
172 | suuid, cuuid, state: !listening
173 | }).then(res => {
174 | console.log('home notify success', res);
175 | this.setData({ [`notifyUUIDs[${index}].listening`]: !listening });
176 | }).catch(e => {
177 | console.log('home notify fail', e);
178 | })
179 | },
180 |
181 | // 读特征值
182 | _read(e) {
183 | let index = e.currentTarget.id;
184 | let { suuid, cuuid } = this.data.readUUIDs[index];
185 | this.bt.read({
186 | suuid, cuuid
187 | }).then(res => {
188 | console.log('home read success', res);
189 | }).catch(e => {
190 | console.log('home read fail', e);
191 | })
192 | },
193 |
194 | // 向蓝牙模块写入数据,这里只做简单的例子,发送的是 'FFFF' 的十六进制字符串
195 | _write(e) {
196 | let index = e.currentTarget.id;
197 | let { suuid, cuuid } = this.data.writeUUIDs[index];
198 | this.bt.write({
199 | suuid,
200 | cuuid,
201 | value: 'FFFF'
202 | }).then(res => {
203 | console.log('home write success', res);
204 | }).catch(e => {
205 | console.log('home write fail', e);
206 | })
207 | },
208 | });
209 |
--------------------------------------------------------------------------------
/example/antdemo/pages/home/home.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/example/antdemo/pages/index/index.axml:
--------------------------------------------------------------------------------
1 |
2 | this is a blank page
3 |
4 |
--------------------------------------------------------------------------------
/example/antdemo/pages/index/index.js:
--------------------------------------------------------------------------------
1 | Page({
2 | onLoad(query) {
3 | // 页面加载
4 | console.info(`Page onLoad with query: ${JSON.stringify(query)}`);
5 | },
6 | onReady() {
7 | // 页面加载完成
8 | },
9 | onShow() {
10 | // 页面显示
11 | },
12 | onHide() {
13 | // 页面隐藏
14 | },
15 | onUnload() {
16 | // 页面被关闭
17 | },
18 | onTitleClick() {
19 | // 标题被点击
20 | },
21 | onPullDownRefresh() {
22 | // 页面被下拉
23 | },
24 | onReachBottom() {
25 | // 页面被拉到底部
26 | },
27 | onShareAppMessage() {
28 | // 返回自定义分享信息
29 | return {
30 | title: 'My App',
31 | desc: 'My App description',
32 | path: 'pages/index/index',
33 | };
34 | },
35 | });
36 |
--------------------------------------------------------------------------------
/example/antdemo/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/example/antdemo/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/antdemo/snapshot.png
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/READAPI.md:
--------------------------------------------------------------------------------
1 | # wx-ant-ble 接口文档
2 |
3 | ### 目录
4 | - [初始化 `BTManager() `](#jump-init)
5 | - [扫描外设 `scan()`](#jump-scan)
6 | - [停止扫描 `stopScan()`](#jump-stopScan)
7 | - [连接外设 `connect()`](#jump-connect)
8 | - [断开连接 `disconnect()`](#jump-disconnect)
9 | - [读特征值 `read()`](#jump-read)
10 | - [写数据 `write()`](#jump-write)
11 | - [监听特征值 `notify()`](#jump-notify)
12 | - [注册状态更新回调 `registerDidUpdateConnectStatus()`](#jump-callback-connect-status)
13 | - [注册发现外设回调 `registerDidDiscoverDevice()`](#jump-callback-discover-device)
14 | - [注册特征值改变回调 `registerDidUpdateValueForCharacteristic()`](#jump-callback-value-update)
15 |
16 |
17 | ### 详细接口说明
18 | - #### 初始化 `BTManager(config)`
19 | > 参数:`config(Object)` 配置
20 | > 说明:初始化SDK管理实例,**单例模式**。
21 | >
22 | ```js
23 | this.bt = new BTManager({
24 | debug: false
25 | });
26 | ```
27 | | 参数字段 | 类型 | 必填 | 默认 | 说明 |
28 | | :-- | :--: | :--: | :--: | :-- |
29 | | debug | Boolean | 否 | false | 是否开启打印调试 |
30 |
31 |
32 | - #### 扫描外设 `scan(options)`
33 | > 参数:`options(Object)` 扫描参数
34 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
35 | > 说明:开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。
36 | > 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。
37 | > 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。
38 | >
39 | ```js
40 | this.bt.scan({
41 | services: [],
42 | allowDuplicatesKey: false,
43 | interval : 0,
44 | timeout: 15,
45 | deviceName: '',
46 | containName:''
47 | }).then(res => {
48 | console.log('scan success', res);
49 | }).catch(e => {
50 | console.log('scan fail', e);
51 | });
52 | ```
53 | | 参数字段 | 类型 | 必填 | 默认 | 说明 |
54 | | :-- | :-- | :--: | :--: | :-- |
55 | | services | Array | 否 | [] |主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备 |
56 | | allowDuplicatesKey | Boolean | 否 | false | 是否允许重复上报设备 |
57 | | interval | Number | 否 | 0 | 上报新设备的间隔 |
58 | | timeout | Number | 否 | 15000 | 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。-1表示无限超时。[超时回调](#jump-callback-discover-device) |
59 | | deviceName | String | 否 | "" | 通过蓝牙名称过滤,需要匹配的设备名称 |
60 | | containName | String | 否 | "" | 通过蓝牙名称过滤,需要包含的设备名称 |
61 |
62 |
63 | - #### 停止扫描 `stopScan()`
64 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
65 | > 说明:停止扫描,取消超时延时。
66 | ```js
67 | this.bt.stopScan()
68 | .then(res => {
69 | console.log('stopScan success', res);
70 | }).catch(e => {
71 | console.log('stopScan fail', e);
72 | })
73 | ```
74 |
75 | - #### 连接外设 `connect(device , timeout)`
76 | > 参数:`device(Object)` 设备对象
77 | > `timeout(Number)` 超时时间
78 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
79 | > 说明:连接指定的外设,需要传入外设对象。
80 | >
81 | ```js
82 | this.bt.connect(device)
83 | .then(res => {
84 | console.log('connect success', res);
85 | }).catch(e => {
86 | console.log('connect fail', e);
87 | });
88 | ```
89 | | 参数字段 | 类型 | 必填 | 默认 | 说明 |
90 | | :-- | :-- | :--: | :--: | :-- |
91 | | device | Object | 是 | -- | 指定连接的[外设对象](#jump-DeviceInfo),从[registerDidDiscoverDevice](#jump-callback-discover-device)注册的回调中得到 |
92 | | timeout | Number | 否 | 15000 | 连接超时时间,毫秒,支付宝小程序无效|
93 |
94 |
95 | - #### 断开连接 `disconnect()`
96 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
97 | >
98 | ```js
99 | this.bt.disconnect()
100 | .then(res => {
101 | console.log('disconnect success', res);
102 | }).catch(e => {
103 | console.log('disconnect fail', e);
104 | })
105 | ```
106 |
107 | - #### 读特征值 `read(params)`
108 | > 参数:`params(Object)` 参数
109 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
110 | > 说明:读某个特征值。
111 | >
112 | ```js
113 | this.bt.read({
114 | suuid: 'xxxx', // 特征对应的服务uuid
115 | cuuid: 'xxxx' // 特征uuid
116 | }).then(res => {
117 | console.log('read success', res);
118 | }).catch(e => {
119 | console.log('read fail', e);
120 | })
121 | ```
122 | | 参数字段 | 类型 | 必填 | 说明 |
123 | | :--: | :--: | :--: | :--: |
124 | | suuid | String | 是 | 特征对应的服务uuid |
125 | | cuuid | String | 是 | 特征uuid |
126 |
127 |
128 | - #### 写数据 `write(params)`
129 | > 参数:`params(Object)` 参数
130 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
131 | > 说明:向蓝牙模块写入数据。
132 | >
133 | ```js
134 | this.bt.write({
135 | suuid: 'xxxx', // 特征对应的服务uuid
136 | cuuid: 'xxxx', // 特征uuid
137 | value: 'FFFF' // 写入的数据
138 | }).then(res => {
139 | console.log('write success', res);
140 | }).catch(e => {
141 | console.log('write fail', e);
142 | })
143 | ```
144 | | 参数字段 | 类型 | 必填 | 说明 |
145 | | :--: | :--: | :--: | :--: |
146 | | suuid | String | 是 | 特征对应的服务uuid |
147 | | cuuid | String | 是 | 特征uuid |
148 | | value | String | 是 | 16进制字符串 |
149 |
150 |
151 | - #### 读特征值 `notify(params)`
152 | > 参数:`params(Object)` 参数
153 | > 返回值:Promise对象,[调用成功](#jump-SuccessApiThen) | [调用失败](#jump-ErrorApiCatch)
154 | > 说明:监听某个特征值变化。
155 | >
156 | ```js
157 | this.bt.notify({
158 | suuid: 'xxxx', // 特征对应的服务uuid
159 | cuuid: 'xxxx', // 特征uuid
160 | state: true/false // 是否监听
161 | }).then(res => {
162 | console.log('notify success', res);
163 | }).catch(e => {
164 | console.log('notify fail', e);
165 | })
166 | ```
167 | | 参数字段 | 类型 | 必填 | 说明 |
168 | | :--: | :--: | :--: | :--: |
169 | | suuid | String | 是 | 特征对应的服务uuid |
170 | | cuuid | String | 是 | 特征uuid |
171 | | state | Boolean | 是 | 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听 |
172 |
173 |
174 | - #### 注册状态更新回调 `registerDidUpdateConnectStatus(callback)`
175 | > 参数:`callback(Function)` 回调函数
176 | > 说明:注册状态更新时的回调函数,当状态connectStatus发生变化时回调已注册的函数。
177 | >
178 | ```js
179 | this.bt.registerDidUpdateConnectStatus(res => {
180 | // 参数结构注意查看log
181 | console.log('registerDidUpdateConnectStatus', res);
182 | if (res.connectStatus === ConnectStatus.connected) {
183 | this.setData({ isConnected: true });
184 | ...
185 | }else if (res.connectStatus === ConnectStatus.disconnected) {
186 | this.setData({ isConnected: false });
187 | ...
188 | }
189 | });
190 | ```
191 | **返回示例**
192 | ```js
193 | {
194 | code: 222,
195 | connectStatus: 2,
196 | message: '连接成功',
197 | device: {
198 | RSSI: -70, // 蓝牙信号强度,越大越强
199 | name: 'XXXX', // 设备名称
200 | advertisData: 'XXXX', // 广播数据
201 | deviceId: 'XXXX', // 设备id
202 | ...
203 | }
204 | }
205 | ```
206 | | 参数字段 | 类型 | 参考 | 必填 | 说明 |
207 | | :-- | :-- | :-- | :--: | :-- |
208 | | code | Number | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 连接状态码 |
209 | | connectStatus | ConnectStatus | [连接状态说明](#jump-ConnectStatus) | 是 | 连接状态 |
210 | | device | Object | [设备说明](#jump-DeviceInfo) | 否 | 设备信息,连接成功之后获取 |
211 | | message | String | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 状态描述 |
212 |
213 |
214 | - #### 注册发现外设回调 `registerDidDiscoverDevice(callback)`
215 | > 参数:`callback(Function)` 回调函数
216 | > 说明:当扫描到设备时回调,或者达到超时时间回调。
217 | >
218 | ```js
219 | this.bt.registerDidDiscoverDevice(res => {
220 | // 参数结构注意查看log
221 | console.log('registerDidDiscoverDevice', res);
222 | ...
223 | });
224 | ```
225 | **返回示例**
226 | ```js
227 | {
228 | code: 210,
229 | timeout: false,
230 | message: '发现外设',
231 | device: {
232 | RSSI: -70, // 蓝牙信号强度,越大越强
233 | name: 'XXXX', // 设备名称
234 | advertisData: 'XXXX', // 广播数据
235 | deviceId: 'XXXX', // 设备id
236 | ...
237 | }
238 | }
239 | ```
240 | | 参数字段 | 类型 | 参考 | 必填 | 说明 |
241 | | :-- | :-- | :-- | :--: | :-- |
242 | | code | Number | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 扫描状态码 |
243 | | timeout | Boolean | -- | 是 | 扫描是否超时 |
244 | | device | Object | [设备说明](#jump-DeviceInfo) | 否 | 设备信息 |
245 | | message | String | [回调成功事件](#jump-SuccessCallbackEvent) [回调失败事件](#jump-ErrorCallbackEvent) | 是 | 状态描述 |
246 |
247 |
248 | - #### 注册状态更新回调 `registerDidUpdateValueForCharacteristic(callback)`
249 | > 参数:`callback(Function)` 回调函数
250 | > 说明:当监听的特征值改变时回调,或者读特征值时回调。
251 | >
252 | ```js
253 | this.bt.registerDidUpdateValueForCharacteristic(res => {
254 | // 参数结构注意查看log
255 | console.log('registerDidUpdateValueForCharacteristic', res);
256 | ...
257 | });
258 | ```
259 | **返回示例**
260 | ```js
261 | {
262 | characteristic: 'XXXX',
263 | serviceId: 'XXXX',
264 | deviceId: 'XXXX',
265 | value: 'FFFF'
266 | }
267 | ```
268 | | 参数字段 | 类型 | 必填 | 说明 |
269 | | :-- | :--: | :--: | :-- |
270 | | characteristic | String | 是 | 特征uuid |
271 | | serviceId | String | 是 | 特征对应的服务uuid |
272 | | deviceId | String | 是 | 设备id |
273 | | value | String | 是 | 特征值 |
274 |
275 |
276 | ## 枚举类型说明
277 |
278 | - #### 设备信息
279 | | 属性字段 | 类型 | 必填 | 说明 |
280 | | :-- | :-- | :--: | :-- |
281 | | RSSI | Number | 是 | 设备信号强度 |
282 | | deviceId | String | 是 | 设备id |
283 | | name | String | 是 | 设备名称 |
284 | | localName | String | 否 | 设备名称 |
285 | | services | Array | 是 | 设备所有[服务](#jump-DeviceService) |
286 | | advertisData | String | 否 | 广播数据 |
287 | | advertisServiceUUIDs | Array | 否 | 广播中的服务UUID |
288 |
289 |
290 | - #### 服务
291 | | 属性字段 | 类型 | 必填 | 说明 |
292 | | :-- | :-- | :--: | :-- |
293 | | serviceId | String | 是 | 服务uuid |
294 | | characteristics | Array | 是 | 服务中的所有[特征](#jump-DeviceCharacteristic) |
295 |
296 |
297 | - #### 连接状态 `ConnectStatus`
298 | | 状态key | 状态值(Number)| 状态描述 |
299 | | :-- | :--: | :--: |
300 | | ConnectStatus.disconnected | 0 | 未连接或连接断开,允许连接 |
301 | | ConnectStatus.connecting | 1 | 正在连接,不允许再连接 |
302 | | ConnectStatus.connected | 2 | 已连接,不允许再连接 |
303 |
304 |
305 | - #### 特征
306 | | 属性字段 | 类型 | 必填 | 说明 |
307 | | :-- | :-- | :--: | :-- |
308 | | uuid | String | 是 | 特征uuid |
309 | | properties | Object | 是 | 特征的特性 indicate/notify/read/write |
310 |
311 |
312 | - #### 发现外设回调和连接状态改变回调成功事件 `SuccessCallbackEvent`
313 | | 事件码 | 事件key |事件描述 |
314 | | :--: | :-- | :-- |
315 | | 210 | SuccessCallbackEvent.Success_DiscoverDevice_CB_Discover | 发现外设 |
316 | | 211 | SuccessCallbackEvent.Success_DiscoverDevice_CB_ScanDone | 扫描完成 |
317 | | 220 | SuccessCallbackEvent.Success_ConnectStatus_CB_PowerOn | 蓝牙打开 |
318 | | 221 | SuccessCallbackEvent.Success_ConnectStatus_CB_Connecting| 正在连接 |
319 | | 222 | SuccessCallbackEvent.Success_ConnectStatus_CB_Connected | 连接成功 |
320 | | 223 | SuccessCallbackEvent.Success_ConnectStatus_CB_Stop | 断开成功 |
321 |
322 |
323 | - #### 发现外设回调和连接状态改变回调失败事件 `ErrorCallbackEvent`
324 | | 事件码 | 事件key |事件描述 |
325 | | :--: | :-- | :-- |
326 | | 410 | ErrorCallbackEvent.Error_DiscoverDevice_CB_Timeout | 扫描超时 |
327 | | 420 | ErrorCallbackEvent.Error_ConnectStatus_CB_PowerOff | 蓝牙关闭 |
328 | | 421 | ErrorCallbackEvent.Error_ConnectStatus_CB_ConnectFail | 连接失败 |
329 | | 422 | ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected | 连接断开 |
330 |
331 |
332 | - #### 接口调用成功事件`SuccessApiThen`
333 | | 事件码 | 事件key |事件描述 |
334 | | :--: | :-- | :-- |
335 | | 2010 | SuccessApiThen.Success_Scan | 扫描接口成功调用 |
336 | | 2020 | SuccessApiThen.Success_StopScan | 停止扫描接口成功调用 |
337 | | 2030 | SuccessApiThen.Success_Connect | 连接接口成功调用 |
338 | | 2040 | SuccessApiThen.Success_Disconnect | 断开接口成功调用 |
339 | | 2050 | SuccessApiThen.Success_Read | 读特征值接口成功调用 |
340 | | 2060 | SuccessApiThen.Success_Write | 写入数据接口成功调用 |
341 | | 2070 | SuccessApiThen.Success_Notify | 监听特征值接口成功调用 |
342 |
343 |
344 | - #### 接口调用失败事件`ErrorApiCatch`
345 | | 事件码 | 事件key |事件描述 |
346 | | :--: | :-- | :-- |
347 | | 4000 | ErrorApiCatch.Error_Low_Version | 当前基础库版本低,请更新微信版本 |
348 | | 4010 | ErrorApiCatch.Error_Scan_Failed | 扫描错误:扫描失败 |
349 | | 4011 | ErrorApiCatch.Error_Scan_PowerOff | 扫描错误:蓝牙被关闭 |
350 | | 4012 | ErrorApiCatch.Error_Scan_NoService | 扫描错误:没有找到指定服务 |
351 | | 4020 | ErrorApiCatch.Error_StopScan_Failed | 停止扫描错误:停止扫描失败 |
352 | | 4021 | ErrorApiCatch.Error_StopScan_PowerOff | 停止扫描错误:蓝牙被关闭 |
353 | | 4030 | ErrorApiCatch.Error_Connect_Failed | 连接错误:连接失败 |
354 | | 4031 | ErrorApiCatch.Error_Connect_PowerOff | 连接错误:蓝牙被关闭 |
355 | | 4032 | ErrorApiCatch.Error_Connect_AlreadyConnected | 连接错误:已经连接或正在连接 |
356 | | 4033 | ErrorApiCatch.Error_Connect_Timeout | 连接错误:连接超时 |
357 | | 4034 | ErrorApiCatch.Error_Connect_EmptyId | 连接错误:设备id不能为空 |
358 | | 4040 | ErrorApiCatch.Error_Disconnect_Failed | 断开错误:断开失败 |
359 | | 4050 | ErrorApiCatch.Error_Read_Failed | 读特征值错误:读特征值失败 |
360 | | 4051 | ErrorApiCatch.Error_Read_NotConnected | 读特征值错误:蓝牙未连接 |
361 | | 4052 | ErrorApiCatch.Error_Read_NotSupport | 读特征值错误:当前特征不支持读操作 |
362 | | 4053 | ErrorApiCatch.Error_Read_NoService | 读特征值错误:没有找到指定服务 |
363 | | 4054 | ErrorApiCatch.Error_Read_NoCharacteristic | 读特征值错误:没有找到指定特征值 |
364 | | 4060 | ErrorApiCatch.Error_Write_Failed | 写入数据错误:写入数据失败 |
365 | | 4061 | ErrorApiCatch.Error_Write_NotConnected | 写入数据错误:蓝牙未连接 |
366 | | 4062 | ErrorApiCatch.Error_Write_NotSupport | 写入数据错误:当前特征不支持写操作 |
367 | | 4063 | ErrorApiCatch.Error_Write_NoService | 写入数据错误:没有找到指定服务 |
368 | | 4064 | ErrorApiCatch.Error_Write_NoCharacteristic | 写入数据错误:没有找到指定特征值 |
369 | | 4070 | ErrorApiCatch.Error_Notify_Failed | 监听特征值错误:监听特征值错误失败 |
370 | | 4071 | ErrorApiCatch.Error_Notify_NotConnected | 监听特征值错误:蓝牙未连接 |
371 | | 4072 | ErrorApiCatch.Error_Notify_NotSupport | 监听特征值错误:当前特征不支持监听操作 |
372 | | 4073 | ErrorApiCatch.Error_Notify_NoService | 监听特征值错误:没有找到指定服务 |
373 | | 4074 | ErrorApiCatch.Error_Notify_NoCharacteristic | 监听特征值错误:没有找到指定特征值 |
374 |
375 |
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/README.md:
--------------------------------------------------------------------------------
1 | # wx-ant-ble
2 |
3 | [](https://www.npmjs.com/package/wx-ant-ble)
4 |
5 | ### 微信、支付宝小程序BLE蓝牙SDK
6 |
7 | ### [详细接口说明](READAPI.md)
8 |
9 | ## Features
10 | - 兼容微信和支付宝小程序
11 | - 简洁但功能完整的Api,可以根据需求自由调用
12 | - 接口均有返回状态,判断是否调用成功
13 | - 单例模式
14 |
15 | ## Directory
16 | - ~/index.js SDK入口
17 | - ~/src SDK源码
18 | - ~/example 微信和支付宝小程序demo
19 |
20 | ## Installation
21 | - npm
22 |
23 | ```shell
24 | npm install wx-ant-ble
25 |
26 | // 微信小程序请查看npm文档,支付宝小程序仅下载
27 | ```
28 |
29 | - 直接下载或者使用git克隆
30 |
31 | ```shell
32 | git clone https://github.com/zhaodahai/wx-ant-ble.git
33 | ```
34 |
35 | ## Usage
36 | #### 1、引入SDK管理类和枚举
37 | ```js
38 | // 引入SDK管理类 微信小程序通过npm构建后
39 | import { BTManager, ConnectStatus } from 'wx-ant-ble';
40 | // 引入SDK管理类 支付宝小程序
41 | import { BTManager, ConnectStatus } from '../../wx-ant-ble/index.js';
42 | ```
43 |
44 | #### 2、初始化蓝牙管理器 & 设置用户信息
45 | ```js
46 | // 初始化蓝牙管理器 单例模式
47 | this.bt = new BTManager({
48 | debug: true // 是否开启打印调试
49 | });
50 | ```
51 |
52 | #### 3、注册回调
53 | ```js
54 | // 注册状态更新回调
55 | this.bt.registerDidUpdateConnectStatus(res => {
56 | // 参数结构注意查看log
57 | console.log('registerDidUpdateConnectStatus', res);
58 | if (res.connectStatus === ConnectStatus.connected) {
59 | this.setData({ isConnected: true });
60 | ...
61 | }else if (res.connectStatus === ConnectStatus.disconnected) {
62 | this.setData({ isConnected: false });
63 | ...
64 | }
65 | });
66 | ```
67 | ```js
68 | // 注册发现外设回调,当扫描到设备时回调,或者达到超时时间回调。
69 | this.bt.registerDidDiscoverDevice(res => {
70 | // 参数结构注意查看log
71 | console.log('registerDidDiscoverDevice', res);
72 | ...
73 | });
74 | ```
75 | ```js
76 | // 注册特征值改变回调,当监听的特征值改变时回调,或者读特征值时回调。
77 | this.bt.registerDidUpdateValueForCharacteristic(res => {
78 | // 参数结构注意查看log
79 | console.log('registerDidUpdateValueForCharacteristic', res);
80 | ...
81 | });
82 | ```
83 |
84 | #### 4、扫描 & 停止扫描
85 | ```js
86 | // 开始扫描
87 | this.bt.scan({
88 | services: [], // 主service的uuid列表
89 | allowDuplicatesKey: false, // 是否允许重复上报设备
90 | interval: 0, // 上报新设备的间隔,默认为0
91 | timeout: 15000, // 扫描超时时间,毫秒
92 | deviceName: '', // 需要匹配的设备名称
93 | containName: '' // 需要包含的设备名称
94 | }).then(res => {
95 | console.log('scan success', res);
96 | }).catch(e => {
97 | console.log('scan fail', e);
98 | });
99 | ```
100 | ```js
101 | // 停止扫描
102 | this.bt.stopScan()
103 | .then(res => {
104 | console.log('stopScan success', res);
105 | }).catch(e => {
106 | console.log('stopScan fail', e);
107 | })
108 | ```
109 | #### 5、连接 & 断开连接
110 | ```js
111 | // 连接设备
112 | this.bt.connect(device) // device应为registerDidDiscoverDevice注册的方法上报的设备对象
113 | .then(res => {
114 | console.log('connect success', res);
115 | }).catch(e => {
116 | console.log('connect fail', e);
117 | });
118 | ```
119 | ```js
120 | // 断开连接
121 | this.bt.disconnect()
122 | .then(res => {
123 | console.log('disconnect success', res);
124 | }).catch(e => {
125 | console.log('disconnect fail', e);
126 | })
127 | ```
128 | #### 5、Read & Notify & Write
129 | ```js
130 | // 读特征值,读到的值从registerDidUpdateValueForCharacteristic注册的方法上报
131 | this.bt.read({
132 | suuid: 'xxxx', // 特征对应的服务uuid
133 | cuuid: 'xxxx' // 特征uuid
134 | }).then(res => {
135 | console.log('read success', res);
136 | }).catch(e => {
137 | console.log('read fail', e);
138 | })
139 | ```
140 | ```js
141 | // 向蓝牙模块写入数据
142 | this.bt.write({
143 | suuid: 'xxxx', // 特征对应的服务uuid
144 | cuuid: 'xxxx', // 特征uuid
145 | value: 'FFFF' // 写入的数据
146 | }).then(res => {
147 | console.log('write success', res);
148 | }).catch(e => {
149 | console.log('write fail', e);
150 | })
151 | ```
152 | ```js
153 | // 监听/停止监听 特征值改变,改变特征值从registerDidUpdateValueForCharacteristic注册的方法上报
154 | this.bt.notify({
155 | suuid: 'xxxx', // 特征对应的服务uuid
156 | cuuid: 'xxxx', // 特征uuid
157 | state: true/false // 是否监听
158 | }).then(res => {
159 | console.log('notify success', res);
160 | }).catch(e => {
161 | console.log('notify fail', e);
162 | })
163 | ```
164 |
165 | ## WxApp
166 |
167 |
168 | 
169 |
170 |
171 |
172 | ## ReleaseNotes
173 |
174 | ```
175 | v1.1.0
176 | 2019/01/15
177 | 发布第一个可用版本。封装蓝牙接口,兼容微信和支付宝小程序。附有说明和demo。
178 | ```
179 |
180 | ## Notice
181 | - 具体使用请查看[demo](example)和[Api文档](READAPI.md)
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/index.js:
--------------------------------------------------------------------------------
1 |
2 | import { BTManager } from './src/btmanager.js';
3 | import { ConnectStatus, SuccessCallbackEvent, ErrorCallbackEvent, SuccessApiThen, ErrorApiCatch } from './src/enum.js';
4 | const Version = '1.1.0';
5 |
6 | export {
7 | Version,
8 | BTManager,
9 | ConnectStatus,
10 | SuccessCallbackEvent,
11 | ErrorCallbackEvent,
12 | SuccessApiThen,
13 | ErrorApiCatch
14 | }
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "_from": "wx-ant-ble",
3 | "_id": "wx-ant-ble@1.1.0",
4 | "_inBundle": false,
5 | "_integrity": "sha512-+wriBP/UwGTn5gwwsNcDh/0AmT1rUwFpQFHsxMyeZPFFlsTPRlrgsExbGsY1mBLms0+/Q9tssc1OB7KX4KvxIw==",
6 | "_location": "/wx-ant-ble",
7 | "_phantomChildren": {},
8 | "_requested": {
9 | "type": "tag",
10 | "registry": true,
11 | "raw": "wx-ant-ble",
12 | "name": "wx-ant-ble",
13 | "escapedName": "wx-ant-ble",
14 | "rawSpec": "",
15 | "saveSpec": null,
16 | "fetchSpec": "latest"
17 | },
18 | "_requiredBy": [
19 | "#USER",
20 | "/"
21 | ],
22 | "_resolved": "https://registry.npmjs.org/wx-ant-ble/-/wx-ant-ble-1.1.0.tgz",
23 | "_shasum": "f829913b988b48b2ec0dc6b6156bcbd3bcf71a96",
24 | "_spec": "wx-ant-ble",
25 | "_where": "/Users/bianla/Desktop/iGit/dahai/BLEHelper/test",
26 | "author": {
27 | "name": "zhaodahai"
28 | },
29 | "bugs": {
30 | "url": "https://github.com/zhaodahai/wx-ant-ble/issues"
31 | },
32 | "bundleDependencies": false,
33 | "deprecated": false,
34 | "description": "微信、支付宝小程序BLE蓝牙SDK",
35 | "homepage": "https://github.com/zhaodahai/wx-ant-ble#readme",
36 | "keywords": [
37 | "ble",
38 | "bluetooth",
39 | "蓝牙",
40 | "小程序"
41 | ],
42 | "license": "ISC",
43 | "main": "index.js",
44 | "name": "wx-ant-ble",
45 | "repository": {
46 | "type": "git",
47 | "url": "git+https://github.com/zhaodahai/wx-ant-ble.git"
48 | },
49 | "scripts": {
50 | "test": "echo \"Error: no test specified\" && exit 1"
51 | },
52 | "version": "1.1.0"
53 | }
54 |
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/src/bluetooth.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { ConnectStatus, SuccessCallbackEvent, ErrorCallbackEvent, SuccessApiThen, ErrorApiCatch} from './enum.js';
4 | import _ from './tools.js';
5 |
6 | export default class Bluetooth {
7 |
8 | constructor(btmanager) {
9 | this.bm = btmanager;
10 | // 蓝牙适配器是否初始化完成
11 | this.isInitializedAdapter = false;
12 | // 蓝牙适配器是否可用
13 | this.isAvailableAdapter = false;
14 | // 初始化蓝牙适配器
15 | this.openAndListenBluetoothAdapter().then(__=>__).catch(__=>__);
16 | // 扫描到的设备
17 | this.scanDevices = [];
18 | }
19 |
20 | /**
21 | * 打开和监听蓝牙适配器
22 | *
23 | * @return Promise对象
24 | */
25 | openAndListenBluetoothAdapter() {
26 |
27 | // 未初始化蓝牙适配器,打开蓝牙适配器
28 | if (!this.isInitializedAdapter) {
29 |
30 | // 监听蓝牙适配器状态
31 | _.api('offBluetoothAdapterStateChange').then(__ => __).catch(__ => __);
32 | _.on('onBluetoothAdapterStateChange','' ,res => {
33 | this.bm.log('onBluetoothAdapterStateChange', res);
34 | if (res.available && !this.isAvailableAdapter) {
35 | this.isAvailableAdapter = true;
36 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_PowerOn);
37 | } else if (!res.available) {
38 | this.isAvailableAdapter = false;
39 | // 支付宝小程序当蓝牙适配器关闭,再次进行蓝牙操作需要重新打开,微信只需要打开一次就行
40 | _.getAppPlatform() === 'ant' && (this.isInitializedAdapter = false);
41 | this.bm.connectStatus = ConnectStatus.disconnected;
42 | this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_PowerOff);
43 | }
44 | });
45 |
46 | // 先关闭再打开蓝牙适配器,避免出现某些机型打开无效的情况
47 | return _.api('closeBluetoothAdapter')
48 | .then(__ => {
49 | // 打开蓝牙适配器
50 | return _.api('openBluetoothAdapter')
51 | }).then(res => {
52 | this.bm.log('openBluetoothAdapter success', res);
53 | this.isInitializedAdapter = true;
54 | this.isAvailableAdapter = true;
55 | return Promise.resolve();
56 | }).catch(e => {
57 | this.bm.log('openBluetoothAdapter fail', e);
58 | this.isInitializedAdapter = false;
59 | this.isAvailableAdapter = false;
60 | this.bm.connectStatus = ConnectStatus.disconnected;
61 | return Promise.reject(e);
62 | })
63 | } else {
64 | return Promise.resolve();
65 | }
66 | }
67 |
68 | /**
69 | * 扫描外设
70 | *
71 | * @param {object} options 扫描参数
72 | * @property {array} services 主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备
73 | * @property {boolean} allowDuplicatesKey 是否允许重复上报设备
74 | * @property {number} interval 上报新设备的间隔,默认为0
75 | * @property {number} timeout 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。默认1500ms,-1表示无限超时
76 | * @property {string} deviceName 通过蓝牙名称过滤,需要匹配的设备名称
77 | * @property {string} containName 通过蓝牙名称过滤,需要包含的设备名称
78 | *
79 | * @return Promise对象
80 | *
81 | * @discussion 开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。
82 | * 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。
83 | * 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。
84 | *
85 | * @see registerDidDiscoverDevice
86 | */
87 | scanDevice(options) {
88 |
89 | // 解构参数
90 | let { services, allowDuplicatesKey, interval, timeout, deviceName, containName} = options;
91 |
92 | // 打开和监听蓝牙适配器
93 | return this.openAndListenBluetoothAdapter()
94 | .then(__ => {
95 | // 清空存储的设备
96 | this.scanDevices = [];
97 | // 销毁扫描延时
98 | this.destoryTimer();
99 | // 设置扫描超时
100 | this.scanTimeoutTimer = timeout!==-1 ? setTimeout(() => {
101 | this.stopScan();
102 | if (this.scanDevices.length === 0) { // 扫描超时
103 | this.bm.log('startBluetoothDevicesDiscovery fail ' , 'timeout');
104 | this.callBackDiscoverDevice(null, ErrorCallbackEvent.Error_DiscoverDevice_CB_Timeout, true);
105 | } else {// 扫描时间结束
106 | this.callBackDiscoverDevice(null, SuccessCallbackEvent.Success_DiscoverDevice_CB_ScanDone, false);
107 | }
108 | }, timeout || 15000) : null;
109 | // 开始扫描
110 | return _.api('startBluetoothDevicesDiscovery', '' ,{
111 | services,
112 | allowDuplicatesKey,
113 | interval
114 | })
115 | }).then(res => {
116 | this.bm.log('startBluetoothDevicesDiscovery success', res);
117 | // 取消设备监听,仅支付宝小程序有效
118 | _.api('offBluetoothDeviceFound').then(__ => __).catch(__ => __);
119 | // 监听扫描到外设
120 | _.on('onBluetoothDeviceFound','', res => {
121 | // this.bm.log('onBluetoothDeviceFound' , res);
122 | let devices = res.devices || res;
123 | // 过滤、格式化、存储、上报设备
124 | for (let device of devices) {
125 | if (Array.isArray(device)) device = devices[0][0];
126 | // 信号强度为127表示RSSI不可用
127 | if (device.RSSI === 127) return;
128 | // 匹配名称,过滤设备
129 | let name = device.name || device.deviceName;
130 | device.name = name;
131 | if (deviceName && (!name || name !== deviceName)) return;
132 | if (containName && (!name || !~name.indexOf(containName))) return;
133 | // 格式化广播数据
134 | if (typeof device.advertisData !== 'string') device.advertisData = _.ab2str(device.advertisData);
135 | // 上报设备
136 | this.callBackDiscoverDevice(device, SuccessCallbackEvent.Success_DiscoverDevice_CB_Discover, false);
137 | // 更新不重复记录设备
138 | for (let v of this.scanDevices) {
139 | if (v.deviceId === device.deviceId) {
140 | Object.assign(v, device);
141 | return;
142 | }
143 | }
144 | // 存储新设备
145 | this.scanDevices.push(device);
146 | }
147 | })
148 | return Promise.resolve(SuccessApiThen.Success_Scan);
149 | }).catch(e => {
150 | this.bm.log('startBluetoothDevicesDiscovery fail', e);
151 | if (e.code === 12 || e.code === 10001) {
152 | return Promise.reject(ErrorApiCatch.Error_Scan_PowerOff);
153 | } else if (e.code === 10012 || e.code === 10004) {
154 | return Promise.reject(ErrorApiCatch.Error_Scan_NoService);
155 | } else {
156 | return Promise.reject(ErrorApiCatch.Error_Scan_Failed);
157 | }
158 | })
159 | }
160 |
161 | /**
162 | * 停止扫描
163 | *
164 | * @return Promise对象
165 | *
166 | * @discussion 停止扫描,取消超时延时。
167 | */
168 | stopScan() {
169 | // 销毁扫描延时
170 | this.destoryTimer();
171 | // 取消设备监听,仅支付宝小程序有效
172 | _.api('offBluetoothDeviceFound').then(__ => __).catch(__ => __);
173 | // 停止扫描
174 | return _.api('stopBluetoothDevicesDiscovery')
175 | .then(res => {
176 | this.bm.log('stopBluetoothDevicesDiscovery success', res);
177 | return Promise.resolve(SuccessApiThen.Success_StopScan);
178 | }).catch(e => {
179 | this.bm.log('stopBluetoothDevicesDiscovery fail', e);
180 | if (e.code === 12 || e.code === 10001) {
181 | return Promise.reject(ErrorApiCatch.Error_StopScan_PowerOff);
182 | } else {
183 | return Promise.reject(ErrorApiCatch.Error_StopScan_Failed);
184 | }
185 | })
186 | }
187 |
188 | /**
189 | * 连接外设
190 | *
191 | * @param {object} device 指定连接的外设对象,从registerDidDiscoverDevice注册的回调中得到
192 | * @param {number} timeout 连接超时时间,毫秒,默认15000ms,支付宝小程序无效
193 | *
194 | * @return Promise对象
195 | *
196 | * @discussion 连接指定的外设,需要传入外设对象。
197 | * 注意实现返回对象的then和catch方法,监听接口是否调用成功。
198 | */
199 | connect(device , timeout) {
200 | // 判断是否已经连接
201 | if (this.bm.connectStatus !== ConnectStatus.disconnected) {
202 | this.bm.logwarn('connect fail', 'Is already connected');
203 | return Promise.reject(ErrorApiCatch.Error_Connect_AlreadyConnected);
204 | }
205 | // 判断设备id是否为空
206 | if (_.isEmpty(device.deviceId)) {
207 | return Promise.reject(ErrorApiCatch.Error_Connect_EmptyId);
208 | }
209 |
210 | this.bm.deviceInfo = device;
211 | this.bm.connectStatus = ConnectStatus.connecting;
212 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Connecting);
213 | let deviceId = device.deviceId;
214 |
215 | // 打开和监听蓝牙适配器
216 | return this.openAndListenBluetoothAdapter()
217 | .then(__ => {
218 | // 连接设备
219 | return _.api('createBLEConnection', 'connectBLEDevice', {
220 | deviceId ,
221 | timeout: timeout || 15000 ,
222 | })
223 | }).then( res => {
224 | this.bm.log('connectBLEDevice success', res);
225 | // 取消蓝牙连接状态监听,仅支付宝小程序有效
226 | _.api('offBLEConnectionStateChanged').then(__ => __).catch(__ => __);
227 | // 蓝牙连接状态监听
228 | _.on('onBLEConnectionStateChange', 'onBLEConnectionStateChanged',(res) => {
229 | this.bm.log('onBLEConnectionStateChange', res);
230 | if (!res.connected && this.bm.connectStatus !== ConnectStatus.disconnected) {
231 | this.bm.connectStatus = ConnectStatus.disconnected;
232 | if (res.errorCode === 0) {
233 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Stop);
234 | } else if (res.errorCode === 10003) {
235 | this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected);
236 | } else if (_.getAppPlatform() === 'ant'){
237 | this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected);
238 | }
239 | }
240 | })
241 |
242 | // 获取设备所有服务
243 | _.api('getBLEDeviceServices', '', { deviceId })
244 | .then(res => {
245 | this.bm.log('getBLEDeviceServices success', res);
246 | // 存储所有服务promise
247 | let sPromises = [];
248 | // 获取所有服务的所有特征
249 | device.services = res.services.map(server => {
250 | let sUUID = server.uuid || server.serviceId;
251 | let sPromise = _.api('getBLEDeviceCharacteristics', '', {
252 | deviceId,
253 | serviceId: sUUID
254 | })
255 | sPromises.push(sPromise);
256 | return { serviceId: sUUID };
257 | })
258 | return Promise.all(sPromises);
259 | }).then(res => {
260 | this.bm.log('getBLEDeviceCharacteristics success', res);
261 | device.services = res.map((v, i) => {
262 | let service = device.services[i];
263 | service.characteristics = v.characteristics;
264 | return service;
265 | })
266 | // 获取特征成功之后才算连接成功
267 | this.bm.deviceInfo = device;
268 | this.bm.connectStatus = ConnectStatus.connected;
269 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Connected);
270 | }).catch(e => {
271 | this.bm.log('api connecting error', e);
272 | // 出现错误断开蓝牙,避免出现已连接成功未找到服务或者特征出错时,再次连接状态不正确
273 | _.api('closeBLEConnection', 'disconnectBLEDevice', {
274 | deviceId: this.bm.deviceInfo.deviceId
275 | }).then(__ => __).catch(__ => __);
276 | this.bm.connectStatus = ConnectStatus.disconnected;
277 | this.callBackConnectStatus(e);
278 | })
279 |
280 | // 开始连接接口调用成功
281 | return Promise.resolve(SuccessApiThen.Success_Connect);
282 | }).catch(e => {
283 | this.bm.log('api connect error', e);
284 | // 未知错误,直接报连接失败
285 | if (e.code === 100000) e = ErrorCallbackEvent.Error_ConnectStatus_CB_ConnectFail;
286 | this.bm.connectStatus = ConnectStatus.disconnected;
287 | this.callBackConnectStatus(e);
288 | if (e.code === 12 || e.code === 10001) {
289 | return Promise.reject(ErrorApiCatch.Error_Connect_PowerOff);
290 | } else if (~e.message.indexOf('超时') || (e.errMsg && ~e.errMsg.indexOf('time out'))){
291 | return Promise.reject(ErrorApiCatch.Error_Connect_Timeout);
292 | } else {
293 | return Promise.reject(ErrorApiCatch.Error_Connect_Failed);
294 | }
295 | })
296 | }
297 |
298 | /**
299 | * 断开连接
300 | *
301 | * @return Promise对象
302 | */
303 | disconnect() {
304 |
305 | this.bm.connectStatus === ConnectStatus.connected &&
306 | _.api('closeBLEConnection','disconnectBLEDevice',{
307 | deviceId: this.bm.deviceInfo.deviceId
308 | }).then(res => {
309 | this.bm.log('closeBLEConnection success', res);
310 | }).catch(e => {
311 | this.bm.log('closeBLEConnection fail', e);
312 | })
313 |
314 | _.api('closeBluetoothAdapter')
315 | .then(res => {
316 | this.bm.log('closeBluetoothAdapter success', res);
317 | this.isInitializedAdapter = false;
318 | if (this.bm.connectStatus !== ConnectStatus.disconnected) {
319 | this.bm.connectStatus = ConnectStatus.disconnected;
320 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Stop);
321 | };
322 | this.bm.connectStatus = ConnectStatus.disconnected;
323 | }).catch(e => {
324 | this.bm.log('closeBluetoothAdapter fail', e);
325 | })
326 |
327 | return Promise.resolve(SuccessApiThen.Success_Disconnect);
328 | }
329 |
330 | /**
331 | * 读特征值
332 | *
333 | * @param {object} params 参数
334 | * @property {string} suuid 特征对应的服务uuid
335 | * @property {string} cuuid 写入特征uuid
336 | *
337 | * @discussion 读某个服务下的某个特征值。
338 | */
339 | read(params) {
340 | if (this.bm.connectStatus === ConnectStatus.connected) {
341 | let { suuid, cuuid } = params;
342 | return _.api('readBLECharacteristicValue', '', {
343 | deviceId: this.bm.deviceInfo.deviceId,
344 | serviceId: suuid,
345 | characteristicId: cuuid,
346 | }).then(res => {
347 | this.bm.log('readBLECharacteristicValue success', res);
348 | return Promise.resolve(SuccessApiThen.Success_Read);
349 | }).catch(e => {
350 | this.bm.log('readBLECharacteristicValue fail', e);
351 | if (e.code === 10007) {
352 | return Promise.reject(ErrorApiCatch.Error_Read_NotSupport);
353 | } else if (e.code === 10004) {
354 | return Promise.reject(ErrorApiCatch.Error_Read_NoService);
355 | } else if (e.code === 10005) {
356 | return Promise.reject(ErrorApiCatch.Error_Read_NoCharacteristic);
357 | } else {
358 | return Promise.reject(ErrorApiCatch.Error_Read_Failed);
359 | }
360 | })
361 | } else {
362 | return Promise.reject(ErrorApiCatch.Error_Read_NotConnected);
363 | }
364 | }
365 |
366 | /**
367 | * 向蓝牙模块写入数据
368 | *
369 | * @param {object} params 参数
370 | * @property {string} suuid 特征对应的服务uuid
371 | * @property {string} cuuid 写入特征uuid
372 | * @property {Hex string} value 16进制字符串
373 | *
374 | * @return Promise对象
375 | *
376 | * @discussion 向蓝牙模块写入数据。
377 | */
378 | write(params) {
379 | let {suuid , cuuid , value} = params;
380 | if (this.bm.connectStatus === ConnectStatus.connected) {
381 | if (_.getAppPlatform() === 'wx') {
382 | if (typeof value === 'string') {
383 | value = _.str2ab(value);
384 | } else {
385 | value = typedArrayToArrayBuffer(value);
386 | }
387 | } else if (typeof value !== 'string'){
388 | value = _.ab2str(value);
389 | }
390 | this.bm.log('writeCmdToDevice', _.ab2str(value));
391 | return _.api('writeBLECharacteristicValue', '', {
392 | deviceId: this.bm.deviceInfo.deviceId,
393 | serviceId: suuid,
394 | characteristicId: cuuid,
395 | value,
396 | }).then(res => {
397 | this.bm.log('writeBLECharacteristicValue success', res);
398 | return Promise.resolve(SuccessApiThen.Success_Write);
399 | }).catch(e => {
400 | this.bm.log('writeBLECharacteristicValue fail', e);
401 | if (e.code === 10007) {
402 | return Promise.reject(ErrorApiCatch.Error_Write_NotSupport);
403 | } else if (e.code === 10004) {
404 | return Promise.reject(ErrorApiCatch.Error_Write_NoService);
405 | } else if (e.code === 10005) {
406 | return Promise.reject(ErrorApiCatch.Error_Write_NoCharacteristic);
407 | } else {
408 | return Promise.reject(ErrorApiCatch.Error_Write_Failed);
409 | }
410 | })
411 | } else {
412 | return Promise.reject(ErrorApiCatch.Error_Write_NotConnected);
413 | }
414 | }
415 |
416 | /**
417 | * 监听特征值改变
418 | *
419 | * @param {object} params 参数
420 | * @property {string} suuid 特征对应的服务uuid
421 | * @property {string} cuuid 写入特征uuid
422 | * @property {boolean} state 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听
423 | *
424 | * @return Promise对象
425 | *
426 | * @discussion 监听某个特征值变化。
427 | */
428 | notify(params) {
429 | if (this.bm.connectStatus === ConnectStatus.connected) {
430 | let { suuid, cuuid, state } = params;
431 | return _.api('notifyBLECharacteristicValueChange', '', {
432 | deviceId: this.bm.deviceInfo.deviceId,
433 | serviceId: suuid,
434 | characteristicId: cuuid,
435 | state
436 | }).then(res => {
437 | this.bm.log('readBLECharacteristicValue success', res);
438 | return Promise.resolve(SuccessApiThen.Success_Notify);
439 | }).catch(e => {
440 | this.bm.log('readBLECharacteristicValue fail', e);
441 | if (e.code === 10007) {
442 | return Promise.reject(ErrorApiCatch.Error_Notify_NotSupport);
443 | } else if (e.code === 10004) {
444 | return Promise.reject(ErrorApiCatch.Error_Notify_NoService);
445 | } else if (e.code === 10005) {
446 | return Promise.reject(ErrorApiCatch.Error_Notify_NoCharacteristic);
447 | } else {
448 | return Promise.reject(ErrorApiCatch.Error_Notify_Failed);
449 | }
450 | })
451 | } else {
452 | return Promise.reject(ErrorApiCatch.Error_Notify_NotConnected);
453 | }
454 | }
455 |
456 | /**
457 | * 注册状态改变回调
458 | *
459 | * @param {function} cb 回调函数
460 | *
461 | * @discussion 连接状态发生改变时,回调此方法。
462 | */
463 | registerDidUpdateConnectStatus(cb) {
464 | this._didUpdateStatusCB = cb;
465 | }
466 |
467 | /**
468 | * 注册发现外设回调
469 | *
470 | * @param {function} cb 回调函数
471 | *
472 | * @discussion 当扫描到设备时回调
473 | */
474 | registerDidDiscoverDevice(cb) {
475 | this._didDiscoverDeviceCB = cb;
476 | }
477 |
478 | /**
479 | * 注册特征值改变回调
480 | *
481 | * @param {function} cb 回调函数
482 | *
483 | * @discussion 当监听的特征值改变时回调
484 | */
485 | registerDidUpdateValueForCharacteristic(cb) {
486 | this._didUpdateValueCB = cb;
487 | _.api('offBLECharacteristicValueChange').then(__ => __).catch(__ => __);
488 | _.on('onBLECharacteristicValueChange', '' ,characteristic => {
489 | if (typeof characteristic.value === 'string') {
490 | this._didUpdateValueCB(characteristic);
491 | } else {
492 | characteristic.value = _.ab2str(characteristic.value);
493 | this._didUpdateValueCB(characteristic);
494 | }
495 | })
496 | }
497 |
498 | /**
499 | * 回调蓝牙连接状态
500 | */
501 | callBackConnectStatus(status) {
502 | this._didUpdateStatusCB && this._didUpdateStatusCB({
503 | ...status,
504 | device:this.bm.deviceInfo,
505 | connectStatus:this.bm.connectStatus
506 | });
507 | }
508 |
509 | /**
510 | * 回调发现外设
511 | *
512 | * @param device 扫描到的设备
513 | */
514 | callBackDiscoverDevice(device , event , timeout) {
515 | this._didDiscoverDeviceCB && this._didDiscoverDeviceCB(
516 | device ? {
517 | ...event,
518 | timeout,
519 | device,
520 | } : {
521 | ...event,
522 | timeout,
523 | }
524 | )
525 | }
526 |
527 | /**
528 | * 销毁延时
529 | */
530 | destoryTimer() {
531 | this.scanTimeoutTimer && clearTimeout(this.scanTimeoutTimer);
532 | this.scanTimeoutTimer = null;
533 | }
534 | }
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/src/btmanager.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { Log } from './extends.js';
4 | import { ConnectStatus } from './enum.js';
5 | import Bluetooth from './bluetooth.js';
6 |
7 | export class BTManager {
8 |
9 | /**
10 | * 构造函数
11 | *
12 | * @param {object} config 配置
13 | * @property {boolean} debug 是否开启打印调试,默认不开启
14 | *
15 | * @discussion 单例模式。
16 | */
17 | constructor(config = {}) {
18 | if (!BTManager.instance) {
19 | BTManager.instance = this;
20 | // 初始化log
21 | Log.call(BTManager.prototype);
22 | // 初始化设备信息
23 | this.deviceInfo = {};
24 | // 初始化连接状态
25 | this.connectStatus = ConnectStatus.disconnected;
26 | // 初始化蓝牙管理器
27 | this._bt = new Bluetooth(this);
28 | }
29 | // 合并配置
30 | Object.assign(BTManager.instance, config);
31 | return BTManager.instance;
32 | }
33 |
34 | /**
35 | * 扫描外设
36 | *
37 | * @param {object} options 扫描参数
38 | * @property {array} services 主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备
39 | * @property {boolean} allowDuplicatesKey 是否允许重复上报设备
40 | * @property {number} interval 上报新设备的间隔,默认为0
41 | * @property {number} timeout 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。默认15000ms,-1表示无限超时
42 | * @property {string} deviceName 通过蓝牙名称过滤,需要匹配的设备名称
43 | * @property {string} containName 通过蓝牙名称过滤,需要包含的设备名称
44 | *
45 | * @return Promise对象
46 | *
47 | * @discussion 开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。
48 | * 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。
49 | * 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。
50 | *
51 | * @see registerDidDiscoverDevice
52 | */
53 | scan( options = {
54 | services: [],
55 | allowDuplicatesKey: false,
56 | interval : 0,
57 | timeout: 15000,
58 | deviceName: '',
59 | containName:''
60 | })
61 | {
62 | return this._bt.scanDevice(options);
63 | }
64 |
65 | /**
66 | * 停止扫描
67 | *
68 | * @return Promise对象
69 | *
70 | * @discussion 停止扫描,取消超时延时。
71 | */
72 | stopScan() {
73 | return this._bt.stopScan();
74 | }
75 |
76 | /**
77 | * 连接外设
78 | *
79 | * @param {object} device 指定连接的外设对象,从registerDidDiscoverDevice注册的回调中得到
80 | * @param {number} timeout 连接超时时间,毫秒,默认15000ms,支付宝小程序无效
81 | *
82 | * @return Promise对象
83 | *
84 | * @discussion 连接指定的外设,需要传入外设对象。
85 | * 注意实现返回对象的then和catch方法,监听接口是否调用成功。
86 | */
87 | connect(device , timeout) {
88 | if (!device) throw new Error('device is undefiend');
89 | return this._bt.connect(device);
90 | }
91 |
92 | /**
93 | * 断开连接
94 | *
95 | * @return Promise对象
96 | */
97 | disconnect() {
98 | return this._bt.disconnect();
99 | }
100 |
101 | /**
102 | * 读特征值
103 | *
104 | * @param {object} params 参数
105 | * @property {string} suuid 特征对应的服务uuid
106 | * @property {string} cuuid 特征uuid
107 | *
108 | * @return Promise对象
109 | *
110 | * @discussion 读某个服务下的某个特征值。
111 | */
112 | read(params = {
113 | suuid: '',
114 | cuuid: ''
115 | })
116 | {
117 | return this._bt.read(params);
118 | }
119 |
120 | /**
121 | * 向蓝牙模块写入数据
122 | *
123 | * @param {object} params 参数
124 | * @property {string} suuid 特征对应的服务uuid
125 | * @property {string} cuuid 特征uuid
126 | * @property {Hex string} value 16进制字符串
127 | *
128 | * @return Promise对象
129 | *
130 | * @discussion 向蓝牙模块写入数据。
131 | */
132 | write(params = {
133 | suuid: '',
134 | cuuid: '',
135 | value: ''
136 | })
137 | {
138 | return this._bt.write(params);
139 | }
140 |
141 | /**
142 | * 监听特征值改变
143 | *
144 | * @param {object} params 参数
145 | * @property {string} suuid 特征对应的服务uuid
146 | * @property {string} cuuid 特征uuid
147 | * @property {boolean} state 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听
148 | *
149 | * @return Promise对象
150 | *
151 | * @discussion 监听某个特征值变化。
152 | */
153 | notify(params = {
154 | suuid: '',
155 | cuuid: '',
156 | state: true,
157 | })
158 | {
159 | return this._bt.notify(params);
160 | }
161 |
162 | /**
163 | * 注册状态改变回调
164 | *
165 | * @param {function} cb 回调函数
166 | *
167 | * @discussion 连接状态发生改变时,回调此方法。
168 | */
169 | registerDidUpdateConnectStatus(cb) {
170 | if (typeof cb !== 'function') throw new TypeError('connectStatus callback expect function');
171 | this._bt.registerDidUpdateConnectStatus(cb);
172 | }
173 |
174 | /**
175 | * 注册发现外设回调
176 | *
177 | * @param {function} cb 回调函数
178 | *
179 | * @discussion 当扫描到设备时回调,或者达到超时时间回调。
180 | */
181 | registerDidDiscoverDevice(cb) {
182 | if (typeof cb !== 'function') throw new TypeError('discoverDevice callback expect function');
183 | this._bt.registerDidDiscoverDevice(cb);
184 | }
185 |
186 | /**
187 | * 注册特征值改变回调
188 | *
189 | * @param {function} cb 回调函数
190 | *
191 | * @discussion 当监听的特征值改变时回调,或者读特征值时回调。
192 | */
193 | registerDidUpdateValueForCharacteristic(cb) {
194 | if (typeof cb !== 'function') throw new TypeError('updateValueForCharacteristic callback expect function');
195 | this._bt.registerDidUpdateValueForCharacteristic(cb);
196 | }
197 |
198 | }
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/src/enum.js:
--------------------------------------------------------------------------------
1 |
2 | // 连接状态
3 | export const ConnectStatus = {
4 | // 未连接或连接断开,允许连接
5 | disconnected: 0,
6 | // 正在连接,不允许再连接
7 | connecting: 1,
8 | // 已连接,不允许再连接
9 | connected: 2,
10 | }
11 |
12 | // 发现外设回调和连接状态改变回调成功事件
13 | export const SuccessCallbackEvent = {
14 | Success_DiscoverDevice_CB_Discover: { code: 210, message: '发现外设' },
15 | Success_DiscoverDevice_CB_ScanDone: { code: 211, message: '扫描完成' },
16 | Success_ConnectStatus_CB_PowerOn: { code: 220, message: '蓝牙打开' },
17 | Success_ConnectStatus_CB_Connecting: { code: 221, message: '正在连接' },
18 | Success_ConnectStatus_CB_Connected: { code: 222, message: '连接成功' },
19 | Success_ConnectStatus_CB_Stop: { code: 223, message: '断开成功' },
20 | }
21 |
22 | // 发现外设回调和连接状态改变回调失败事件
23 | export const ErrorCallbackEvent = {
24 | Error_DiscoverDevice_CB_Timeout: { code: 410, message: '扫描超时' },
25 | Error_ConnectStatus_CB_PowerOff: { code: 420, message: '蓝牙关闭' },
26 | Error_ConnectStatus_CB_ConnectFail: { code: 421, message: '连接失败' },
27 | Error_ConnectStatus_CB_Disconnected: { code: 422, message: '连接断开' },
28 | }
29 |
30 | // 接口调用成功事件
31 | export const SuccessApiThen = {
32 | Success_Scan: { code: 2010, message: '扫描接口成功调用' },
33 | Success_StopScan: { code: 2020, message: '停止扫描接口成功调用' },
34 | Success_Connect: { code: 2030, message: '连接接口成功调用' },
35 | Success_Disconnect: { code: 2040, message: '断开接口成功调用' },
36 | Success_Read: { code: 2050, message: '读特征值接口成功调用' },
37 | Success_Write: { code: 2060, message: '写入数据接口成功调用' },
38 | Success_Notify: { code: 2070, message: '监听特征值接口成功调用' },
39 | }
40 |
41 | // 接口调用失败事件
42 | export const ErrorApiCatch = {
43 | // 基础库版本低
44 | Error_Low_Version: { code: 4000, message: '当前基础库版本低,请更新微信版本' },
45 | // Api: scan
46 | Error_Scan_Failed: { code: 4010, message: '扫描错误:扫描失败' },
47 | Error_Scan_PowerOff: { code: 4011, message: '扫描错误:蓝牙被关闭' },
48 | Error_Scan_NoService: { code: 4012, message: '扫描错误:没有找到指定服务' },
49 | // Api: stopScan
50 | Error_StopScan_Failed: { code: 4020, message: '停止扫描错误:停止扫描失败' },
51 | Error_StopScan_PowerOff: { code: 4021, message: '停止扫描错误:蓝牙被关闭' },
52 | // Api: connect
53 | Error_Connect_Failed: { code: 4030, message: '连接错误:连接失败' },
54 | Error_Connect_PowerOff: { code: 4031, message: '连接错误:蓝牙被关闭' },
55 | Error_Connect_AlreadyConnected: { code: 4032, message: '连接错误:已经连接或正在连接' },
56 | Error_Connect_Timeout: { code: 4033, message: '连接错误:连接超时' },
57 | Error_Connect_EmptyId: { code: 4034, message: '连接错误:设备id不能为空' },
58 | // Api: disconnect
59 | Error_Disconnect_Failed: { code: 4040, message: '断开错误:断开失败' },
60 | // Api: read
61 | Error_Read_Failed: { code: 4050, message: '读特征值错误:读特征值失败'},
62 | Error_Read_NotConnected: { code: 4051, message: '读特征值错误:蓝牙未连接' },
63 | Error_Read_NotSupport: { code: 4052, message: '读特征值错误:当前特征不支持读操作' },
64 | Error_Read_NoService: { code: 4053, message: '读特征值错误:没有找到指定服务' },
65 | Error_Read_NoCharacteristic: { code: 4054, message: '读特征值错误:没有找到指定特征值' },
66 | // Api: write
67 | Error_Write_Failed: { code: 4060, message: '写入数据错误:写入数据失败'},
68 | Error_Write_NotConnected: { code: 4061, message: '写入数据错误:蓝牙未连接' },
69 | Error_Write_NotSupport: { code: 4062, message: '写入数据错误:当前特征不支持写操作' },
70 | Error_Write_NoService: { code: 4063, message: '写入数据错误:没有找到指定服务' },
71 | Error_Write_NoCharacteristic: { code: 4064, message: '写入数据错误:没有找到指定特征值' },
72 | // Api: notify
73 | Error_Notify_Failed: { code: 4070, message: '监听特征值错误:监听特征值错误失败' },
74 | Error_Notify_NotConnected: { code: 4071, message: '监听特征值错误:蓝牙未连接' },
75 | Error_Notify_NotSupport: { code: 4072, message: '监听特征值错误:当前特征不支持监听操作' },
76 | Error_Notify_NoService: { code: 4073, message: '监听特征值错误:没有找到指定服务' },
77 | Error_Notify_NoCharacteristic: { code: 4074, message: '监听特征值错误:没有找到指定特征值' },
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/src/extends.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export function Log() {
4 |
5 | this.log = function(identifier, msg='') {
6 | this.debug && console.log(`BLE:(${identifier}):` , msg);
7 | }
8 |
9 | this.loginfo = function(identifier,msg) {
10 | this.debug && console.info(`BLE:(${identifier}):` ,msg);
11 | }
12 |
13 | this.logwarn = function (identifier, msg) {
14 | this.debug && console.warn(`BLE:(${identifier}):`, msg);
15 | }
16 |
17 | this.logerror = function (identifier, msg) {
18 | this.debug && console.error(`BLE:(${identifier}):`, msg);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/src/promisify.js:
--------------------------------------------------------------------------------
1 |
2 | // 判断是微信小程序还是支付宝小程序
3 | let ii, mini;
4 | try {
5 | ii = wx;
6 | mini = 'wx';
7 | } catch (e) {
8 | ii = my;
9 | mini = 'ant';
10 | }
11 |
12 | import { ErrorApiCatch} from './enum.js';
13 |
14 | // 微信系统错误
15 | const ERROR_WX = {
16 | NOT_INIT: { code: 10000, message: '未初始化蓝牙适配器' },
17 | NOT_AVAILABLE: { code: 10001, message: '当前蓝牙适配器不可用' },
18 | NO_DEVICE: { code: 10002, message: '没有找到指定设备' },
19 | CONNECTION_FAIL: { code: 10003, message: '连接失败' },
20 | NO_SERVICE: { code: 10004, message: '没有找到指定服务' },
21 | NO_CHARACTERISTIC: { code: 10005, message: '没有找到指定特征值' },
22 | NO_CONNECTION: { code: 10006, message: '当前连接已断开' },
23 | PROPERTY_NOT_SUPPORT: { code: 10007, message: '当前特征值不支持此操作' },
24 | SYSTEM_ERROR: { code: 10008, message: '系统异常' },
25 | SYSTEM_NOT_SUPPORT: { code: 10009, message: 'Android 系统版本低于 4.3 不支持 BLE' },
26 | OPERATE_TIMEOUT: { code: 10012, message: '操作超时' },
27 | INVALID_PARAMETER: { code: 10013, message: '无效参数' },
28 | ALREADY_CONNECTED: { code: -1 , message: '蓝牙已连接,不能再连接' },
29 | UN_KNOWN: { code: 100000,message: '未知' },
30 | }
31 |
32 | // 支付宝系统错误
33 | const ERROR_ANT = {
34 | POWER_OFF: { code: 12, message: '蓝牙未打开' },
35 | LOST_SERVICE: { code: 13, message: '与系统服务的链接暂时丢失' },
36 | UNAUTH_BLE: { code: 14, message: '未授权支付宝使用蓝牙功能' },
37 | UNKNOWN_ERROR: { code: 15, message: '未知错误' },
38 | NOT_INIT: { code: 10000, message: '未初始化蓝牙适配器' },
39 | NOT_AVAILABLE: { code: 10001, message: '当前蓝牙适配器不可用' },
40 | NO_DEVICE: { code: 10002, message: '没有找到指定设备' },
41 | CONNECTION_FAIL: { code: 10003, message: '连接失败' },
42 | NO_SERVICE: { code: 10004, message: '没有找到指定服务' },
43 | NO_CHARACTERISTIC: { code: 10005, message: '没有找到指定特征值' },
44 | NO_CONNECTION: { code: 10006, message: '当前连接已断开' },
45 | PROPERTY_NOT_SUPPORT: { code: 10007, message: '当前特征值不支持此操作' },
46 | SYSTEM_ERROR: { code: 10008, message: '系统异常' },
47 | SYSTEM_NOT_SUPPORT: { code: 10009, message: 'Android 系统版本低于 4.3 不支持 BLE' },
48 | SYMBOL_UNFOUND: { code: 10010, message: '没有找到指定描述符' },
49 | DEVICE_ID_INVALID: { code: 10011, message: '设备 id 不可用/为空' },
50 | SERVICE_ID_INVALID: { code: 10012, message: '服务 id 不可用/为空' },
51 | CHARACTERISTIC_ID_INVALID: { code: 10013, message: '特征 id 不可用/为空' },
52 | CMD_FORMAT_ERROR: { code: 10014, message: '发送的数据为空或格式错误' },
53 | OPERATION_TIMEOUT: { code: 10015, message: '操作超时' },
54 | LACK_PARAMETER: { code: 10016, message: '缺少参数' },
55 | WRITE_ERROR: { code: 10017, message: '写入特征值失败' },
56 | READ_ERROR: { code: 10018, message: '读取特征值失败' },
57 | }
58 |
59 | const ERROR_TYPES = mini==='wx' ? ERROR_WX : ERROR_ANT;
60 |
61 | /**
62 | * 获取错误类型
63 | */
64 | function getErrorType(code) {
65 | for (let key of Object.keys(ERROR_TYPES)) {
66 | if (code === ERROR_TYPES[key].code) {
67 | return ERROR_TYPES[key];
68 | }
69 | }
70 | return ERROR_TYPES.UN_KNOWN;
71 | }
72 |
73 | /**
74 | * 获取小程序平台 wx||ant
75 | */
76 | export function getAppPlatform() {
77 | return mini;
78 | }
79 |
80 | /**
81 | * 重写异步API
82 | *
83 | * @param {string} fn1 方法名1
84 | * @param {string} fn2 方法名2
85 | * @param {object} options 可选参数
86 | *
87 | * @return Promise对象
88 | */
89 | export function api(fn1, fn2, options) {
90 | let func = ii[fn1] || ii[fn2];
91 | return new Promise((resolve, reject) => {
92 | let params = {
93 | success(res) {
94 | resolve(res);
95 | },
96 | fail(res) {
97 | if (res.errorMessage) {
98 | reject({
99 | code: res.error,
100 | message: res.errorMessage
101 | });
102 | } else {
103 | reject({
104 | ...getErrorType(res.errCode),
105 | errMsg: res.errMsg
106 | });
107 | }
108 | },
109 | complete(res) {
110 | //...
111 | }
112 | }
113 | if (options) {
114 | params = Object.assign(params, options);
115 | }
116 | if (func) {
117 | func(params);
118 | } else {
119 | reject(ErrorApiCatch.Error_Low_Version);
120 | }
121 | })
122 | }
123 |
124 | /**
125 | * 重写回调API
126 | *
127 | * @param {string} fn1 方法名1
128 | * @param {string} fn2 方法名2
129 | * @param {function} cb 回调函数
130 | */
131 | export function on(fn1, fn2, cb) {
132 | let func = ii[fn1] || ii[fn2];
133 | if (func) {
134 | if (mini === 'wx') {
135 | func(cb);
136 | } else {
137 | func({
138 | success: cb,
139 | })
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/example/antdemo/wx-ant-ble/src/tools.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import { getAppPlatform , api , on} from './promisify.js';
5 |
6 | /**
7 | * 判断字符串是否为空或者空格
8 | */
9 | function isEmpty(str = '') {
10 | return !str || str == '' || str.replace(/(^\s*)|(\s*$)/g, "") == "";
11 | }
12 |
13 | /**
14 | * 判断是否为null或者未定义
15 | */
16 | function isNullOrUndefined(obj) {
17 | return obj === undefined || obj === null;
18 | }
19 |
20 | /**
21 | * ArrayBuffer类型转换为16进制字符串
22 | */
23 | function ab2str(buffer) {
24 | return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
25 | }
26 |
27 | /**
28 | * 字符串转为ArrayBuffer对象,参数为字符串
29 | */
30 | function str2ab(str) {
31 | var buf = new ArrayBuffer(str.length / 2);
32 | var bufView = new Uint8Array(buf);
33 | for (var i = 0; i < str.length; i += 2) {
34 | bufView[parseInt(i / 2)] = char2Hex(str.charCodeAt(i)) << 4 | char2Hex(str.charCodeAt(i + 1));
35 | }
36 | return buf;
37 | }
38 |
39 | /**
40 | * 字符转十六进制
41 | */
42 | function char2Hex(bChar) {
43 | if ((bChar >= 0x30) && (bChar <= 0x39)) { // 数字
44 | bChar -= 0x30;
45 | } else if ((bChar >= 0x41) && (bChar <= 0x46)) { // 大写字母
46 | bChar -= 0x37;
47 | } else if ((bChar >= 0x61) && (bChar <= 0x66)) { // 小写字母
48 | bChar -= 0x57;
49 | } else {
50 | bChar = 0xff;
51 | }
52 | return bChar;
53 | }
54 |
55 | /**
56 | * TypedArray转为ArrayBuffer
57 | */
58 | function typedArray2ArrayBuffer(pbuff) {
59 | let buffer = new ArrayBuffer(pbuff.byteLength)
60 | let uInit8 = new Uint8Array(buffer)
61 | uInit8.set(pbuff);
62 | return buffer;
63 | }
64 |
65 | /**
66 | * 获取异或校验数值
67 | *
68 | * @param p 参与运算的字符数组指针
69 | * @param len 参与运算的字符数组长度
70 | */
71 | function createXOR(b, p, len) {
72 | let i = 0;
73 | let ckc = 0;
74 | for (; i < len; i++) {
75 | ckc = ckc ^ b[p + i];
76 | }
77 | return ckc;
78 | }
79 |
80 | /**
81 | * UUID128位转换为16位
82 | *
83 | * @param uuid128 128位的uuid
84 | */
85 | function uuid128to16(uuid128) {
86 | let arr = uuid128.split('-');
87 | if (arr.length === 5 && arr[4] === '00805F9B34FB') {
88 | return arr[0].slice(3);
89 | } else {
90 | return uuid128;
91 | }
92 | }
93 |
94 | export default {
95 | getAppPlatform,
96 | api,
97 | on,
98 | isEmpty,
99 | isNullOrUndefined,
100 | ab2str,
101 | str2ab,
102 | typedArray2ArrayBuffer,
103 | createXOR,
104 | uuid128to16
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------
/example/wxdemo/app.js:
--------------------------------------------------------------------------------
1 | //app.js
2 | App({
3 | onLaunch: function () {
4 | // 展示本地存储能力
5 | var logs = wx.getStorageSync('logs') || []
6 | logs.unshift(Date.now())
7 | wx.setStorageSync('logs', logs)
8 |
9 | // 登录
10 | wx.login({
11 | success: res => {
12 | // 发送 res.code 到后台换取 openId, sessionKey, unionId
13 | }
14 | })
15 | // 获取用户信息
16 | wx.getSetting({
17 | success: res => {
18 | if (res.authSetting['scope.userInfo']) {
19 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
20 | wx.getUserInfo({
21 | success: res => {
22 | // 可以将 res 发送给后台解码出 unionId
23 | this.globalData.userInfo = res.userInfo
24 |
25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
26 | // 所以此处加入 callback 以防止这种情况
27 | if (this.userInfoReadyCallback) {
28 | this.userInfoReadyCallback(res)
29 | }
30 | }
31 | })
32 | }
33 | }
34 | })
35 | },
36 | globalData: {
37 | userInfo: null
38 | }
39 | })
--------------------------------------------------------------------------------
/example/wxdemo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/home/home",
4 | "pages/index/index",
5 | "pages/logs/logs"
6 |
7 | ],
8 | "window": {
9 | "backgroundTextStyle": "light",
10 | "navigationBarBackgroundColor": "#fff",
11 | "navigationBarTitleText": "WeChat",
12 | "navigationBarTextStyle": "black"
13 | }
14 | }
--------------------------------------------------------------------------------
/example/wxdemo/app.wxss:
--------------------------------------------------------------------------------
1 | /**app.wxss**/
2 | page {
3 | width: 100%;
4 | height: 100%;
5 | display: flex;
6 | background: #f7f7f7;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 |
11 | .absTopLeft0 {
12 | position: absolute;
13 | top: 0rpx;
14 | left: 0rpx;
15 | }
16 |
17 | /* 居中显示 */
18 | .flex-center{
19 | display: flex;
20 | align-items: center;
21 | justify-content: center;
22 | }
23 |
24 | /* 水平居中显示 */
25 | .flex-row-center{
26 | display: flex;
27 | flex-direction: row;
28 | align-items: center;
29 | }
30 |
31 | /* 垂直居中显示 */
32 | .flex-column-center{
33 | display: flex;
34 | flex-direction: column;
35 | align-items: center;
36 | }
37 |
38 | /* 垂直靠左显示 */
39 | .flex-column-left{
40 | display: flex;
41 | flex-direction: column;
42 | align-items: flex-start;
43 | }
44 |
45 | /* 水平靠左显示 */
46 | .flex-row-left{
47 | display: flex;
48 | flex-direction: row;
49 | align-items: flex-start;
50 | justify-content: center
51 | }
52 |
53 | /* 垂直居下显示 */
54 | .flex-column-end{
55 | display: flex;
56 | flex-direction: column;
57 | justify-content: flex-end;
58 | }
59 |
60 | ::-webkit-scrollbar {
61 | width: 0;
62 | height: 0;
63 | color: transparent;
64 | }
--------------------------------------------------------------------------------
/example/wxdemo/images/arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/example/wxdemo/images/arrow.png
--------------------------------------------------------------------------------
/example/wxdemo/miniprogram_npm/wx-ant-ble/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["index.js","src/btmanager.js","src/extends.js","src/enum.js","src/bluetooth.js","src/tools.js","src/promisify.js"],"names":[],"mappings":";;;;;;;AAAA;AACA;AACA;AACA,ACHA;ADIA,ACHA;ADIA,ACHA;ADIA,ACHA,ACHA;AFOA,ACHA,ACHA;AFOA,ACHA,ACHA;AFOA,ACHA,AENA,ADGA;AFOA,ACHA,AENA,ADGA;AFOA,ACHA,AENA,ADGA;AFOA,AIZA,AHSA,AENA,ADGA;AFOA,AIZA,AHSA,AENA,ADGA;AELA,AHSA,AENA,ADGA;AELA,AHSA,AENA,ADGA,AGTA;ADIA,AHSA,AENA,ADGA,AGTA;ADIA,AHSA,AENA,ADGA,AGTA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,ADGA,AIZA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AENA,AGTA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA,ADGA;ADIA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA,AKfA;AFOA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA,AHSA;AGRA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"index.js","sourcesContent":["\nvar __TEMP__ = require('./src/btmanager.js');var BTManager = __TEMP__['BTManager'];\nvar __TEMP__ = require('./src/enum.js');var ConnectStatus = __TEMP__['ConnectStatus'];var SuccessCallbackEvent = __TEMP__['SuccessCallbackEvent'];var ErrorCallbackEvent = __TEMP__['ErrorCallbackEvent'];var SuccessApiThen = __TEMP__['SuccessApiThen'];var ErrorApiCatch = __TEMP__['ErrorApiCatch'];\nconst Version = '1.1.0';\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });Object.defineProperty(exports, 'Version', { enumerable: true, get: function() { return Version; } });Object.defineProperty(exports, 'BTManager', { enumerable: true, get: function() { return BTManager; } });Object.defineProperty(exports, 'ConnectStatus', { enumerable: true, get: function() { return ConnectStatus; } });Object.defineProperty(exports, 'SuccessCallbackEvent', { enumerable: true, get: function() { return SuccessCallbackEvent; } });Object.defineProperty(exports, 'ErrorCallbackEvent', { enumerable: true, get: function() { return ErrorCallbackEvent; } });Object.defineProperty(exports, 'SuccessApiThen', { enumerable: true, get: function() { return SuccessApiThen; } });Object.defineProperty(exports, 'ErrorApiCatch', { enumerable: true, get: function() { return ErrorApiCatch; } });\n\n\n\n\n\n\n\n","\n\nvar __TEMP__ = require('./extends.js');var Log = __TEMP__['Log'];\nvar __TEMP__ = require('./enum.js');var ConnectStatus = __TEMP__['ConnectStatus'];\nvar __TEMP__ = require('./bluetooth.js');var Bluetooth = __REQUIRE_DEFAULT__(__TEMP__);\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.BTManager = class BTManager {\n\n /**\n * 构造函数\n *\n * @param {object} config 配置\n * @property {boolean} debug 是否开启打印调试,默认不开启\n * \n * @discussion 单例模式。\n */\n constructor(config = {}) {\n if (!BTManager.instance) {\n BTManager.instance = this;\n // 初始化log\n Log.call(BTManager.prototype);\n // 初始化设备信息\n this.deviceInfo = {};\n // 初始化连接状态\n this.connectStatus = ConnectStatus.disconnected;\n // 初始化蓝牙管理器\n this._bt = new Bluetooth(this);\n }\n // 合并配置\n Object.assign(BTManager.instance, config);\n return BTManager.instance;\n }\n\n /**\n * 扫描外设\n *\n * @param {object} options 扫描参数\n * @property {array} services 主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备\n * @property {boolean} allowDuplicatesKey 是否允许重复上报设备\n * @property {number} interval 上报新设备的间隔,默认为0\n * @property {number} timeout 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。默认15000ms,-1表示无限超时\n * @property {string} deviceName 通过蓝牙名称过滤,需要匹配的设备名称\n * @property {string} containName 通过蓝牙名称过滤,需要包含的设备名称\n * \n * @return Promise对象\n * \n * @discussion 开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。\n * 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。\n * 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。\n * \n * @see registerDidDiscoverDevice\n */\n scan( options = { \n services: [],\n allowDuplicatesKey: false,\n interval : 0,\n timeout: 15000,\n deviceName: '', \n containName:''\n }) \n {\n return this._bt.scanDevice(options);\n }\n\n /**\n * 停止扫描\n *\n * @return Promise对象\n * \n * @discussion 停止扫描,取消超时延时。\n */\n stopScan() {\n return this._bt.stopScan();\n }\n\n /**\n * 连接外设\n * \n * @param {object} device 指定连接的外设对象,从registerDidDiscoverDevice注册的回调中得到\n * @param {number} timeout 连接超时时间,毫秒,默认15000ms,支付宝小程序无效\n * \n * @return Promise对象\n * \n * @discussion 连接指定的外设,需要传入外设对象。\n * 注意实现返回对象的then和catch方法,监听接口是否调用成功。\n */\n connect(device , timeout) {\n if (!device) throw new Error('device is undefiend');\n return this._bt.connect(device);\n }\n\n /**\n * 断开连接\n * \n * @return Promise对象\n */\n disconnect() {\n return this._bt.disconnect();\n }\n\n /**\n * 读特征值\n * \n * @param {object} params 参数\n * @property {string} suuid 特征对应的服务uuid\n * @property {string} cuuid 特征uuid\n * \n * @return Promise对象\n * \n * @discussion 读某个服务下的某个特征值。\n */\n read(params = {\n suuid: '',\n cuuid: ''\n }) \n {\n return this._bt.read(params);\n }\n\n /**\n * 向蓝牙模块写入数据\n * \n * @param {object} params 参数\n * @property {string} suuid 特征对应的服务uuid\n * @property {string} cuuid 特征uuid\n * @property {Hex string} value 16进制字符串 \n * \n * @return Promise对象\n * \n * @discussion 向蓝牙模块写入数据。\n */\n write(params = {\n suuid: '',\n cuuid: '',\n value: ''\n })\n {\n return this._bt.write(params);\n }\n\n /**\n * 监听特征值改变\n * \n * @param {object} params 参数\n * @property {string} suuid 特征对应的服务uuid\n * @property {string} cuuid 特征uuid\n * @property {boolean} state 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听 \n * \n * @return Promise对象\n * \n * @discussion 监听某个特征值变化。\n */\n notify(params = {\n suuid: '',\n cuuid: '',\n state: true,\n }) \n {\n return this._bt.notify(params);\n }\n\n /**\n * 注册状态改变回调\n *\n * @param {function} cb 回调函数\n * \n * @discussion 连接状态发生改变时,回调此方法。\n */\n registerDidUpdateConnectStatus(cb) {\n if (typeof cb !== 'function') throw new TypeError('connectStatus callback expect function');\n this._bt.registerDidUpdateConnectStatus(cb);\n }\n\n /**\n * 注册发现外设回调\n *\n * @param {function} cb 回调函数\n * \n * @discussion 当扫描到设备时回调,或者达到超时时间回调。\n */\n registerDidDiscoverDevice(cb) {\n if (typeof cb !== 'function') throw new TypeError('discoverDevice callback expect function');\n this._bt.registerDidDiscoverDevice(cb);\n }\n\n /**\n * 注册\b特征值改变回调\n *\n * @param {function} cb 回调函数\n * \n * @discussion 当监听的特征值改变时回调,或者读特征值时回调。\n */\n registerDidUpdateValueForCharacteristic(cb) {\n if (typeof cb !== 'function') throw new TypeError('updateValueForCharacteristic callback expect function');\n this._bt.registerDidUpdateValueForCharacteristic(cb);\n }\n\n};","\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.Log = function Log() {\n\n this.log = function(identifier, msg='') {\n this.debug && console.log(`BLE:(${identifier}):` , msg);\n }\n\n this.loginfo = function(identifier,msg) {\n this.debug && console.info(`BLE:(${identifier}):` ,msg);\n }\n\n this.logwarn = function (identifier, msg) {\n this.debug && console.warn(`BLE:(${identifier}):`, msg);\n }\n\n this.logerror = function (identifier, msg) {\n this.debug && console.error(`BLE:(${identifier}):`, msg);\n }\n\n};\n","\n// 连接状态\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });var ConnectStatus = exports.ConnectStatus = {\n // 未连接或连接断开,允许连接\n disconnected: 0,\n // 正在连接,不允许再连接\n connecting: 1,\n // 已连接,不允许再连接\n connected: 2,\n};\n\n// 发现外设回调和连接状态改变回调成功事件\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });var SuccessCallbackEvent = exports.SuccessCallbackEvent = {\n Success_DiscoverDevice_CB_Discover: { code: 210, message: '发现外设' },\n Success_DiscoverDevice_CB_ScanDone: { code: 211, message: '扫描完成' },\n Success_ConnectStatus_CB_PowerOn: { code: 220, message: '蓝牙打开' },\n Success_ConnectStatus_CB_Connecting: { code: 221, message: '正在连接' },\n Success_ConnectStatus_CB_Connected: { code: 222, message: '连接成功' },\n Success_ConnectStatus_CB_Stop: { code: 223, message: '断开成功' },\n};\n\n// 发现外设回调和连接状态改变回调失败事件\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });var ErrorCallbackEvent = exports.ErrorCallbackEvent = {\n Error_DiscoverDevice_CB_Timeout: { code: 410, message: '扫描超时' },\n Error_ConnectStatus_CB_PowerOff: { code: 420, message: '蓝牙关闭' },\n Error_ConnectStatus_CB_ConnectFail: { code: 421, message: '连接失败' },\n Error_ConnectStatus_CB_Disconnected: { code: 422, message: '连接断开' },\n};\n\n// 接口调用成功事件\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });var SuccessApiThen = exports.SuccessApiThen = {\n Success_Scan: { code: 2010, message: '\b扫描接口成功调用' },\n Success_StopScan: { code: 2020, message: '\b停止扫描接口成功调用' },\n Success_Connect: { code: 2030, message: '\b连接接口成功调用' },\n Success_Disconnect: { code: 2040, message: '\b断开接口成功调用' },\n Success_Read: { code: 2050, message: '\b读特征值接口成功调用' },\n Success_Write: { code: 2060, message: '\b写入数据接口成功调用' },\n Success_Notify: { code: 2070, message: '\b监听特征值接口成功调用' },\n};\n\n// 接口调用失败事件\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });var ErrorApiCatch = exports.ErrorApiCatch = {\n // 基础库版本低\n Error_Low_Version: { code: 4000, message: '当前基础库版本低,请更新微信版本' },\n // Api: scan\n Error_Scan_Failed: { code: 4010, message: '扫描错误:扫描失败' },\n Error_Scan_PowerOff: { code: 4011, message: '扫描错误:蓝牙被关闭' },\n Error_Scan_NoService: { code: 4012, message: '扫描错误:没有找到指定服务' },\n // Api: stopScan\n Error_StopScan_Failed: { code: 4020, message: '停止扫描错误:停止扫描失败' },\n Error_StopScan_PowerOff: { code: 4021, message: '停止扫描错误:蓝牙被关闭' },\n // Api: connect\n Error_Connect_Failed: { code: 4030, message: '连接错误:连接失败' },\n Error_Connect_PowerOff: { code: 4031, message: '连接错误:蓝牙被关闭' },\n Error_Connect_AlreadyConnected: { code: 4032, message: '连接错误:已经连接或正在连接' },\n Error_Connect_Timeout: { code: 4033, message: '连接错误:连接超时' },\n Error_Connect_EmptyId: { code: 4034, message: '连接错误:设备id不能为空' },\n // Api: disconnect\n Error_Disconnect_Failed: { code: 4040, message: '断开错误:断开失败' },\n // Api: read\n Error_Read_Failed: { code: 4050, message: '\b读特征值错误:读特征值失败'},\n Error_Read_NotConnected: { code: 4051, message: '读特征值错误:蓝牙未连接' },\n Error_Read_NotSupport: { code: 4052, message: '读特征值错误:当前特征不支持读操作' },\n Error_Read_NoService: { code: 4053, message: '读特征值错误:没有找到指定服务' },\n Error_Read_NoCharacteristic: { code: 4054, message: '读特征值错误:没有找到指定特征值' },\n // Api: write\n Error_Write_Failed: { code: 4060, message: '\b写入数据错误:写入数据失败'},\n Error_Write_NotConnected: { code: 4061, message: '写入数据错误:蓝牙未连接' },\n Error_Write_NotSupport: { code: 4062, message: '写入数据错误:当前特征不支持写操作' },\n Error_Write_NoService: { code: 4063, message: '写入数据错误:没有找到指定服务' },\n Error_Write_NoCharacteristic: { code: 4064, message: '写入数据错误:没有找到指定特征值' },\n // Api: notify\n Error_Notify_Failed: { code: 4070, message: '\b监听特征值错误:监听特征值错误失败' },\n Error_Notify_NotConnected: { code: 4071, message: '监听特征值错误:蓝牙未连接' },\n Error_Notify_NotSupport: { code: 4072, message: '监听特征值错误:当前特征不支持监听操作' },\n Error_Notify_NoService: { code: 4073, message: '监听特征值错误:没有找到指定服务' },\n Error_Notify_NoCharacteristic: { code: 4074, message: '监听特征值错误:没有找到指定特征值' },\n};\n\n","\n\nvar __TEMP__ = require('./enum.js');var ConnectStatus = __TEMP__['ConnectStatus'];var SuccessCallbackEvent = __TEMP__['SuccessCallbackEvent'];var ErrorCallbackEvent = __TEMP__['ErrorCallbackEvent'];var SuccessApiThen = __TEMP__['SuccessApiThen'];var ErrorApiCatch = __TEMP__['ErrorApiCatch'];\nvar __TEMP__ = require('./tools.js');var _ = __REQUIRE_DEFAULT__(__TEMP__);\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = class Bluetooth {\n\n constructor(btmanager) {\n this.bm = btmanager;\n // 蓝牙适配器是否初始化完成\n this.isInitializedAdapter = false;\n // 蓝牙适配器是否可用\n this.isAvailableAdapter = false;\n // 初始化蓝牙适配器\n this.openAndListenBluetoothAdapter().then(__=>__).catch(__=>__);\n // 扫描到的设备\n this.scanDevices = [];\n }\n\n /**\n * 打开和监听蓝牙适配器\n * \n * @return Promise对象\n */\n openAndListenBluetoothAdapter() {\n\n // 未初始化蓝牙适配器,打开蓝牙适配器\n if (!this.isInitializedAdapter) {\n\n // 监听蓝牙适配器状态\n _.api('offBluetoothAdapterStateChange').then(__ => __).catch(__ => __);\n _.on('onBluetoothAdapterStateChange','' ,res => {\n this.bm.log('onBluetoothAdapterStateChange', res);\n if (res.available && !this.isAvailableAdapter) {\n this.isAvailableAdapter = true;\n this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_PowerOn);\n } else if (!res.available) {\n this.isAvailableAdapter = false;\n // 支付宝小程序当蓝牙适配器关闭,再次进行蓝牙操作需要重新打开,微信只需要打开一次就行\n _.getAppPlatform() === 'ant' && (this.isInitializedAdapter = false);\n this.bm.connectStatus = ConnectStatus.disconnected;\n this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_PowerOff);\n }\n });\n\n // 先关闭再打开蓝牙适配器,避免出现某些机型打开无效的情况\n return _.api('closeBluetoothAdapter')\n .then(__ => {\n // 打开蓝牙适配器\n return _.api('openBluetoothAdapter')\n }).then(res => {\n this.bm.log('openBluetoothAdapter success', res);\n this.isInitializedAdapter = true;\n this.isAvailableAdapter = true;\n return Promise.resolve();\n }).catch(e => {\n this.bm.log('openBluetoothAdapter fail', e);\n this.isInitializedAdapter = false;\n this.isAvailableAdapter = false;\n this.bm.connectStatus = ConnectStatus.disconnected;\n return Promise.reject(e);\n })\n } else {\n return Promise.resolve();\n }\n }\n\n /**\n * 扫描外设\n *\n * @param {object} options 扫描参数\n * @property {array} services 主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备\n * @property {boolean} allowDuplicatesKey 是否允许重复上报设备\n * @property {number} interval 上报新设备的间隔,默认为0\n * @property {number} timeout 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。默认1500ms,-1表示无限超时\n * @property {string} deviceName 通过蓝牙名称过滤,需要匹配的设备名称\n * @property {string} containName 通过蓝牙名称过滤,需要包含的设备名称\n * \n * @return Promise对象\n * \n * @discussion 开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。\n * 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。\n * 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。\n * \n * @see registerDidDiscoverDevice\n */\n scanDevice(options) {\n\n // 解构参数\n let { services, allowDuplicatesKey, interval, timeout, deviceName, containName} = options;\n\n // 打开和监听蓝牙适配器\n return this.openAndListenBluetoothAdapter()\n .then(__ => {\n // 清空存储的设备\n this.scanDevices = [];\n // 销毁扫描延时\n this.destoryTimer();\n // 设置扫描超时\n this.scanTimeoutTimer = timeout!==-1 ? setTimeout(() => {\n this.stopScan();\n if (this.scanDevices.length === 0) { // 扫描超时\n this.bm.log('startBluetoothDevicesDiscovery fail ' , 'timeout');\n this.callBackDiscoverDevice(null, ErrorCallbackEvent.Error_DiscoverDevice_CB_Timeout, true);\n } else {// 扫描时间结束\n this.callBackDiscoverDevice(null, SuccessCallbackEvent.Success_DiscoverDevice_CB_ScanDone, false);\n }\n }, timeout || 15000) : null;\n // 开始扫描\n return _.api('startBluetoothDevicesDiscovery', '' ,{\n services,\n allowDuplicatesKey,\n interval\n })\n }).then(res => {\n this.bm.log('startBluetoothDevicesDiscovery success', res);\n // 取消设备监听,仅支付宝小程序有效\n _.api('offBluetoothDeviceFound').then(__ => __).catch(__ => __);\n // 监听扫描到外设\n _.on('onBluetoothDeviceFound','', res => {\n // this.bm.log('onBluetoothDeviceFound' , res);\n let devices = res.devices || res;\n // 过滤、格式化、存储、上报设备\n for (let device of devices) {\n if (Array.isArray(device)) device = devices[0][0];\n // 信号强度为127表示RSSI不可用\n if (device.RSSI === 127) return;\n // 匹配名称,过滤设备\n let name = device.name || device.deviceName;\n device.name = name;\n if (deviceName && (!name || name !== deviceName)) return;\n if (containName && (!name || !~name.indexOf(containName))) return;\n // 格式化广播数据\n if (typeof device.advertisData !== 'string') device.advertisData = _.ab2str(device.advertisData);\n // 上报设备\n this.callBackDiscoverDevice(device, SuccessCallbackEvent.Success_DiscoverDevice_CB_Discover, false);\n // 更新不重复记录设备\n for (let v of this.scanDevices) {\n if (v.deviceId === device.deviceId) {\n Object.assign(v, device);\n return;\n }\n }\n // 存储新设备\n this.scanDevices.push(device);\n }\n })\n return Promise.resolve(SuccessApiThen.Success_Scan);\n }).catch(e => {\n this.bm.log('startBluetoothDevicesDiscovery fail', e);\n if (e.code === 12 || e.code === 10001) {\n return Promise.reject(ErrorApiCatch.Error_Scan_PowerOff);\n } else if (e.code === 10012 || e.code === 10004) {\n return Promise.reject(ErrorApiCatch.Error_Scan_NoService);\n } else {\n return Promise.reject(ErrorApiCatch.Error_Scan_Failed);\n }\n })\n }\n\n /**\n * 停止扫描\n *\n * @return Promise对象\n * \n * @discussion 停止扫描,取消超时延时。\n */\n stopScan() {\n // 销毁扫描延时\n this.destoryTimer();\n // 取消设备监听,仅支付宝小程序有效\n _.api('offBluetoothDeviceFound').then(__ => __).catch(__ => __);\n // 停止扫描\n return _.api('stopBluetoothDevicesDiscovery')\n .then(res => {\n this.bm.log('stopBluetoothDevicesDiscovery success', res);\n return Promise.resolve(SuccessApiThen.Success_StopScan);\n }).catch(e => {\n this.bm.log('stopBluetoothDevicesDiscovery fail', e);\n if (e.code === 12 || e.code === 10001) {\n return Promise.reject(ErrorApiCatch.Error_StopScan_PowerOff);\n } else {\n return Promise.reject(ErrorApiCatch.Error_StopScan_Failed);\n }\n })\n }\n\n /**\n * 连接外设\n * \n * @param {object} device 指定连接的外设对象,从registerDidDiscoverDevice注册的回调中得到\n * @param {number} timeout 连接超时时间,毫秒,默认15000ms,支付宝小程序无效\n * \n * @return Promise对象\n * \n * @discussion 连接指定的外设,需要传入外设对象。\n * 注意实现返回对象的then和catch方法,监听接口是否调用成功。\n */\n connect(device , timeout) {\n // 判断是否已经连接\n if (this.bm.connectStatus !== ConnectStatus.disconnected) {\n this.bm.logwarn('connect fail', 'Is already connected');\n return Promise.reject(ErrorApiCatch.Error_Connect_AlreadyConnected);\n }\n // 判断设备id是否为空\n if (_.isEmpty(device.deviceId)) {\n return Promise.reject(ErrorApiCatch.Error_Connect_EmptyId);\n }\n\n this.bm.deviceInfo = device;\n this.bm.connectStatus = ConnectStatus.connecting;\n this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Connecting);\n let deviceId = device.deviceId;\n\n // 打开和监听蓝牙适配器\n return this.openAndListenBluetoothAdapter()\n .then(__ => {\n // 连接设备\n return _.api('createBLEConnection', 'connectBLEDevice', {\n deviceId ,\n timeout: timeout || 15000 ,\n })\n }).then( res => {\n this.bm.log('connectBLEDevice success', res);\n // 取消蓝牙连接状态监听,仅支付宝小程序有效\n _.api('offBLEConnectionStateChanged').then(__ => __).catch(__ => __);\n // 蓝牙连接状态监听\n _.on('onBLEConnectionStateChange', 'onBLEConnectionStateChanged',(res) => {\n this.bm.log('onBLEConnectionStateChange', res);\n if (!res.connected && this.bm.connectStatus !== ConnectStatus.disconnected) {\n this.bm.connectStatus = ConnectStatus.disconnected;\n if (res.errorCode === 0) {\n this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Stop);\n } else if (res.errorCode === 10003) {\n this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected);\n } else if (_.getAppPlatform() === 'ant'){\n this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected);\n }\n }\n })\n\n // 获取设备所有服务\n _.api('getBLEDeviceServices', '', { deviceId })\n .then(res => {\n this.bm.log('getBLEDeviceServices success', res);\n // 存储所有服务promise\n let sPromises = [];\n // 获取所有服务的所有特征\n device.services = res.services.map(server => {\n let sUUID = server.uuid || server.serviceId;\n let sPromise = _.api('getBLEDeviceCharacteristics', '', {\n deviceId,\n serviceId: sUUID\n })\n sPromises.push(sPromise);\n return { serviceId: sUUID };\n })\n return Promise.all(sPromises);\n }).then(res => {\n this.bm.log('getBLEDeviceCharacteristics success', res);\n device.services = res.map((v, i) => {\n let service = device.services[i];\n service.characteristics = v.characteristics;\n return service;\n })\n // 获取特征成功之后才算连接成功\n this.bm.deviceInfo = device;\n this.bm.connectStatus = ConnectStatus.connected;\n this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Connected);\n }).catch(e => {\n this.bm.log('api connecting error', e);\n // 出现错误断开蓝牙,避免出现已连接成功未找到服务或者特征出错时,再次连接状态不正确\n _.api('closeBLEConnection', 'disconnectBLEDevice', {\n deviceId: this.bm.deviceInfo.deviceId\n }).then(__ => __).catch(__ => __);\n this.bm.connectStatus = ConnectStatus.disconnected;\n this.callBackConnectStatus(e);\n })\n\n // 开始连接接口调用成功\n return Promise.resolve(SuccessApiThen.Success_Connect);\n }).catch(e => {\n this.bm.log('api connect error', e);\n // 未知错误,直接报连接失败\n if (e.code === 100000) e = ErrorCallbackEvent.Error_ConnectStatus_CB_ConnectFail;\n this.bm.connectStatus = ConnectStatus.disconnected;\n this.callBackConnectStatus(e);\n if (e.code === 12 || e.code === 10001) {\n return Promise.reject(ErrorApiCatch.Error_Connect_PowerOff);\n } else if (~e.message.indexOf('超时') || (e.errMsg && ~e.errMsg.indexOf('time out'))){\n return Promise.reject(ErrorApiCatch.Error_Connect_Timeout);\n } else {\n return Promise.reject(ErrorApiCatch.Error_Connect_Failed);\n }\n })\n }\n\n /**\n * 断开连接\n * \n * @return Promise对象\n */\n disconnect() {\n\n this.bm.connectStatus === ConnectStatus.connected && \n _.api('closeBLEConnection','disconnectBLEDevice',{\n deviceId: this.bm.deviceInfo.deviceId\n }).then(res => {\n this.bm.log('closeBLEConnection success', res);\n }).catch(e => {\n this.bm.log('closeBLEConnection fail', e);\n })\n \n _.api('closeBluetoothAdapter')\n .then(res => {\n this.bm.log('closeBluetoothAdapter success', res);\n this.isInitializedAdapter = false;\n if (this.bm.connectStatus !== ConnectStatus.disconnected) {\n this.bm.connectStatus = ConnectStatus.disconnected;\n this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Stop);\n };\n this.bm.connectStatus = ConnectStatus.disconnected;\n }).catch(e => {\n this.bm.log('closeBluetoothAdapter fail', e);\n })\n\n return Promise.resolve(SuccessApiThen.Success_Disconnect);\n }\n\n /**\n * 读特征值\n * \n * @param {object} params 参数\n * @property {string} suuid 特征对应的服务uuid\n * @property {string} cuuid 写入特征uuid\n * \n * @discussion 读某个服务下的某个特征值。\n */\n read(params) {\n if (this.bm.connectStatus === ConnectStatus.connected) {\n let { suuid, cuuid } = params;\n return _.api('readBLECharacteristicValue', '', {\n deviceId: this.bm.deviceInfo.deviceId,\n serviceId: suuid,\n characteristicId: cuuid,\n }).then(res => {\n this.bm.log('readBLECharacteristicValue success', res);\n return Promise.resolve(SuccessApiThen.Success_Read);\n }).catch(e => {\n this.bm.log('readBLECharacteristicValue fail', e);\n if (e.code === 10007) {\n return Promise.reject(ErrorApiCatch.Error_Read_NotSupport);\n } else if (e.code === 10004) {\n return Promise.reject(ErrorApiCatch.Error_Read_NoService);\n } else if (e.code === 10005) {\n return Promise.reject(ErrorApiCatch.Error_Read_NoCharacteristic);\n } else {\n return Promise.reject(ErrorApiCatch.Error_Read_Failed);\n }\n })\n } else {\n return Promise.reject(ErrorApiCatch.Error_Read_NotConnected);\n }\n }\n\n /**\n * 向蓝牙模块写入数据\n * \n * @param {object} params 参数\n * @property {string} suuid 特征对应的服务uuid\n * @property {string} cuuid 写入特征uuid\n * @property {Hex string} value 16进制字符串 \n * \n * @return Promise对象\n * \n * @discussion 向蓝牙模块写入数据。\n */\n write(params) {\n let {suuid , cuuid , value} = params;\n if (this.bm.connectStatus === ConnectStatus.connected) {\n if (_.getAppPlatform() === 'wx') {\n if (typeof value === 'string') {\n value = _.str2ab(value);\n } else {\n value = typedArrayToArrayBuffer(value);\n }\n } else if (typeof value !== 'string'){\n value = _.ab2str(value);\n }\n this.bm.log('writeCmdToDevice', _.ab2str(value));\n return _.api('writeBLECharacteristicValue', '', {\n deviceId: this.bm.deviceInfo.deviceId,\n serviceId: suuid,\n characteristicId: cuuid,\n value,\n }).then(res => {\n this.bm.log('writeBLECharacteristicValue success', res);\n return Promise.resolve(SuccessApiThen.Success_Write);\n }).catch(e => {\n this.bm.log('writeBLECharacteristicValue fail', e);\n if (e.code === 10007) {\n return Promise.reject(ErrorApiCatch.Error_Write_NotSupport);\n } else if (e.code === 10004) {\n return Promise.reject(ErrorApiCatch.Error_Write_NoService);\n } else if (e.code === 10005) {\n return Promise.reject(ErrorApiCatch.Error_Write_NoCharacteristic);\n } else {\n return Promise.reject(ErrorApiCatch.Error_Write_Failed);\n }\n })\n } else {\n return Promise.reject(ErrorApiCatch.Error_Write_NotConnected);\n }\n }\n\n /**\n * 监听特征值改变\n * \n * @param {object} params 参数\n * @property {string} suuid 特征对应的服务uuid\n * @property {string} cuuid 写入特征uuid\n * @property {boolean} state 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听 \n * \n * @return Promise对象\n * \n * @discussion 监听某个特征值变化。\n */\n notify(params) {\n if (this.bm.connectStatus === ConnectStatus.connected) {\n let { suuid, cuuid, state } = params;\n return _.api('notifyBLECharacteristicValueChange', '', {\n deviceId: this.bm.deviceInfo.deviceId,\n serviceId: suuid,\n characteristicId: cuuid,\n state\n }).then(res => {\n this.bm.log('readBLECharacteristicValue success', res);\n return Promise.resolve(SuccessApiThen.Success_Notify);\n }).catch(e => {\n this.bm.log('readBLECharacteristicValue fail', e);\n if (e.code === 10007) {\n return Promise.reject(ErrorApiCatch.Error_Notify_NotSupport);\n } else if (e.code === 10004) {\n return Promise.reject(ErrorApiCatch.Error_Notify_NoService);\n } else if (e.code === 10005) {\n return Promise.reject(ErrorApiCatch.Error_Notify_NoCharacteristic);\n } else {\n return Promise.reject(ErrorApiCatch.Error_Notify_Failed);\n }\n })\n } else {\n return Promise.reject(ErrorApiCatch.Error_Notify_NotConnected);\n }\n }\n\n /**\n * 注册状态改变回调\n *\n * @param {function} cb 回调函数\n * \n * @discussion 连接状态发生改变时,回调此方法。\n */\n registerDidUpdateConnectStatus(cb) {\n this._didUpdateStatusCB = cb;\n }\n\n /**\n * 注册发现外设回调\n *\n * @param {function} cb 回调函数\n * \n * @discussion 当扫描到设备时回调\n */\n registerDidDiscoverDevice(cb) {\n this._didDiscoverDeviceCB = cb;\n }\n\n /**\n * 注册\b特征值改变回调\n *\n * @param {function} cb 回调函数\n * \n * @discussion 当监听的特征值改变时回调\n */\n registerDidUpdateValueForCharacteristic(cb) {\n this._didUpdateValueCB = cb;\n _.api('offBLECharacteristicValueChange').then(__ => __).catch(__ => __);\n _.on('onBLECharacteristicValueChange', '' ,characteristic => {\n if (typeof characteristic.value === 'string') {\n this._didUpdateValueCB(characteristic);\n } else {\n characteristic.value = _.ab2str(characteristic.value);\n this._didUpdateValueCB(characteristic);\n }\n })\n }\n\n /**\n * 回调蓝牙连接状态\n */\n callBackConnectStatus(status) {\n this._didUpdateStatusCB && this._didUpdateStatusCB({\n ...status,\n device:this.bm.deviceInfo,\n connectStatus:this.bm.connectStatus\n });\n }\n\n /**\n * 回调发现外设\n * \n * @param device 扫描到的设备\n */\n callBackDiscoverDevice(device , event , timeout) {\n this._didDiscoverDeviceCB && this._didDiscoverDeviceCB(\n device ? {\n ...event,\n timeout,\n device,\n } : {\n ...event,\n timeout,\n }\n )\n }\n\n /**\n * 销毁延时\n */\n destoryTimer() {\n this.scanTimeoutTimer && clearTimeout(this.scanTimeoutTimer);\n this.scanTimeoutTimer = null;\n }\n};","\n\n\nvar __TEMP__ = require('./promisify.js');var getAppPlatform = __TEMP__['getAppPlatform'];var api = __TEMP__['api'];var on = __TEMP__['on'];\n\n/**\n * 判断字符串是否为空或者空格\n */\nfunction isEmpty(str = '') {\n return !str || str == '' || str.replace(/(^\\s*)|(\\s*$)/g, \"\") == \"\";\n}\n\n/**\n * 判断是否为null或者未定义\n */\nfunction isNullOrUndefined(obj) {\n return obj === undefined || obj === null;\n}\n\n/**\n * ArrayBuffer类型转换为16进制字符串\n */\nfunction ab2str(buffer) {\n return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');\n}\n\n/**\n * 字符串转为ArrayBuffer对象,参数为字符串\n */\nfunction str2ab(str) {\n var buf = new ArrayBuffer(str.length / 2);\n var bufView = new Uint8Array(buf);\n for (var i = 0; i < str.length; i += 2) {\n bufView[parseInt(i / 2)] = char2Hex(str.charCodeAt(i)) << 4 | char2Hex(str.charCodeAt(i + 1));\n }\n return buf;\n}\n\n/**\n * 字符转十六进制\n */\nfunction char2Hex(bChar) {\n if ((bChar >= 0x30) && (bChar <= 0x39)) { // 数字\n bChar -= 0x30;\n } else if ((bChar >= 0x41) && (bChar <= 0x46)) { // 大写字母\n bChar -= 0x37;\n } else if ((bChar >= 0x61) && (bChar <= 0x66)) { // 小写字母\n bChar -= 0x57;\n } else {\n bChar = 0xff;\n }\n return bChar;\n}\n\n/**\n * TypedArray转为ArrayBuffer\n */\nfunction typedArray2ArrayBuffer(pbuff) {\n let buffer = new ArrayBuffer(pbuff.byteLength)\n let uInit8 = new Uint8Array(buffer)\n uInit8.set(pbuff);\n return buffer;\n}\n\n/**\n * 获取异或校验数值\n *\n * @param p 参与运算的字符数组指针\n * @param len 参与运算的字符数组长度\n */\nfunction createXOR(b, p, len) {\n let i = 0;\n let ckc = 0;\n for (; i < len; i++) {\n ckc = ckc ^ b[p + i];\n }\n return ckc;\n}\n\n/**\n * UUID128位转换为16位\n * \n * @param uuid128 128位的uuid\n */\nfunction uuid128to16(uuid128) {\n let arr = uuid128.split('-');\n if (arr.length === 5 && arr[4] === '00805F9B34FB') {\n return arr[0].slice(3);\n } else {\n return uuid128;\n }\n}\n\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.default = {\n getAppPlatform,\n api,\n on,\n isEmpty,\n isNullOrUndefined,\n ab2str,\n str2ab,\n typedArray2ArrayBuffer,\n createXOR,\n uuid128to16\n};\n\n\n","\n// 判断是微信小程序还是支付宝小程序 \nlet ii, mini;\ntry {\n ii = wx;\n mini = 'wx';\n} catch (e) {\n ii = my;\n mini = 'ant';\n}\n\nvar __TEMP__ = require('./enum.js');var ErrorApiCatch = __TEMP__['ErrorApiCatch'];\n\n// 微信系统错误\nconst ERROR_WX = {\n NOT_INIT: { code: 10000, message: '未初始化蓝牙适配器' },\n NOT_AVAILABLE: { code: 10001, message: '当前蓝牙适配器不可用' },\n NO_DEVICE: { code: 10002, message: '没有找到指定设备' },\n CONNECTION_FAIL: { code: 10003, message: '连接失败' },\n NO_SERVICE: { code: 10004, message: '没有找到指定服务' },\n NO_CHARACTERISTIC: { code: 10005, message: '没有找到指定特征值' },\n NO_CONNECTION: { code: 10006, message: '当前连接已断开' },\n PROPERTY_NOT_SUPPORT: { code: 10007, message: '当前特征值不支持此操作' },\n SYSTEM_ERROR: { code: 10008, message: '系统异常' },\n SYSTEM_NOT_SUPPORT: { code: 10009, message: 'Android 系统版本低于 4.3 不支持 BLE' },\n OPERATE_TIMEOUT: { code: 10012, message: '操作超时' },\n INVALID_PARAMETER: { code: 10013, message: '无效参数' },\n ALREADY_CONNECTED: { code: -1 , message: '蓝牙已连接,不能再连接' },\n UN_KNOWN: { code: 100000,message: '未知' },\n}\n\n// 支付宝系统错误\nconst ERROR_ANT = {\n POWER_OFF: { code: 12, message: '蓝牙未打开' },\n LOST_SERVICE: { code: 13, message: '与系统服务的链接暂时丢失' },\n UNAUTH_BLE: { code: 14, message: '未授权支付宝使用蓝牙功能' },\n UNKNOWN_ERROR: { code: 15, message: '未知错误' },\n NOT_INIT: { code: 10000, message: '未初始化蓝牙适配器' },\n NOT_AVAILABLE: { code: 10001, message: '当前蓝牙适配器不可用' },\n NO_DEVICE: { code: 10002, message: '没有找到指定设备' },\n CONNECTION_FAIL: { code: 10003, message: '连接失败' },\n NO_SERVICE: { code: 10004, message: '没有找到指定服务' },\n NO_CHARACTERISTIC: { code: 10005, message: '没有找到指定特征值' },\n NO_CONNECTION: { code: 10006, message: '当前连接已断开' },\n PROPERTY_NOT_SUPPORT: { code: 10007, message: '当前特征值不支持此操作' },\n SYSTEM_ERROR: { code: 10008, message: '系统异常' },\n SYSTEM_NOT_SUPPORT: { code: 10009, message: 'Android 系统版本低于 4.3 不支持 BLE' },\n SYMBOL_UNFOUND: { code: 10010, message: '没有找到指定描述符' },\n DEVICE_ID_INVALID: { code: 10011, message: '设备 id 不可用/为空' },\n SERVICE_ID_INVALID: { code: 10012, message: '服务 id 不可用/为空' },\n CHARACTERISTIC_ID_INVALID: { code: 10013, message: '特征 id 不可用/为空' },\n CMD_FORMAT_ERROR: { code: 10014, message: '发送的数据为空或格式错误' },\n OPERATION_TIMEOUT: { code: 10015, message: '操作超时' },\n LACK_PARAMETER: { code: 10016, message: '缺少参数' },\n WRITE_ERROR: { code: 10017, message: '写入特征值失败' },\n READ_ERROR: { code: 10018, message: '读取特征值失败' },\n}\n\nconst ERROR_TYPES = mini==='wx' ? ERROR_WX : ERROR_ANT;\n\n/**\n * 获取错误类型\n */\nfunction getErrorType(code) {\n for (let key of Object.keys(ERROR_TYPES)) {\n if (code === ERROR_TYPES[key].code) {\n return ERROR_TYPES[key];\n }\n }\n return ERROR_TYPES.UN_KNOWN;\n}\n\n/**\n * 获取小程序平台 wx||ant\n */\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.getAppPlatform = function getAppPlatform() {\n return mini;\n};\n\n/**\n * 重写异步API\n * \n * @param {string} fn1 方法名1\n * @param {string} fn2 方法名2\n * @param {object} options 可选参数\n * \n * @return Promise对象\n */\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.api = function api(fn1, fn2, options) {\n let func = ii[fn1] || ii[fn2];\n return new Promise((resolve, reject) => {\n let params = {\n success(res) {\n resolve(res);\n },\n fail(res) {\n if (res.errorMessage) {\n reject({\n code: res.error,\n message: res.errorMessage\n });\n } else {\n reject({\n ...getErrorType(res.errCode),\n errMsg: res.errMsg\n });\n }\n },\n complete(res) {\n //...\n }\n }\n if (options) {\n params = Object.assign(params, options);\n }\n if (func) {\n func(params);\n } else {\n reject(ErrorApiCatch.Error_Low_Version);\n }\n })\n};\n\n/**\n * 重写回调API\n * \n * @param {string} fn1 方法名1\n * @param {string} fn2 方法名2\n * @param {function} cb 回调函数\n */\nif (!exports.__esModule) Object.defineProperty(exports, \"__esModule\", { value: true });exports.on = function on(fn1, fn2, cb) {\n let func = ii[fn1] || ii[fn2];\n if (func) {\n if (mini === 'wx') {\n func(cb);\n } else {\n func({\n success: cb,\n })\n }\n }\n};"]}
--------------------------------------------------------------------------------
/example/wxdemo/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wxdemo",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "wx-ant-ble": {
8 | "version": "1.0.9",
9 | "resolved": "https://registry.npmjs.org/wx-ant-ble/-/wx-ant-ble-1.0.9.tgz",
10 | "integrity": "sha512-R4WQMly8uuRjeklGfK1QE1nfTJU+DOD1UC3yfrHLY1c4f1P5qrBW470j0Accv7v0QT/ZmYeS5zABCsscy2JiNA=="
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/wxdemo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wxdemo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "app.js",
6 | "dependencies": {
7 | "wx-ant-ble": "^1.0.9"
8 | },
9 | "devDependencies": {},
10 | "scripts": {
11 | "test": "echo \"Error: no test specified\" && exit 1"
12 | },
13 | "author": "",
14 | "license": "ISC"
15 | }
16 |
--------------------------------------------------------------------------------
/example/wxdemo/pages/home/home.js:
--------------------------------------------------------------------------------
1 | // pages/home/home.js
2 |
3 | import { BTManager, ConnectStatus } from 'wx-ant-ble';
4 |
5 | Page({
6 |
7 | data: {
8 | // 蓝牙是否连接
9 | connected: false,
10 | // 成功连接的设备
11 | device: {},
12 | // 扫描到的设备
13 | devices:[],
14 | // 设备能够notify的特征
15 | notifyUUIDs: [],
16 | // 设备能够read的特征
17 | readUUIDs: [],
18 | // 设备能够write的特征
19 | writeUUIDs: [],
20 | },
21 |
22 | onLoad: function (options) {
23 | // 初始化蓝牙管理器
24 | this.bt = new BTManager({
25 | debug: true
26 | });
27 | // 注册状态回调
28 | this.bt.registerDidUpdateConnectStatus(this.didUpdateConnectStatus.bind(this));
29 | // 注册发现外设回调
30 | this.bt.registerDidDiscoverDevice(this.didDiscoverDevice.bind(this));
31 | // 注册特征值改变回调
32 | this.bt.registerDidUpdateValueForCharacteristic(this.didUpdateValueForCharacteristic.bind(this));
33 | },
34 |
35 | // 状态改变回调
36 | didUpdateConnectStatus(res) {
37 | console.log('home registerDidUpdateConnectStatus', res);
38 | if (res.connectStatus === ConnectStatus.connected) {
39 | wx.hideLoading();
40 | this.setData({connected: true , device:res.device});
41 | this.parseDeviceUUIDs(res.device);
42 | } else if (res.connectStatus === ConnectStatus.disconnected) {
43 | wx.hideLoading();
44 | wx.showToast({
45 | title: res.message,
46 | icon: 'none'
47 | })
48 | this.setData({ connected: false, notifyUUIDs: [], readUUIDs: [], writeUUIDs:[]});
49 | }
50 | },
51 |
52 | // 发现外设回调
53 | didDiscoverDevice(res) {
54 | console.log('home didDiscoverDevice', res);
55 | if (res.timeout) {
56 | console.log('home didDiscoverDevice', '扫描超时');
57 | wx.showToast({
58 | title: res.message,
59 | icon: 'none'
60 | })
61 | } else {
62 | let device = res.device;
63 | let devices = this.data.devices;
64 | function checkDevice(d, ds) {
65 | for (let v of ds) {
66 | if (v.deviceId === d.deviceId) {
67 | return true;
68 | }
69 | }
70 | return false;
71 | }
72 | if (!checkDevice(device, devices)) {
73 | devices.push(device);
74 | }
75 | this.setData({ devices });
76 | }
77 | },
78 |
79 | // 特征值改变回调
80 | didUpdateValueForCharacteristic(res) {
81 | console.log('home registerDidUpdateValueForCharacteristic', res);
82 | },
83 |
84 | parseDeviceUUIDs(device) {
85 | let { notifyUUIDs, readUUIDs, writeUUIDs } = this.data;
86 | for (let service of device.services) {
87 | for (let char of service.characteristics) {
88 | if (char.properties.notify) {
89 | notifyUUIDs.push({
90 | suuid: service.serviceId,
91 | cuuid: char.uuid,
92 | listening: false
93 | })
94 | }
95 | if (char.properties.read) {
96 | readUUIDs.push({
97 | suuid: service.serviceId,
98 | cuuid: char.uuid,
99 | })
100 | }
101 | if (char.properties.write) {
102 | writeUUIDs.push({
103 | suuid: service.serviceId,
104 | cuuid: char.uuid,
105 | })
106 | }
107 | }
108 | }
109 | this.setData({ notifyUUIDs, readUUIDs, writeUUIDs });
110 | },
111 |
112 | // 扫描
113 | _scan() {
114 | this.bt.scan({
115 | services: [],
116 | allowDuplicatesKey: false,
117 | interval: 0,
118 | timeout: 15000,
119 | deviceName: '',
120 | containName: ''
121 | }).then(res => {
122 | console.log('home scan success', res);
123 | }).catch(e => {
124 | console.log('home scan fail', e);
125 | wx.showToast({
126 | title: e.message,
127 | icon: 'none'
128 | });
129 | });
130 | },
131 |
132 | // 停止扫描
133 | _stopScan() {
134 | this.bt.stopScan().then(res => {
135 | console.log('home stopScan success', res);
136 | }).catch(e => {
137 | console.log('home stopScan fail', e);
138 | })
139 | },
140 |
141 | // 连接
142 | _connect(e) {
143 | let index = e.currentTarget.id;
144 | this.bt.stopScan();
145 | let device = this.data.devices[index];
146 | this.bt.connect(device).then(res => {
147 | console.log('home connect success', res);
148 | }).catch(e => {
149 | wx.showToast({
150 | title: e.message,
151 | icon: 'none'
152 | });
153 | console.log('home connect fail', e);
154 | });
155 | wx.showLoading({
156 | title: '连接' + device.name,
157 | });
158 | },
159 |
160 | // 断开连接
161 | _disconnect() {
162 | this.bt.disconnect().then(res => {
163 | console.log('home disconnect success', res);
164 | }).catch(e => {
165 | console.log('home disconnect fail', e);
166 | })
167 | },
168 |
169 | // 监听/停止监听
170 | _notify(e) {
171 | let index = e.currentTarget.id;
172 | let { suuid, cuuid, listening } = this.data.notifyUUIDs[index];
173 | this.bt.notify({
174 | suuid, cuuid, state: !listening
175 | }).then(res => {
176 | console.log('home notify success', res);
177 | this.setData({ [`notifyUUIDs[${index}].listening`]: !listening });
178 | }).catch(e => {
179 | console.log('home notify fail', e);
180 | })
181 | },
182 |
183 | // 读特征值
184 | _read(e) {
185 | let index = e.currentTarget.id;
186 | let { suuid, cuuid } = this.data.readUUIDs[index];
187 | this.bt.read({
188 | suuid, cuuid
189 | }).then(res => {
190 | console.log('home read success', res);
191 | }).catch(e => {
192 | console.log('home read fail', e);
193 | })
194 | },
195 |
196 | // 向蓝牙模块写入数据,这里只做简单的例子,发送的是 'FFFF' 的十六进制字符串
197 | _write(e) {
198 | let index = e.currentTarget.id;
199 | let { suuid, cuuid } = this.data.writeUUIDs[index];
200 | this.bt.write({
201 | suuid,
202 | cuuid,
203 | value: 'FFFF'
204 | }).then(res => {
205 | console.log('home write success', res);
206 | }).catch(e => {
207 | console.log('home write fail', e);
208 | })
209 | },
210 |
211 | })
--------------------------------------------------------------------------------
/example/wxdemo/pages/home/home.json:
--------------------------------------------------------------------------------
1 | {
2 | "disableScroll": true,
3 | "usingComponents": {}
4 | }
--------------------------------------------------------------------------------
/example/wxdemo/pages/home/home.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 扫描
6 | 停止扫描
7 | 断开蓝牙
8 |
9 |
10 | {{connected?("Connected "+device.name):"Disconnected"}}
11 |
12 | Peripherals
13 |
14 |
15 |
16 | {{item.name}}
17 |
18 |
19 |
20 |
21 |
22 |
23 | Notify Characteristic
24 |
25 |
26 |
27 | {{item.cuuid}}
28 | {{item.listening?"Stop":"Notify"}}
29 |
30 |
31 |
32 |
33 |
34 | Read Characteristic
35 |
36 |
37 |
38 | {{item.cuuid}}
39 | Read
40 |
41 |
42 |
43 |
44 |
45 | Write Characteristic
46 |
47 |
48 |
49 | {{item.cuuid}}
50 | Write
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/example/wxdemo/pages/home/home.wxss:
--------------------------------------------------------------------------------
1 | /* pages/home/home.wxss */
2 |
3 | .page {
4 | width: 100%;
5 | height: 100%;
6 | background: #eee;
7 | }
8 |
9 | .btn-view {
10 | margin-top: 30rpx;
11 | height: 100rpx;
12 | width: 95%;
13 | justify-content: space-around;
14 | }
15 |
16 | .btn-view .btn {
17 | width: 25%;
18 | height: 80%;
19 | background: #1E90FF;
20 | color: white;
21 | border-radius: 10rpx;
22 | }
23 |
24 | .status-view {
25 | height: 50rpx;
26 | width: 100%;
27 | margin-left: 50rpx;
28 | }
29 |
30 | .title-view {
31 | margin-top: 30rpx;
32 | height: 50rpx;
33 | width: 100%;
34 | margin-left: 50rpx;
35 | font-size: 40rpx;
36 | font-weight: 400;
37 | line-height: 40rpx;
38 | }
39 |
40 | scroll-view {
41 | height: 240rpx;
42 | width: 100%;
43 | background: white;
44 | }
45 |
46 | .cell {
47 | height: 80rpx;
48 | width: 100%;
49 | justify-content: space-between;
50 | }
51 |
52 | .cell .name{
53 | font-size: 40rpx;
54 | font-weight: 300;
55 | margin-left: 30rpx;
56 | }
57 |
58 | .cell image {
59 | margin-right: 20rpx;
60 | height: 30rpx;
61 | width: 30rpx;
62 | }
63 |
64 | .cell .uuid {
65 | font-size: 27rpx;
66 | margin-left: 30rpx;
67 | font-weight: 300;
68 | }
69 |
70 | .cell .btn {
71 | width: 100rpx;
72 | color: #1E90FF;
73 | font-size: 30rpx;
74 | }
75 |
76 | scroll-view .line {
77 | height: 2rpx;
78 | width: 100%;
79 | background:#e0e0e0;
80 | }
--------------------------------------------------------------------------------
/example/wxdemo/pages/index/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | const app = getApp()
4 |
5 | Page({
6 | data: {
7 | motto: 'Hello World',
8 | userInfo: {},
9 | hasUserInfo: false,
10 | canIUse: wx.canIUse('button.open-type.getUserInfo')
11 | },
12 | //事件处理函数
13 | bindViewTap: function() {
14 | wx.navigateTo({
15 | url: '../logs/logs'
16 | })
17 | },
18 | onLoad: function () {
19 | if (app.globalData.userInfo) {
20 | this.setData({
21 | userInfo: app.globalData.userInfo,
22 | hasUserInfo: true
23 | })
24 | } else if (this.data.canIUse){
25 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
26 | // 所以此处加入 callback 以防止这种情况
27 | app.userInfoReadyCallback = res => {
28 | this.setData({
29 | userInfo: res.userInfo,
30 | hasUserInfo: true
31 | })
32 | }
33 | } else {
34 | // 在没有 open-type=getUserInfo 版本的兼容处理
35 | wx.getUserInfo({
36 | success: res => {
37 | app.globalData.userInfo = res.userInfo
38 | this.setData({
39 | userInfo: res.userInfo,
40 | hasUserInfo: true
41 | })
42 | }
43 | })
44 | }
45 | },
46 | getUserInfo: function(e) {
47 | console.log(e)
48 | app.globalData.userInfo = e.detail.userInfo
49 | this.setData({
50 | userInfo: e.detail.userInfo,
51 | hasUserInfo: true
52 | })
53 | }
54 | })
55 |
--------------------------------------------------------------------------------
/example/wxdemo/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
--------------------------------------------------------------------------------
/example/wxdemo/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{userInfo.nickName}}
8 |
9 |
10 |
11 | {{motto}}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/wxdemo/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | /**index.wxss**/
2 | .userinfo {
3 | display: flex;
4 | flex-direction: column;
5 | align-items: center;
6 | }
7 |
8 | .userinfo-avatar {
9 | width: 128rpx;
10 | height: 128rpx;
11 | margin: 20rpx;
12 | border-radius: 50%;
13 | }
14 |
15 | .userinfo-nickname {
16 | color: #aaa;
17 | }
18 |
19 | .usermotto {
20 | margin-top: 200px;
21 | }
--------------------------------------------------------------------------------
/example/wxdemo/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "autoAudits": false,
13 | "nodeModules": true
14 | },
15 | "compileType": "miniprogram",
16 | "libVersion": "2.4.3",
17 | "appid": "wx3b0039a76f9c0d03",
18 | "projectname": "wxdemo",
19 | "debugOptions": {
20 | "hidedInDevtools": []
21 | },
22 | "isGameTourist": false,
23 | "condition": {
24 | "search": {
25 | "current": -1,
26 | "list": []
27 | },
28 | "conversation": {
29 | "current": -1,
30 | "list": []
31 | },
32 | "game": {
33 | "currentL": -1,
34 | "list": []
35 | },
36 | "miniprogram": {
37 | "current": -1,
38 | "list": []
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/example/wxdemo/utils/util.js:
--------------------------------------------------------------------------------
1 | const formatTime = date => {
2 | const year = date.getFullYear()
3 | const month = date.getMonth() + 1
4 | const day = date.getDate()
5 | const hour = date.getHours()
6 | const minute = date.getMinutes()
7 | const second = date.getSeconds()
8 |
9 | return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
10 | }
11 |
12 | const formatNumber = n => {
13 | n = n.toString()
14 | return n[1] ? n : '0' + n
15 | }
16 |
17 | module.exports = {
18 | formatTime: formatTime
19 | }
20 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 |
2 | import { BTManager } from './src/btmanager.js';
3 | import { ConnectStatus, SuccessCallbackEvent, ErrorCallbackEvent, SuccessApiThen, ErrorApiCatch } from './src/enum.js';
4 | const Version = '1.1.1';
5 |
6 | export {
7 | Version,
8 | BTManager,
9 | ConnectStatus,
10 | SuccessCallbackEvent,
11 | ErrorCallbackEvent,
12 | SuccessApiThen,
13 | ErrorApiCatch
14 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wx-ant-ble",
3 | "version": "1.1.1",
4 | "description": "微信、支付宝小程序BLE蓝牙SDK",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "zhaodahai",
10 | "license": "ISC",
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/zhaodahai/wx-ant-ble.git"
14 | },
15 | "keywords": [
16 | "ble", "bluetooth", "蓝牙","小程序"
17 | ],
18 | "bugs": {
19 | "url": "https://github.com/zhaodahai/wx-ant-ble/issues"
20 | },
21 | "homepage": "https://github.com/zhaodahai/wx-ant-ble#readme"
22 | }
23 |
--------------------------------------------------------------------------------
/resource/QRcode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/resource/QRcode.jpg
--------------------------------------------------------------------------------
/resource/powerpoint.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhaodahai/wx-ant-ble/c689614f401ca2c52a1f5f34a036be2f2c3088ec/resource/powerpoint.gif
--------------------------------------------------------------------------------
/src/bluetooth.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { ConnectStatus, SuccessCallbackEvent, ErrorCallbackEvent, SuccessApiThen, ErrorApiCatch} from './enum.js';
4 | import _ from './tools.js';
5 |
6 | export default class Bluetooth {
7 |
8 | constructor(btmanager) {
9 | this.bm = btmanager;
10 | // 蓝牙适配器是否初始化完成
11 | this.isInitializedAdapter = false;
12 | // 蓝牙适配器是否可用
13 | this.isAvailableAdapter = false;
14 | // 初始化蓝牙适配器
15 | this.openAndListenBluetoothAdapter().then(__=>__).catch(__=>__);
16 | // 扫描到的设备
17 | this.scanDevices = [];
18 | }
19 |
20 | /**
21 | * 打开和监听蓝牙适配器
22 | *
23 | * @return Promise对象
24 | */
25 | openAndListenBluetoothAdapter() {
26 |
27 | // 未初始化蓝牙适配器,打开蓝牙适配器
28 | if (!this.isInitializedAdapter) {
29 |
30 | // 监听蓝牙适配器状态
31 | _.api('offBluetoothAdapterStateChange').then(__ => __).catch(__ => __);
32 | _.on('onBluetoothAdapterStateChange','' ,res => {
33 | this.bm.log('onBluetoothAdapterStateChange', res);
34 | if (res.available && !this.isAvailableAdapter) {
35 | this.isAvailableAdapter = true;
36 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_PowerOn);
37 | } else if (!res.available) {
38 | this.isAvailableAdapter = false;
39 | // 支付宝小程序当蓝牙适配器关闭,再次进行蓝牙操作需要重新打开,微信只需要打开一次就行
40 | _.getAppPlatform() === 'ant' && (this.isInitializedAdapter = false);
41 | this.bm.connectStatus = ConnectStatus.disconnected;
42 | this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_PowerOff);
43 | }
44 | });
45 |
46 | // 先关闭再打开蓝牙适配器,避免出现某些机型打开无效的情况
47 | return _.api('closeBluetoothAdapter')
48 | .then(__ => {
49 | // 打开蓝牙适配器
50 | return _.api('openBluetoothAdapter')
51 | }).then(res => {
52 | this.bm.log('openBluetoothAdapter success', res);
53 | this.isInitializedAdapter = true;
54 | this.isAvailableAdapter = true;
55 | return Promise.resolve();
56 | }).catch(e => {
57 | this.bm.log('openBluetoothAdapter fail', e);
58 | this.isInitializedAdapter = false;
59 | this.isAvailableAdapter = false;
60 | this.bm.connectStatus = ConnectStatus.disconnected;
61 | return Promise.reject(e);
62 | })
63 | } else {
64 | return Promise.resolve();
65 | }
66 | }
67 |
68 | /**
69 | * 扫描外设
70 | *
71 | * @param {object} options 扫描参数
72 | * @property {array} services 主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备
73 | * @property {boolean} allowDuplicatesKey 是否允许重复上报设备
74 | * @property {number} interval 上报新设备的间隔,默认为0
75 | * @property {number} timeout 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。默认1500ms,-1表示无限超时
76 | * @property {string} deviceName 通过蓝牙名称过滤,需要匹配的设备名称
77 | * @property {string} containName 通过蓝牙名称过滤,需要包含的设备名称
78 | *
79 | * @return Promise对象
80 | *
81 | * @discussion 开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。
82 | * 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。
83 | * 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。
84 | *
85 | * @see registerDidDiscoverDevice
86 | */
87 | scanDevice(options) {
88 |
89 | // 解构参数
90 | let { services, allowDuplicatesKey, interval, timeout, deviceName, containName} = options;
91 |
92 | // 打开和监听蓝牙适配器
93 | return this.openAndListenBluetoothAdapter()
94 | .then(__ => {
95 | // 清空存储的设备
96 | this.scanDevices = [];
97 | // 销毁扫描延时
98 | this.destoryTimer();
99 | // 设置扫描超时
100 | this.scanTimeoutTimer = timeout!==-1 ? setTimeout(__ => {
101 | this.stopScan();
102 | if (this.scanDevices.length === 0) { // 扫描超时
103 | this.bm.log('startBluetoothDevicesDiscovery fail ' , 'timeout');
104 | this.callBackDiscoverDevice(null, ErrorCallbackEvent.Error_DiscoverDevice_CB_Timeout, true);
105 | } else {// 扫描时间结束
106 | this.callBackDiscoverDevice(null, SuccessCallbackEvent.Success_DiscoverDevice_CB_ScanDone, false);
107 | }
108 | }, timeout || 15000) : null;
109 | // 开始扫描
110 | return _.api('startBluetoothDevicesDiscovery', '' ,{
111 | services,
112 | allowDuplicatesKey,
113 | interval
114 | })
115 | }).then(res => {
116 | this.bm.log('startBluetoothDevicesDiscovery success', res);
117 | // 取消设备监听,仅支付宝小程序有效
118 | _.api('offBluetoothDeviceFound').then(__ => __).catch(__ => __);
119 | // 监听扫描到外设
120 | _.on('onBluetoothDeviceFound','', res => {
121 | // this.bm.log('onBluetoothDeviceFound' , res);
122 | let devices = res.devices || res;
123 | // 过滤、格式化、存储、上报设备
124 | for (let device of devices) {
125 | if (Array.isArray(device)) device = devices[0][0];
126 | // 信号强度为127表示RSSI不可用
127 | if (device.RSSI === 127) return;
128 | // 匹配名称,过滤设备
129 | let name = device.name || device.deviceName;
130 | device.name = name;
131 | if (deviceName && (!name || name !== deviceName)) return;
132 | if (containName && (!name || !~name.indexOf(containName))) return;
133 | // 格式化广播数据
134 | if (typeof device.advertisData !== 'string') device.advertisData = _.ab2str(device.advertisData);
135 | // 上报设备
136 | this.callBackDiscoverDevice(device, SuccessCallbackEvent.Success_DiscoverDevice_CB_Discover, false);
137 | // 更新不重复记录设备
138 | for (let v of this.scanDevices) {
139 | if (v.deviceId === device.deviceId) {
140 | Object.assign(v, device);
141 | return;
142 | }
143 | }
144 | // 存储新设备
145 | this.scanDevices.push(device);
146 | }
147 | })
148 | return Promise.resolve(SuccessApiThen.Success_Scan);
149 | }).catch(e => {
150 | this.bm.log('startBluetoothDevicesDiscovery fail', e);
151 | if (e.code === 12 || e.code === 10001) {
152 | return Promise.reject(ErrorApiCatch.Error_Scan_PowerOff);
153 | } else if (e.code === 10012 || e.code === 10004) {
154 | return Promise.reject(ErrorApiCatch.Error_Scan_NoService);
155 | } else {
156 | return Promise.reject(ErrorApiCatch.Error_Scan_Failed);
157 | }
158 | })
159 | }
160 |
161 | /**
162 | * 停止扫描
163 | *
164 | * @return Promise对象
165 | *
166 | * @discussion 停止扫描,取消超时延时。
167 | */
168 | stopScan() {
169 | // 销毁扫描延时
170 | this.destoryTimer();
171 | // 取消设备监听,仅支付宝小程序有效
172 | _.api('offBluetoothDeviceFound').then(__ => __).catch(__ => __);
173 | // 停止扫描
174 | return _.api('stopBluetoothDevicesDiscovery')
175 | .then(res => {
176 | this.bm.log('stopBluetoothDevicesDiscovery success', res);
177 | return Promise.resolve(SuccessApiThen.Success_StopScan);
178 | }).catch(e => {
179 | this.bm.log('stopBluetoothDevicesDiscovery fail', e);
180 | if (e.code === 12 || e.code === 10001) {
181 | return Promise.reject(ErrorApiCatch.Error_StopScan_PowerOff);
182 | } else {
183 | return Promise.reject(ErrorApiCatch.Error_StopScan_Failed);
184 | }
185 | })
186 | }
187 |
188 | /**
189 | * 连接外设
190 | *
191 | * @param {object} device 指定连接的外设对象,从registerDidDiscoverDevice注册的回调中得到
192 | * @param {number} timeout 连接超时时间,毫秒,默认15000ms,支付宝小程序无效
193 | *
194 | * @return Promise对象
195 | *
196 | * @discussion 连接指定的外设,需要传入外设对象。
197 | * 注意实现返回对象的then和catch方法,监听接口是否调用成功。
198 | */
199 | connect(device , timeout) {
200 | // 判断是否已经连接
201 | if (this.bm.connectStatus !== ConnectStatus.disconnected) {
202 | this.bm.logwarn('connect fail', 'Is already connected');
203 | return Promise.reject(ErrorApiCatch.Error_Connect_AlreadyConnected);
204 | }
205 | // 判断设备id是否为空
206 | if (_.isEmpty(device.deviceId)) {
207 | return Promise.reject(ErrorApiCatch.Error_Connect_EmptyId);
208 | }
209 |
210 | this.bm.deviceInfo = device;
211 | this.bm.connectStatus = ConnectStatus.connecting;
212 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Connecting);
213 | let deviceId = device.deviceId;
214 |
215 | // 打开和监听蓝牙适配器
216 | return this.openAndListenBluetoothAdapter()
217 | .then(__ => {
218 | // 连接设备
219 | return _.api('createBLEConnection', 'connectBLEDevice', {
220 | deviceId ,
221 | timeout: timeout || 15000 ,
222 | })
223 | }).then( res => {
224 | this.bm.log('connectBLEDevice success', res);
225 | // 取消蓝牙连接状态监听,仅支付宝小程序有效
226 | _.api('offBLEConnectionStateChanged').then(__ => __).catch(__ => __);
227 | // 蓝牙连接状态监听
228 | _.on('onBLEConnectionStateChange', 'onBLEConnectionStateChanged',(res) => {
229 | this.bm.log('onBLEConnectionStateChange', res);
230 | if (!res.connected && this.bm.connectStatus !== ConnectStatus.disconnected) {
231 | this.bm.connectStatus = ConnectStatus.disconnected;
232 | if (res.errorCode === 0 || res.errorCode === undefined) {
233 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Stop);
234 | } else if (res.errorCode === 10003) {
235 | this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected);
236 | } else if (_.getAppPlatform() === 'ant'){
237 | this.callBackConnectStatus(ErrorCallbackEvent.Error_ConnectStatus_CB_Disconnected);
238 | }
239 | }
240 | })
241 |
242 | // 获取设备所有服务
243 | _.api('getBLEDeviceServices', '', { deviceId })
244 | .then(res => {
245 | this.bm.log('getBLEDeviceServices success', res);
246 | // 存储所有服务promise
247 | let sPromises = [];
248 | // 获取所有服务的所有特征
249 | device.services = res.services.map(server => {
250 | let sUUID = server.uuid || server.serviceId;
251 | let sPromise = _.api('getBLEDeviceCharacteristics', '', {
252 | deviceId,
253 | serviceId: sUUID
254 | })
255 | sPromises.push(sPromise);
256 | return { serviceId: sUUID };
257 | })
258 | return Promise.all(sPromises);
259 | }).then(res => {
260 | this.bm.log('getBLEDeviceCharacteristics success', res);
261 | device.services = res.map((v, i) => {
262 | let service = device.services[i];
263 | service.characteristics = v.characteristics;
264 | return service;
265 | })
266 | // 获取特征成功之后才算连接成功
267 | this.bm.deviceInfo = device;
268 | this.bm.connectStatus = ConnectStatus.connected;
269 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Connected);
270 | }).catch(e => {
271 | this.bm.logwarn('api connecting error', e);
272 | // 出现错误断开蓝牙,避免出现已连接成功未找到服务或者特征出错时,再次连接状态不正确
273 | _.api('closeBLEConnection', 'disconnectBLEDevice', {
274 | deviceId: this.bm.deviceInfo.deviceId
275 | }).then(__ => __).catch(__ => __);
276 | this.bm.connectStatus = ConnectStatus.disconnected;
277 | this.callBackConnectStatus(e);
278 | })
279 |
280 | // 开始连接接口调用成功
281 | return Promise.resolve(SuccessApiThen.Success_Connect);
282 | }).catch(e => {
283 | this.bm.logwarn('api connect error', e);
284 | // 未知错误,直接报连接失败
285 | if (e.code === 100000) e = ErrorCallbackEvent.Error_ConnectStatus_CB_ConnectFail;
286 | this.bm.connectStatus = ConnectStatus.disconnected;
287 | this.callBackConnectStatus(e);
288 | if (e.code === 12 || e.code === 10001) {
289 | return Promise.reject(ErrorApiCatch.Error_Connect_PowerOff);
290 | } else if (~e.message.indexOf('超时') || (e.errMsg && ~e.errMsg.indexOf('time out'))){
291 | return Promise.reject(ErrorApiCatch.Error_Connect_Timeout);
292 | } else {
293 | return Promise.reject(ErrorApiCatch.Error_Connect_Failed);
294 | }
295 | })
296 | }
297 |
298 | /**
299 | * 断开连接
300 | *
301 | * @return Promise对象
302 | */
303 | disconnect() {
304 |
305 | this.bm.connectStatus === ConnectStatus.connected &&
306 | _.api('closeBLEConnection','disconnectBLEDevice',{
307 | deviceId: this.bm.deviceInfo.deviceId
308 | }).then(res => {
309 | this.bm.log('closeBLEConnection success', res);
310 | }).catch(e => {
311 | this.bm.log('closeBLEConnection fail', e);
312 | })
313 |
314 | _.api('closeBluetoothAdapter')
315 | .then(res => {
316 | this.bm.log('closeBluetoothAdapter success', res);
317 | this.isInitializedAdapter = false;
318 | if (this.bm.connectStatus !== ConnectStatus.disconnected) {
319 | this.bm.connectStatus = ConnectStatus.disconnected;
320 | this.callBackConnectStatus(SuccessCallbackEvent.Success_ConnectStatus_CB_Stop);
321 | };
322 | this.bm.connectStatus = ConnectStatus.disconnected;
323 | }).catch(e => {
324 | this.bm.log('closeBluetoothAdapter fail', e);
325 | })
326 |
327 | return Promise.resolve(SuccessApiThen.Success_Disconnect);
328 | }
329 |
330 | /**
331 | * 读特征值
332 | *
333 | * @param {object} params 参数
334 | * @property {string} suuid 特征对应的服务uuid
335 | * @property {string} cuuid 写入特征uuid
336 | *
337 | * @discussion 读某个服务下的某个特征值。
338 | */
339 | read(params) {
340 | if (this.bm.connectStatus === ConnectStatus.connected) {
341 | let { suuid, cuuid } = params;
342 | return _.api('readBLECharacteristicValue', '', {
343 | deviceId: this.bm.deviceInfo.deviceId,
344 | serviceId: suuid,
345 | characteristicId: cuuid,
346 | }).then(res => {
347 | this.bm.log('readBLECharacteristicValue success', res);
348 | return Promise.resolve(SuccessApiThen.Success_Read);
349 | }).catch(e => {
350 | this.bm.log('readBLECharacteristicValue fail', e);
351 | if (e.code === 10007) {
352 | return Promise.reject(ErrorApiCatch.Error_Read_NotSupport);
353 | } else if (e.code === 10004) {
354 | return Promise.reject(ErrorApiCatch.Error_Read_NoService);
355 | } else if (e.code === 10005) {
356 | return Promise.reject(ErrorApiCatch.Error_Read_NoCharacteristic);
357 | } else {
358 | return Promise.reject(ErrorApiCatch.Error_Read_Failed);
359 | }
360 | })
361 | } else {
362 | return Promise.reject(ErrorApiCatch.Error_Read_NotConnected);
363 | }
364 | }
365 |
366 | /**
367 | * 向蓝牙模块写入数据
368 | *
369 | * @param {object} params 参数
370 | * @property {string} suuid 特征对应的服务uuid
371 | * @property {string} cuuid 写入特征uuid
372 | * @property {Hex string} value 16进制字符串
373 | *
374 | * @return Promise对象
375 | *
376 | * @discussion 向蓝牙模块写入数据。
377 | */
378 | write(params) {
379 | let {suuid , cuuid , value} = params;
380 | if (this.bm.connectStatus === ConnectStatus.connected) {
381 | if (_.getAppPlatform() === 'wx') {
382 | if (typeof value === 'string') {
383 | value = _.str2ab(value);
384 | } else {
385 | value = _.typedArray2ArrayBuffer(value);
386 | }
387 | } else if (typeof value !== 'string'){
388 | value = _.ab2str(value);
389 | }
390 | this.bm.log('writeCmdToDevice', _.ab2str(value));
391 | return _.api('writeBLECharacteristicValue', '', {
392 | deviceId: this.bm.deviceInfo.deviceId,
393 | serviceId: suuid,
394 | characteristicId: cuuid,
395 | value,
396 | }).then(res => {
397 | this.bm.log('writeBLECharacteristicValue success', res);
398 | return Promise.resolve(SuccessApiThen.Success_Write);
399 | }).catch(e => {
400 | this.bm.log('writeBLECharacteristicValue fail', e);
401 | if (e.code === 10007) {
402 | return Promise.reject(ErrorApiCatch.Error_Write_NotSupport);
403 | } else if (e.code === 10004) {
404 | return Promise.reject(ErrorApiCatch.Error_Write_NoService);
405 | } else if (e.code === 10005) {
406 | return Promise.reject(ErrorApiCatch.Error_Write_NoCharacteristic);
407 | } else {
408 | return Promise.reject(ErrorApiCatch.Error_Write_Failed);
409 | }
410 | })
411 | } else {
412 | return Promise.reject(ErrorApiCatch.Error_Write_NotConnected);
413 | }
414 | }
415 |
416 | /**
417 | * 监听特征值改变
418 | *
419 | * @param {object} params 参数
420 | * @property {string} suuid 特征对应的服务uuid
421 | * @property {string} cuuid 写入特征uuid
422 | * @property {boolean} state 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听
423 | *
424 | * @return Promise对象
425 | *
426 | * @discussion 监听某个特征值变化。
427 | */
428 | notify(params) {
429 | if (this.bm.connectStatus === ConnectStatus.connected) {
430 | let { suuid, cuuid, state } = params;
431 | return _.api('notifyBLECharacteristicValueChange', '', {
432 | deviceId: this.bm.deviceInfo.deviceId,
433 | serviceId: suuid,
434 | characteristicId: cuuid,
435 | state
436 | }).then(res => {
437 | this.bm.log('readBLECharacteristicValue success', res);
438 | return Promise.resolve(SuccessApiThen.Success_Notify);
439 | }).catch(e => {
440 | this.bm.log('readBLECharacteristicValue fail', e);
441 | if (e.code === 10007) {
442 | return Promise.reject(ErrorApiCatch.Error_Notify_NotSupport);
443 | } else if (e.code === 10004) {
444 | return Promise.reject(ErrorApiCatch.Error_Notify_NoService);
445 | } else if (e.code === 10005) {
446 | return Promise.reject(ErrorApiCatch.Error_Notify_NoCharacteristic);
447 | } else {
448 | return Promise.reject(ErrorApiCatch.Error_Notify_Failed);
449 | }
450 | })
451 | } else {
452 | return Promise.reject(ErrorApiCatch.Error_Notify_NotConnected);
453 | }
454 | }
455 |
456 | /**
457 | * 注册状态改变回调
458 | *
459 | * @param {function} cb 回调函数
460 | *
461 | * @discussion 连接状态发生改变时,回调此方法。
462 | */
463 | registerDidUpdateConnectStatus(cb) {
464 | this._didUpdateStatusCB = cb;
465 | }
466 |
467 | /**
468 | * 注册发现外设回调
469 | *
470 | * @param {function} cb 回调函数
471 | *
472 | * @discussion 当扫描到设备时回调
473 | */
474 | registerDidDiscoverDevice(cb) {
475 | this._didDiscoverDeviceCB = cb;
476 | }
477 |
478 | /**
479 | * 注册特征值改变回调
480 | *
481 | * @param {function} cb 回调函数
482 | *
483 | * @discussion 当监听的特征值改变时回调
484 | */
485 | registerDidUpdateValueForCharacteristic(cb) {
486 | this._didUpdateValueCB = cb;
487 | _.api('offBLECharacteristicValueChange').then(__ => __).catch(__ => __);
488 | _.on('onBLECharacteristicValueChange', '' ,characteristic => {
489 | if (typeof characteristic.value === 'string') {
490 | this._didUpdateValueCB(characteristic);
491 | } else {
492 | characteristic.value = _.ab2str(characteristic.value);
493 | this._didUpdateValueCB(characteristic);
494 | }
495 | })
496 | }
497 |
498 | /**
499 | * 回调蓝牙连接状态
500 | */
501 | callBackConnectStatus(status) {
502 | this._didUpdateStatusCB && this._didUpdateStatusCB({
503 | ...status,
504 | device:this.bm.deviceInfo,
505 | connectStatus:this.bm.connectStatus
506 | });
507 | }
508 |
509 | /**
510 | * 回调发现外设
511 | *
512 | * @param device 扫描到的设备
513 | */
514 | callBackDiscoverDevice(device , event , timeout) {
515 | this._didDiscoverDeviceCB && this._didDiscoverDeviceCB(
516 | device ? {
517 | ...event,
518 | timeout,
519 | device,
520 | } : {
521 | ...event,
522 | timeout,
523 | }
524 | )
525 | }
526 |
527 | /**
528 | * 销毁延时
529 | */
530 | destoryTimer() {
531 | this.scanTimeoutTimer && clearTimeout(this.scanTimeoutTimer);
532 | this.scanTimeoutTimer = null;
533 | }
534 | }
535 |
--------------------------------------------------------------------------------
/src/btmanager.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | import { Log } from './extends.js';
4 | import { ConnectStatus } from './enum.js';
5 | import Bluetooth from './bluetooth.js';
6 |
7 | export class BTManager {
8 |
9 | /**
10 | * 构造函数
11 | *
12 | * @param {object} config 配置
13 | * @property {boolean} debug 是否开启打印调试,默认不开启
14 | *
15 | * @discussion 单例模式。
16 | */
17 | constructor(config = {}) {
18 | if (!BTManager.instance) {
19 | BTManager.instance = this;
20 | // 初始化log
21 | Log.call(BTManager.prototype);
22 | // 初始化设备信息
23 | this.deviceInfo = {};
24 | // 初始化连接状态
25 | this.connectStatus = ConnectStatus.disconnected;
26 | // 初始化蓝牙管理器
27 | this._bt = new Bluetooth(this);
28 | }
29 | // 合并配置
30 | Object.assign(BTManager.instance, config);
31 | return BTManager.instance;
32 | }
33 |
34 | /**
35 | * 扫描外设
36 | *
37 | * @param {object} options 扫描参数
38 | * @property {array} services 主service的uuid列表。确认在蓝牙广播中存在此服务id,可以通过服务id过滤掉其他设备
39 | * @property {boolean} allowDuplicatesKey 是否允许重复上报设备
40 | * @property {number} interval 上报新设备的间隔,默认为0
41 | * @property {number} timeout 扫描超时时间,毫秒。在该时间内未扫描到符合要求的设备,上报超时。默认15000ms,-1表示无限超时
42 | * @property {string} deviceName 通过蓝牙名称过滤,需要匹配的设备名称
43 | * @property {string} containName 通过蓝牙名称过滤,需要包含的设备名称
44 | *
45 | * @return Promise对象
46 | *
47 | * @discussion 开始扫描外设,注意实现返回对象的then和catch方法,监听接口是否调用成功。
48 | * 此操作比较耗费系统资源,请在搜索到设备后调用stopScan方法停止扫描。
49 | * 重复调用此接口,会清空之前设备存储,再次上报已上报的设备,能够起到刷新的作用。
50 | *
51 | * @see registerDidDiscoverDevice
52 | */
53 | scan( options = {
54 | services: [],
55 | allowDuplicatesKey: false,
56 | interval : 0,
57 | timeout: 15000,
58 | deviceName: '',
59 | containName:''
60 | })
61 | {
62 | return this._bt.scanDevice(options);
63 | }
64 |
65 | /**
66 | * 停止扫描
67 | *
68 | * @return Promise对象
69 | *
70 | * @discussion 停止扫描,取消超时延时。
71 | */
72 | stopScan() {
73 | return this._bt.stopScan();
74 | }
75 |
76 | /**
77 | * 连接外设
78 | *
79 | * @param {object} device 指定连接的外设对象,从registerDidDiscoverDevice注册的回调中得到
80 | * @param {number} timeout 连接超时时间,毫秒,默认15000ms,支付宝小程序无效
81 | *
82 | * @return Promise对象
83 | *
84 | * @discussion 连接指定的外设,需要传入外设对象。
85 | * 注意实现返回对象的then和catch方法,监听接口是否调用成功。
86 | */
87 | connect(device , timeout) {
88 | if (!device) throw new Error('device is undefiend');
89 | return this._bt.connect(device);
90 | }
91 |
92 | /**
93 | * 断开连接
94 | *
95 | * @return Promise对象
96 | */
97 | disconnect() {
98 | return this._bt.disconnect();
99 | }
100 |
101 | /**
102 | * 读特征值
103 | *
104 | * @param {object} params 参数
105 | * @property {string} suuid 特征对应的服务uuid
106 | * @property {string} cuuid 特征uuid
107 | *
108 | * @return Promise对象
109 | *
110 | * @discussion 读某个服务下的某个特征值。
111 | */
112 | read(params = {
113 | suuid: '',
114 | cuuid: ''
115 | })
116 | {
117 | return this._bt.read(params);
118 | }
119 |
120 | /**
121 | * 向蓝牙模块写入数据
122 | *
123 | * @param {object} params 参数
124 | * @property {string} suuid 特征对应的服务uuid
125 | * @property {string} cuuid 特征uuid
126 | * @property {Hex string} value 16进制字符串
127 | *
128 | * @return Promise对象
129 | *
130 | * @discussion 向蓝牙模块写入数据。
131 | */
132 | write(params = {
133 | suuid: '',
134 | cuuid: '',
135 | value: ''
136 | })
137 | {
138 | return this._bt.write(params);
139 | }
140 |
141 | /**
142 | * 监听特征值改变
143 | *
144 | * @param {object} params 参数
145 | * @property {string} suuid 特征对应的服务uuid
146 | * @property {string} cuuid 特征uuid
147 | * @property {boolean} state 是否启用notify,可以通过重复调用接口改变此属性打开/关闭监听
148 | *
149 | * @return Promise对象
150 | *
151 | * @discussion 监听某个特征值变化。
152 | */
153 | notify(params = {
154 | suuid: '',
155 | cuuid: '',
156 | state: true,
157 | })
158 | {
159 | return this._bt.notify(params);
160 | }
161 |
162 | /**
163 | * 注册状态改变回调
164 | *
165 | * @param {function} cb 回调函数
166 | *
167 | * @discussion 连接状态发生改变时,回调此方法。
168 | */
169 | registerDidUpdateConnectStatus(cb) {
170 | if (typeof cb !== 'function') throw new TypeError('connectStatus callback expect function');
171 | this._bt.registerDidUpdateConnectStatus(cb);
172 | }
173 |
174 | /**
175 | * 注册发现外设回调
176 | *
177 | * @param {function} cb 回调函数
178 | *
179 | * @discussion 当扫描到设备时回调,或者达到超时时间回调。
180 | */
181 | registerDidDiscoverDevice(cb) {
182 | if (typeof cb !== 'function') throw new TypeError('discoverDevice callback expect function');
183 | this._bt.registerDidDiscoverDevice(cb);
184 | }
185 |
186 | /**
187 | * 注册特征值改变回调
188 | *
189 | * @param {function} cb 回调函数
190 | *
191 | * @discussion 当监听的特征值改变时回调,或者读特征值时回调。
192 | */
193 | registerDidUpdateValueForCharacteristic(cb) {
194 | if (typeof cb !== 'function') throw new TypeError('updateValueForCharacteristic callback expect function');
195 | this._bt.registerDidUpdateValueForCharacteristic(cb);
196 | }
197 |
198 | }
--------------------------------------------------------------------------------
/src/enum.js:
--------------------------------------------------------------------------------
1 |
2 | // 连接状态
3 | export const ConnectStatus = {
4 | // 未连接或连接断开,允许连接
5 | disconnected: 0,
6 | // 正在连接,不允许再连接
7 | connecting: 1,
8 | // 已连接,不允许再连接
9 | connected: 2,
10 | }
11 |
12 | // 发现外设回调和连接状态改变回调成功事件
13 | export const SuccessCallbackEvent = {
14 | Success_DiscoverDevice_CB_Discover: { code: 210, message: '发现外设' },
15 | Success_DiscoverDevice_CB_ScanDone: { code: 211, message: '扫描完成' },
16 | Success_ConnectStatus_CB_PowerOn: { code: 220, message: '蓝牙打开' },
17 | Success_ConnectStatus_CB_Connecting: { code: 221, message: '正在连接' },
18 | Success_ConnectStatus_CB_Connected: { code: 222, message: '连接成功' },
19 | Success_ConnectStatus_CB_Stop: { code: 223, message: '断开成功' },
20 | }
21 |
22 | // 发现外设回调和连接状态改变回调失败事件
23 | export const ErrorCallbackEvent = {
24 | Error_DiscoverDevice_CB_Timeout: { code: 410, message: '扫描超时' },
25 | Error_ConnectStatus_CB_PowerOff: { code: 420, message: '蓝牙关闭' },
26 | Error_ConnectStatus_CB_ConnectFail: { code: 421, message: '连接失败' },
27 | Error_ConnectStatus_CB_Disconnected: { code: 422, message: '连接断开' },
28 | }
29 |
30 | // 接口调用成功事件
31 | export const SuccessApiThen = {
32 | Success_Scan: { code: 2010, message: '扫描接口成功调用' },
33 | Success_StopScan: { code: 2020, message: '停止扫描接口成功调用' },
34 | Success_Connect: { code: 2030, message: '连接接口成功调用' },
35 | Success_Disconnect: { code: 2040, message: '断开接口成功调用' },
36 | Success_Read: { code: 2050, message: '读特征值接口成功调用' },
37 | Success_Write: { code: 2060, message: '写入数据接口成功调用' },
38 | Success_Notify: { code: 2070, message: '监听特征值接口成功调用' },
39 | }
40 |
41 | // 接口调用失败事件
42 | export const ErrorApiCatch = {
43 | // 基础库版本低
44 | Error_Low_Version: { code: 4000, message: '当前基础库版本低,请更新微信版本' },
45 | // Api: scan
46 | Error_Scan_Failed: { code: 4010, message: '扫描错误:扫描失败' },
47 | Error_Scan_PowerOff: { code: 4011, message: '扫描错误:蓝牙被关闭' },
48 | Error_Scan_NoService: { code: 4012, message: '扫描错误:没有找到指定服务' },
49 | // Api: stopScan
50 | Error_StopScan_Failed: { code: 4020, message: '停止扫描错误:停止扫描失败' },
51 | Error_StopScan_PowerOff: { code: 4021, message: '停止扫描错误:蓝牙被关闭' },
52 | // Api: connect
53 | Error_Connect_Failed: { code: 4030, message: '连接错误:连接失败' },
54 | Error_Connect_PowerOff: { code: 4031, message: '连接错误:蓝牙被关闭' },
55 | Error_Connect_AlreadyConnected: { code: 4032, message: '连接错误:已经连接或正在连接' },
56 | Error_Connect_Timeout: { code: 4033, message: '连接错误:连接超时' },
57 | Error_Connect_EmptyId: { code: 4034, message: '连接错误:设备id不能为空' },
58 | // Api: disconnect
59 | Error_Disconnect_Failed: { code: 4040, message: '断开错误:断开失败' },
60 | // Api: read
61 | Error_Read_Failed: { code: 4050, message: '读特征值错误:读特征值失败'},
62 | Error_Read_NotConnected: { code: 4051, message: '读特征值错误:蓝牙未连接' },
63 | Error_Read_NotSupport: { code: 4052, message: '读特征值错误:当前特征不支持读操作' },
64 | Error_Read_NoService: { code: 4053, message: '读特征值错误:没有找到指定服务' },
65 | Error_Read_NoCharacteristic: { code: 4054, message: '读特征值错误:没有找到指定特征值' },
66 | // Api: write
67 | Error_Write_Failed: { code: 4060, message: '写入数据错误:写入数据失败'},
68 | Error_Write_NotConnected: { code: 4061, message: '写入数据错误:蓝牙未连接' },
69 | Error_Write_NotSupport: { code: 4062, message: '写入数据错误:当前特征不支持写操作' },
70 | Error_Write_NoService: { code: 4063, message: '写入数据错误:没有找到指定服务' },
71 | Error_Write_NoCharacteristic: { code: 4064, message: '写入数据错误:没有找到指定特征值' },
72 | // Api: notify
73 | Error_Notify_Failed: { code: 4070, message: '监听特征值错误:监听特征值错误失败' },
74 | Error_Notify_NotConnected: { code: 4071, message: '监听特征值错误:蓝牙未连接' },
75 | Error_Notify_NotSupport: { code: 4072, message: '监听特征值错误:当前特征不支持监听操作' },
76 | Error_Notify_NoService: { code: 4073, message: '监听特征值错误:没有找到指定服务' },
77 | Error_Notify_NoCharacteristic: { code: 4074, message: '监听特征值错误:没有找到指定特征值' },
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/src/extends.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export function Log() {
4 |
5 | this.log = function(identifier, msg='') {
6 | this.debug && console.log(`BLE:(${identifier}):` , msg);
7 | }
8 |
9 | this.loginfo = function(identifier,msg) {
10 | this.debug && console.info(`BLE:(${identifier}):` ,msg);
11 | }
12 |
13 | this.logwarn = function (identifier, msg) {
14 | this.debug && console.warn(`BLE:(${identifier}):`, msg);
15 | }
16 |
17 | this.logerror = function (identifier, msg) {
18 | this.debug && console.error(`BLE:(${identifier}):`, msg);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/promisify.js:
--------------------------------------------------------------------------------
1 |
2 | // 判断是微信小程序还是支付宝小程序
3 | let ii, mini;
4 | try {
5 | ii = wx;
6 | mini = 'wx';
7 | } catch (e) {
8 | ii = my;
9 | mini = 'ant';
10 | }
11 |
12 | import { ErrorApiCatch} from './enum.js';
13 |
14 | // 微信系统错误
15 | const ERROR_WX = {
16 | NOT_INIT: { code: 10000, message: '未初始化蓝牙适配器' },
17 | NOT_AVAILABLE: { code: 10001, message: '当前蓝牙适配器不可用' },
18 | NO_DEVICE: { code: 10002, message: '没有找到指定设备' },
19 | CONNECTION_FAIL: { code: 10003, message: '连接失败' },
20 | NO_SERVICE: { code: 10004, message: '没有找到指定服务' },
21 | NO_CHARACTERISTIC: { code: 10005, message: '没有找到指定特征值' },
22 | NO_CONNECTION: { code: 10006, message: '当前连接已断开' },
23 | PROPERTY_NOT_SUPPORT: { code: 10007, message: '当前特征值不支持此操作' },
24 | SYSTEM_ERROR: { code: 10008, message: '系统异常' },
25 | SYSTEM_NOT_SUPPORT: { code: 10009, message: 'Android 系统版本低于 4.3 不支持 BLE' },
26 | OPERATE_TIMEOUT: { code: 10012, message: '操作超时' },
27 | INVALID_PARAMETER: { code: 10013, message: '无效参数' },
28 | ALREADY_CONNECTED: { code: -1 , message: '蓝牙已连接,不能再连接' },
29 | UN_KNOWN: { code: 100000,message: '未知' },
30 | }
31 |
32 | // 支付宝系统错误
33 | const ERROR_ANT = {
34 | POWER_OFF: { code: 12, message: '蓝牙未打开' },
35 | LOST_SERVICE: { code: 13, message: '与系统服务的链接暂时丢失' },
36 | UNAUTH_BLE: { code: 14, message: '未授权支付宝使用蓝牙功能' },
37 | UNKNOWN_ERROR: { code: 15, message: '未知错误' },
38 | NOT_INIT: { code: 10000, message: '未初始化蓝牙适配器' },
39 | NOT_AVAILABLE: { code: 10001, message: '当前蓝牙适配器不可用' },
40 | NO_DEVICE: { code: 10002, message: '没有找到指定设备' },
41 | CONNECTION_FAIL: { code: 10003, message: '连接失败' },
42 | NO_SERVICE: { code: 10004, message: '没有找到指定服务' },
43 | NO_CHARACTERISTIC: { code: 10005, message: '没有找到指定特征值' },
44 | NO_CONNECTION: { code: 10006, message: '当前连接已断开' },
45 | PROPERTY_NOT_SUPPORT: { code: 10007, message: '当前特征值不支持此操作' },
46 | SYSTEM_ERROR: { code: 10008, message: '系统异常' },
47 | SYSTEM_NOT_SUPPORT: { code: 10009, message: 'Android 系统版本低于 4.3 不支持 BLE' },
48 | SYMBOL_UNFOUND: { code: 10010, message: '没有找到指定描述符' },
49 | DEVICE_ID_INVALID: { code: 10011, message: '设备 id 不可用/为空' },
50 | SERVICE_ID_INVALID: { code: 10012, message: '服务 id 不可用/为空' },
51 | CHARACTERISTIC_ID_INVALID: { code: 10013, message: '特征 id 不可用/为空' },
52 | CMD_FORMAT_ERROR: { code: 10014, message: '发送的数据为空或格式错误' },
53 | OPERATION_TIMEOUT: { code: 10015, message: '操作超时' },
54 | LACK_PARAMETER: { code: 10016, message: '缺少参数' },
55 | WRITE_ERROR: { code: 10017, message: '写入特征值失败' },
56 | READ_ERROR: { code: 10018, message: '读取特征值失败' },
57 | }
58 |
59 | const ERROR_TYPES = mini==='wx' ? ERROR_WX : ERROR_ANT;
60 |
61 | /**
62 | * 获取错误类型
63 | */
64 | function getErrorType(code) {
65 | for (let key of Object.keys(ERROR_TYPES)) {
66 | if (code === ERROR_TYPES[key].code) {
67 | return ERROR_TYPES[key];
68 | }
69 | }
70 | return ERROR_TYPES.UN_KNOWN;
71 | }
72 |
73 | /**
74 | * 获取小程序平台 wx||ant
75 | */
76 | export function getAppPlatform() {
77 | return mini;
78 | }
79 |
80 | /**
81 | * 重写异步API
82 | *
83 | * @param {string} fn1 方法名1
84 | * @param {string} fn2 方法名2
85 | * @param {object} options 可选参数
86 | *
87 | * @return Promise对象
88 | */
89 | export function api(fn1, fn2, options) {
90 | let func = ii[fn1] || ii[fn2];
91 | return new Promise((resolve, reject) => {
92 | let params = {
93 | success(res) {
94 | resolve(res);
95 | },
96 | fail(res) {
97 | if (res.errorMessage) {
98 | reject({
99 | code: res.error,
100 | message: res.errorMessage
101 | });
102 | } else {
103 | reject({
104 | ...getErrorType(res.errCode),
105 | errMsg: res.errMsg
106 | });
107 | }
108 | },
109 | complete(res) {
110 | //...
111 | }
112 | }
113 | if (options) {
114 | params = Object.assign(params, options);
115 | }
116 | if (func) {
117 | func(params);
118 | } else {
119 | reject(ErrorApiCatch.Error_Low_Version);
120 | }
121 | })
122 | }
123 |
124 | /**
125 | * 重写回调API
126 | *
127 | * @param {string} fn1 方法名1
128 | * @param {string} fn2 方法名2
129 | * @param {function} cb 回调函数
130 | */
131 | export function on(fn1, fn2, cb) {
132 | let func = ii[fn1] || ii[fn2];
133 | if (func) {
134 | if (mini === 'wx') {
135 | func(cb);
136 | } else {
137 | func({
138 | success: cb,
139 | })
140 | }
141 | }
142 | }
--------------------------------------------------------------------------------
/src/tools.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import { getAppPlatform , api , on} from './promisify.js';
5 |
6 | /**
7 | * 判断字符串是否为空或者空格
8 | */
9 | function isEmpty(str = '') {
10 | return !str || str == '' || str.replace(/(^\s*)|(\s*$)/g, "") == "";
11 | }
12 |
13 | /**
14 | * 判断是否为null或者未定义
15 | */
16 | function isNullOrUndefined(obj) {
17 | return obj === undefined || obj === null;
18 | }
19 |
20 | /**
21 | * ArrayBuffer类型转换为16进制字符串
22 | */
23 | function ab2str(buffer) {
24 | return Array.prototype.map.call(new Uint8Array(buffer), x => ('00' + x.toString(16)).slice(-2)).join('');
25 | }
26 |
27 | /**
28 | * 字符串转为ArrayBuffer对象,参数为字符串
29 | */
30 | function str2ab(str) {
31 | var buf = new ArrayBuffer(str.length / 2);
32 | var bufView = new Uint8Array(buf);
33 | for (var i = 0; i < str.length; i += 2) {
34 | bufView[parseInt(i / 2)] = char2Hex(str.charCodeAt(i)) << 4 | char2Hex(str.charCodeAt(i + 1));
35 | }
36 | return buf;
37 | }
38 |
39 | /**
40 | * 字符转十六进制
41 | */
42 | function char2Hex(bChar) {
43 | if ((bChar >= 0x30) && (bChar <= 0x39)) { // 数字
44 | bChar -= 0x30;
45 | } else if ((bChar >= 0x41) && (bChar <= 0x46)) { // 大写字母
46 | bChar -= 0x37;
47 | } else if ((bChar >= 0x61) && (bChar <= 0x66)) { // 小写字母
48 | bChar -= 0x57;
49 | } else {
50 | bChar = 0xff;
51 | }
52 | return bChar;
53 | }
54 |
55 | /**
56 | * TypedArray转为ArrayBuffer
57 | */
58 | function typedArray2ArrayBuffer(pbuff) {
59 | let buffer = new ArrayBuffer(pbuff.byteLength)
60 | let uInit8 = new Uint8Array(buffer)
61 | uInit8.set(pbuff);
62 | return buffer;
63 | }
64 |
65 | /**
66 | * 获取异或校验数值
67 | *
68 | * @param p 参与运算的字符数组指针
69 | * @param len 参与运算的字符数组长度
70 | */
71 | function createXOR(b, p, len) {
72 | let i = 0;
73 | let ckc = 0;
74 | for (; i < len; i++) {
75 | ckc = ckc ^ b[p + i];
76 | }
77 | return ckc;
78 | }
79 |
80 | /**
81 | * UUID128位转换为16位
82 | *
83 | * @param uuid128 128位的uuid
84 | */
85 | function uuid128to16(uuid128) {
86 | let arr = uuid128.split('-');
87 | if (arr.length === 5 && arr[4] === '00805F9B34FB') {
88 | return arr[0].slice(3);
89 | } else {
90 | return uuid128;
91 | }
92 | }
93 |
94 | export default {
95 | getAppPlatform,
96 | api,
97 | on,
98 | isEmpty,
99 | isNullOrUndefined,
100 | ab2str,
101 | str2ab,
102 | typedArray2ArrayBuffer,
103 | createXOR,
104 | uuid128to16
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------