mini_program_gizwits_sdk 3.2.20-beta → 3.2.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/sdk.ts CHANGED
@@ -9,6 +9,7 @@ import { errorCode } from '..';
9
9
  import { AnonymousLogin, ILoginRes } from './services/login';
10
10
  import {
11
11
  bindMac,
12
+ editBindInfo,
12
13
  getBindingList,
13
14
  IOpenApiDevice,
14
15
  ISafeRegisterReturn,
@@ -26,13 +27,29 @@ import {
26
27
  } from './utils';
27
28
  import productConfigFileManage from './productConfigFileManage';
28
29
  import { formatEnum, IDataPointConfig, pack, unpack } from './protocol/DataPoint';
29
- import { hexStrint2byte } from './protocol/tool';
30
+ import { arrayToString, hexStrint2byte } from './protocol/tool';
30
31
  import { LanHandle } from './handler/lan';
32
+ import ProtocolBase from './protocol/ProtocolBase';
33
+ import { CancelSync, DeletePackage, RequestSync, SyncPackage } from './protocol/OffLineData';
34
+ import { uploadP0 } from './services/uploadP0';
35
+ import sleep from './sleep';
36
+ import Ntp from './protocol/Ntp';
37
+
38
+ interface SyncCallBackParams {
39
+ event: TSyncEvnet;
40
+ currentNum?: number;
41
+ totalNum?: number;
42
+ message?: string;
43
+ }
44
+ type TSyncEvnet = 'SYNC_START' | 'SYNC_END' | 'SYNC_FAIL' | 'SYNC_CANCEL' | 'SYNC_PROGRESS';
45
+ export type SyncCallBack = (data: SyncCallBackParams) => void
46
+
31
47
 
32
48
  // 接口返回格式
33
- interface ISDKResult<T> {
49
+ export interface ISDKResult<T> {
34
50
  data?: T;
35
51
  err?: IError;
52
+ message?: string;
36
53
  success: boolean;
37
54
  }
38
55
  export interface ISetCommonDeviceOnboardingDeployProps {
@@ -182,7 +199,7 @@ class GizwitsMiniSDK {
182
199
  const index = newDevices.findIndex(
183
200
  (device) => device.bleDeviceID === item.deviceId
184
201
  );
185
- if (index !== -1 && item.isLogin) {
202
+ if (index !== -1) {
186
203
  newDevices[index].connectType = 'BLE';
187
204
  }
188
205
  });
@@ -240,6 +257,15 @@ class GizwitsMiniSDK {
240
257
  private _gizSocket: GizwitsWS;
241
258
  private offlineThreshold?: number;
242
259
 
260
+ // 离线同步
261
+ private syncDataCallBack?: SyncCallBack = null
262
+ private syncDataLengthCallback?: any;
263
+ private syncTotalNum: number = 0
264
+ private syncCurrnetNum: number = 0
265
+ private syncDataTimoutTimer: any = null;
266
+ private SYNC_TIMEOUT: number = 4 * 60 * 1000
267
+ private syncDevice?: IDevice;
268
+
243
269
  constructor({
244
270
  appID,
245
271
  appSecret,
@@ -271,6 +297,7 @@ class GizwitsMiniSDK {
271
297
  * 初始化sdk的时候就要开始扫描设备
272
298
  */
273
299
  this.initLan();
300
+
274
301
  }
275
302
 
276
303
  private get bleHandle() {
@@ -324,7 +351,6 @@ class GizwitsMiniSDK {
324
351
  };
325
352
 
326
353
  private handleBleDevices = (devices: IDevice[]) => {
327
- GizLog.log('handleBleDevices', devices);
328
354
  this.bleDevices = devices;
329
355
  // this.notiDeviceList();
330
356
  };
@@ -358,15 +384,157 @@ class GizwitsMiniSDK {
358
384
  (item) => item.bleDeviceID === curDevice.deviceId
359
385
  );
360
386
  if (target) {
361
- const data = await unpack(hexStrint2byte(hexString), target.productKey);
362
- // 如果有kydata 则上报
363
- if (data?.kvData && this.listenerMap['GizDeviceAttrsNotifications']) {
364
- this.listenerMap['GizDeviceAttrsNotifications'].map((item) => {
365
- item({
366
- device: target,
367
- data: data?.kvData,
368
- });
369
- });
387
+ const bleBytesData = hexStrint2byte(hexString)
388
+ const parseData = new ProtocolBase(bleBytesData);
389
+ switch (parseData.cmd) {
390
+ case '0093':
391
+ case '0094':
392
+ case '0090':
393
+ case '0091': {
394
+ // 设备上报的指令,上传给后台
395
+ console.debug('handleBleDeviceData', hexString, target)
396
+ if (target.passcode) {
397
+ const productConfig = this.productInfo.find(item => item.productKey === target.productKey)
398
+ if (productConfig) {
399
+ const uploadTime = parseInt(`${Date.now() / 1000}`, 10)
400
+ uploadP0({
401
+ device: target,
402
+ data: [
403
+ {
404
+ raw: hexString,
405
+ created_at: uploadTime
406
+ }
407
+ ],
408
+ productSecret: productConfig.productSecret,
409
+ }).then(data => {
410
+ GizLog.debug("设备上报数据:timestemp", new Date(uploadTime * 1000).toLocaleString(), data)
411
+ })
412
+ }
413
+
414
+ }
415
+ const data = await unpack(hexStrint2byte(hexString), target.productKey);
416
+ // 如果有kydata 则上报
417
+ if (data?.kvData && this.listenerMap['GizDeviceAttrsNotifications']) {
418
+ this.listenerMap['GizDeviceAttrsNotifications'].map((item) => {
419
+ item({
420
+ device: target,
421
+ data: data?.kvData,
422
+ });
423
+ });
424
+ }
425
+ break
426
+ }
427
+ case '0052': {
428
+ // 设备回复是否允许同步数据
429
+ const requestSyncData = new RequestSync(bleBytesData)
430
+ GizLog.debug("设备回复是否允许同步, state:" , requestSyncData.state)
431
+
432
+ this.syncDataLengthCallback && this.syncDataLengthCallback({success: requestSyncData.state == 0, data: requestSyncData.len})
433
+
434
+ if (!this.syncDataCallBack) break;
435
+ if (requestSyncData.state === 0) {
436
+ // 开始同步
437
+ this.syncCurrnetNum = 0;
438
+ this.syncTotalNum = requestSyncData.len;
439
+ this.syncDataCallBack({
440
+ event: 'SYNC_START',
441
+ totalNum: this.syncTotalNum,
442
+ currentNum: this.syncCurrnetNum
443
+ })
444
+ // 读取第0条
445
+ GizLog.debug("查询第一条离线数据")
446
+ await this.bleHandle.write(curDevice.deviceId, numberArray2Uint8Array(SyncPackage.pack(0)).buffer)
447
+ } else {
448
+ GizLog.debug("设备返回状态为1,同步失败")
449
+
450
+ // 同步失败
451
+ this.syncDataCallBack({
452
+ event: 'SYNC_FAIL',
453
+ })
454
+ }
455
+ break
456
+ }
457
+ case '0054': {
458
+ if (!this.syncDataCallBack) break;
459
+ // 设备上报需要同步的数据
460
+ GizLog.debug("设备上报离线数据")
461
+
462
+ const syncPackageData = new SyncPackage(bleBytesData)
463
+ const deletePackageCmd = DeletePackage.pack(syncPackageData.id)
464
+ GizLog.debug("设备上报离线数据:payload", syncPackageData.payload)
465
+ try {
466
+ GizLog.debug("设备上报离线数据:timestemp", new Date(syncPackageData.timestemp * 1000).toLocaleString())
467
+ } catch (error) {
468
+
469
+ }
470
+
471
+ const productConfig = this.productInfo.find(item => item.productKey === this.syncDevice.productKey)
472
+ if(productConfig) {
473
+ const uploadRes = await uploadP0({
474
+ device: this.syncDevice,
475
+ data: [
476
+ {
477
+ raw: arrayToString(syncPackageData.payload),
478
+ created_at: syncPackageData.timestemp
479
+ }
480
+ ],
481
+ productSecret: productConfig.productSecret
482
+ })
483
+ GizLog.debug("上传离线数据结果", uploadRes)
484
+ }
485
+
486
+ this.syncCurrnetNum += 1;
487
+ // 通知设备删除
488
+ this.syncDataCallBack({
489
+ event: "SYNC_PROGRESS",
490
+ totalNum: this.syncTotalNum,
491
+ currentNum: this.syncCurrnetNum
492
+ })
493
+
494
+ GizLog.debug("通知设备删除:", syncPackageData.id)
495
+ await this.bleHandle.write(curDevice.deviceId, numberArray2Uint8Array(deletePackageCmd).buffer)
496
+ await sleep(100)
497
+ // 查询新数据
498
+ if (syncPackageData.id < this.syncTotalNum - 1) {
499
+ GizLog.debug("查询新数据:", syncPackageData.id + 1)
500
+ await this.bleHandle.write(curDevice.deviceId, numberArray2Uint8Array(SyncPackage.pack(syncPackageData.id + 1)).buffer)
501
+ } else {
502
+ // 结束同步
503
+ GizLog.debug("send cancel sync to device")
504
+ this.bleHandle.write(curDevice.deviceId, numberArray2Uint8Array(CancelSync.pack()).buffer)
505
+ this.syncDataCallBack({
506
+ event: "SYNC_END",
507
+ })
508
+ // 清除相关数据
509
+ this.cleanSyncDeviceData();
510
+ }
511
+ break
512
+ }
513
+ case '0058': {
514
+ if (!this.syncDataCallBack) break;
515
+ // 设备上报停止同步
516
+
517
+ const cancelData = new CancelSync(bleBytesData)
518
+ GizLog.debug("设备上报停止同步 state:", cancelData.state )
519
+
520
+ if (cancelData.state === 0) {
521
+ // 同步完成
522
+ this.syncDataCallBack({
523
+ event: "SYNC_END",
524
+ })
525
+ } else {
526
+ this.syncDataCallBack({
527
+ event: "SYNC_FAIL",
528
+ })
529
+ }
530
+ // 清除相关数据
531
+ this.cleanSyncDeviceData();
532
+ break
533
+ }
534
+ case '0060': {
535
+ // NTP回复
536
+ break
537
+ }
370
538
  }
371
539
  }
372
540
  };
@@ -402,7 +570,7 @@ class GizwitsMiniSDK {
402
570
  );
403
571
  GizLog.log('handleSocketStatus', dids, enabled);
404
572
  devices.forEach((item) => {
405
- this.setDeviceMeta(item, 'connectType', enabled ? 'NONE' : 'WAN');
573
+ this.setDeviceMeta(item, 'connectType', enabled ? 'WAN' : 'NONE');
406
574
  });
407
575
  };
408
576
 
@@ -433,7 +601,7 @@ class GizwitsMiniSDK {
433
601
  /**
434
602
  * @description 开启后台自动扫描
435
603
  */
436
- public startAutoScan = () => {
604
+ public startAutoScan = (services?: string[]) => {
437
605
  // 保持autoscan 运行 因为一些蓝牙的操作会导致搜索终止
438
606
  const autoTime = 9000;
439
607
 
@@ -451,11 +619,11 @@ class GizwitsMiniSDK {
451
619
  // }
452
620
  this.bleHandle.startScan((deviceList) => {
453
621
  this.bleDevices = deviceList;
454
- }, -1);
622
+ }, -1, services);
455
623
  }, autoTime);
456
624
  this.bleHandle.startScan((deviceList) => {
457
625
  this.bleDevices = deviceList;
458
- }, -1);
626
+ }, -1, services);
459
627
  };
460
628
 
461
629
  /**
@@ -523,12 +691,13 @@ class GizwitsMiniSDK {
523
691
  * @returns {success:boolean,data:IDevice[]}
524
692
  */
525
693
  public scanBleDevice = async (
526
- delay: number
694
+ delay: number,
695
+ services?: string[]
527
696
  ): Promise<ISDKResult<IDevice[]>> => {
528
697
  this.bleHandle.scanList = [];
529
698
  const data = await this.bleHandle.startScan((deviceList) => {
530
699
  this.bleDevices = deviceList;
531
- }, delay);
700
+ }, delay, services);
532
701
  return {
533
702
  success: true,
534
703
  data: data.scanList,
@@ -618,6 +787,120 @@ class GizwitsMiniSDK {
618
787
  };
619
788
  };
620
789
 
790
+ // 设置设备的时间戳
791
+ setDeviceTimeStamp = async (device: IDevice) => {
792
+ const target = this.allDevices.find((item) => isSameDevice(item, device));
793
+ if (!target) {
794
+ return { success: false, message: 'target is undefind' };
795
+ }
796
+
797
+ const data = Ntp.pack()
798
+
799
+ switch (target.connectType) {
800
+ case 'BLE': {
801
+ return this.bleHandle.write(device.bleDeviceID, numberArray2Uint8Array(data).buffer)
802
+ }
803
+ }
804
+ return {
805
+ success: false,
806
+ message: 'the connectType not support set timestamp'
807
+ }
808
+ }
809
+
810
+ // 同步蓝牙设备的离线数据
811
+ syncDeviceData =async (device: IDevice, callback: SyncCallBack = () => {}) => {
812
+ const target = this.allDevices.find((item) => isSameDevice(item, device));
813
+ if (!target) {
814
+ return { success: false, message: 'target is undefind' };
815
+ }
816
+ if (!target.isBind) {
817
+ return { success: false, message: 'Only the bound devices are supported' };
818
+ }
819
+
820
+ if (this.syncDevice) {
821
+ return {
822
+ success: false,
823
+ message: 'Synchronizing'
824
+ }
825
+ }
826
+
827
+ this.syncDevice = {...target}
828
+
829
+ switch (target.connectType) {
830
+ case 'BLE': {
831
+ GizLog.debug("start syncDeviceData", device);
832
+ this.syncDataCallBack = callback;
833
+ this.syncDataTimoutTimer = setTimeout(() => {
834
+ this.syncDataCallBack({
835
+ event: 'SYNC_FAIL',
836
+ message: 'sync timeout'
837
+ })
838
+ this.cleanSyncDeviceData();
839
+ }, this.SYNC_TIMEOUT)
840
+ // 设置超时
841
+ const data = RequestSync.pack();
842
+ // 发送开始同步的指令
843
+ GizLog.debug("sent RequestSync data");
844
+ return this.bleHandle.write(device.bleDeviceID, numberArray2Uint8Array(data).buffer)
845
+ }
846
+ }
847
+ return {
848
+ success: false,
849
+ message: 'the connectType not support sync data'
850
+ }
851
+ }
852
+
853
+ // 查询设备里面还有多少数据需要同步
854
+ queryNeedSyncDataLength = async(device: IDevice): Promise<ISDKResult<number>> => {
855
+ const target = this.allDevices.find((item) => isSameDevice(item, device));
856
+ if (!target) {
857
+ return { success: false, message: 'target is undefind' };
858
+ }
859
+ if (!target.isBind) {
860
+ return { success: false, message: 'Only the bound devices are supported' };
861
+ }
862
+ switch (target.connectType) {
863
+ case 'BLE': {
864
+ const data = RequestSync.pack();
865
+ // 发送开始同步的指令
866
+ this.syncDevice = {...target}
867
+ GizLog.debug("sent RequestSync data len");
868
+ this.bleHandle.write(device.bleDeviceID, numberArray2Uint8Array(data).buffer)
869
+ return new Promise((res) => {
870
+ this.syncDataLengthCallback = res;
871
+ setTimeout(() => {
872
+ try {
873
+ res({success: false, message: 'timeout'})
874
+ } catch (error) {
875
+
876
+ }
877
+ }, 3000)
878
+ })
879
+ }
880
+ }
881
+ return {
882
+ success: false,
883
+ message: 'the connectType not support sync data'
884
+ }
885
+ }
886
+
887
+ public cancelSyncDeviceData =async () => {
888
+ this.cleanSyncDeviceData();
889
+ }
890
+
891
+ private cleanSyncDeviceData = () => {
892
+ if (this.syncDataCallBack) {
893
+ this.syncDataCallBack({
894
+ event: 'SYNC_CANCEL'
895
+ })
896
+ this.syncDataCallBack = null;
897
+ }
898
+ this.syncCurrnetNum = 0;
899
+ this.syncTotalNum = 0;
900
+ this.syncDevice = null;
901
+ this.syncDataTimoutTimer && clearTimeout(this.syncDataTimoutTimer)
902
+ }
903
+
621
904
  getProductConfig = async (
622
905
  pk: string
623
906
  ): Promise<ISDKResult<IDataPointConfig>> => {
@@ -713,8 +996,13 @@ class GizwitsMiniSDK {
713
996
  // 连接BLE
714
997
  const data = await this.bleHandle.connectDevice(target.bleDeviceID);
715
998
  if (data.success) {
999
+
716
1000
  // 蓝牙设备比较特殊,在get 里面从bleHandle里找到连接的设备,再把connectType 设置成BLE,所以这里更新metaData 要强制更新并推送
717
1001
  this.setDeviceMeta(target, 'connectType', 'BLE', true);
1002
+ setTimeout(() => {
1003
+ // 蓝牙设备连接成功之后默认设置一次ntp
1004
+ this.setDeviceTimeStamp(target)
1005
+ }, 250)
718
1006
  setTimeout(() => {
719
1007
  this.getDeviceStatus(target);
720
1008
  }, 500);
@@ -988,6 +1276,25 @@ class GizwitsMiniSDK {
988
1276
  return GizLog.setLogLevel(level);
989
1277
  }
990
1278
 
1279
+ public renameDevice =async (device:IDevice, name: string) => {
1280
+ if (!name) {
1281
+ return {
1282
+ success: false,
1283
+ message: "please input device name"
1284
+ }
1285
+ }
1286
+ if (device.isBind) {
1287
+ return await editBindInfo({
1288
+ did: device.did,
1289
+ name: name,
1290
+ })
1291
+ }
1292
+ return {
1293
+ success: false,
1294
+ message: "device not bind"
1295
+ }
1296
+ }
1297
+
991
1298
  /**
992
1299
  * 移除事件监听
993
1300
  * @param type 事件监听名称,详见下表
@@ -1038,5 +1345,4 @@ class GizwitsMiniSDK {
1038
1345
  this.keepScanTimer && clearInterval(this.keepScanTimer);
1039
1346
  };
1040
1347
  }
1041
-
1042
- export default GizwitsMiniSDK;
1348
+ export default GizwitsMiniSDK;
@@ -45,6 +45,7 @@ export const getBindingList = async (): Promise<IServiceResult<IDevice[]>> => {
45
45
  port: item.port,
46
46
  ws_port: item.ws_port,
47
47
  wss_port: item.wss_port,
48
+ passcode: item.passcode,
48
49
  isBleOnline: false,
49
50
  isLanOnline: false,
50
51
  connectType: 'NONE',
@@ -208,3 +209,35 @@ export async function safeRegister({
208
209
  err: data.err,
209
210
  };
210
211
  }
212
+
213
+ interface IRenameProps {
214
+ name?: string;
215
+ did: string;
216
+ remark?: string;
217
+ }
218
+ export interface IRenamePropsReturn {
219
+ remark: string;
220
+ dev_alias: string;
221
+ }
222
+ export async function editBindInfo({
223
+ name,
224
+ remark,
225
+ did,
226
+ }: IRenameProps): Promise<IServiceResult<IRenamePropsReturn>> {
227
+ const body: any = {};
228
+ if (name) {
229
+ body.dev_alias = name;
230
+ }
231
+ if (remark) {
232
+ body.remark = remark;
233
+ }
234
+ const data = await openApiRequest<IRenamePropsReturn>(
235
+ `/app/bindings/${did}`,
236
+ {
237
+ method: 'PUT',
238
+ data: body,
239
+ },
240
+ true
241
+ );
242
+ return data;
243
+ }
@@ -0,0 +1,56 @@
1
+ import openApiRequest from "../openApiRequest";
2
+ import { enc, AES, mode, pad } from 'crypto-js';
3
+
4
+ export interface IUploadRes {
5
+ }
6
+
7
+ interface IData {
8
+ raw: string;
9
+ created_at: number;
10
+ }
11
+
12
+ interface IProps {
13
+ device: IDevice;
14
+ data: IData[];
15
+ productSecret: string;
16
+ }
17
+ export async function uploadP0 ({device, data, productSecret}: IProps) {
18
+ const path = `/v2/products/${device.productKey}/devices/offline-data`;
19
+
20
+ let passCode = device.passcode
21
+
22
+ const key = enc.Hex.parse(productSecret);
23
+ const iv = enc.Hex.parse('');
24
+ const srcs = enc.Utf8.parse(passCode);
25
+ const encrypted = AES.encrypt(srcs, key, { iv: iv, mode: mode.ECB, padding: pad.Pkcs7 });
26
+ passCode = encrypted.ciphertext.toString().toUpperCase();
27
+
28
+ return openApiRequest<IUploadRes>(path, {
29
+ data: {
30
+ data,
31
+ encoding: 'hex',
32
+ },
33
+ method: 'POST',
34
+ headers: {
35
+ 'X-Gizwits-Device-Id': device.did,
36
+ 'X-Gizwits-Device-passcode': passCode,
37
+ },
38
+ }, false)
39
+ }
40
+
41
+ // setTimeout(() => {
42
+ // uploadP0({
43
+ // device: {
44
+ // productKey: "8da73e49a9c74d6e9321b4383b44e4e9",
45
+ // did: "SlJJzMGa0MDOmhUTt0PIul",
46
+ // passcode: "1234567890",
47
+ // },
48
+ // data: [
49
+ // {
50
+ // raw: "00000003",
51
+ // created_at: parseInt(`${Date.now() / 1000}`, 10)
52
+ // }
53
+ // ],
54
+ // productSecret: "bf5df28e904b455f85fa5be1805945ad"
55
+ // })
56
+ // }, 3000)
package/src/utils.ts CHANGED
@@ -180,6 +180,11 @@ export const merageBleLocalDevices = (
180
180
  mainDevice: IDevice[],
181
181
  localDevice: IDevice[]
182
182
  ) => {
183
+ mainDevice.map(device=> {
184
+ // 重置成离线
185
+ device.isBleOnline = false;
186
+ device.bleWorkStatus = 0;
187
+ })
183
188
  localDevice.map((localD) => {
184
189
  const target = mainDevice.find((device) => isSameDevice(device, localD));
185
190
  if (!target) {
@@ -200,6 +205,10 @@ export const merageLanLocalDevices = (
200
205
  mainDevice: IDevice[],
201
206
  localDevice: IDevice[]
202
207
  ) => {
208
+ mainDevice.map(device=> {
209
+ // 重置成离线
210
+ device.isLanOnline = false;
211
+ })
203
212
  localDevice.map((localD) => {
204
213
  const target = mainDevice.find((device) => isSameDevice(device, localD));
205
214
  if (!target) {
package/src/wechatApi.ts CHANGED
@@ -67,7 +67,7 @@ export function startBluetoothDevicesDiscovery(allowDuplicatesKey = true) {
67
67
  fail: rej,
68
68
  allowDuplicatesKey,
69
69
  powerLevel: 'high',
70
- interval: 0
70
+ interval: 200
71
71
  })
72
72
  })
73
73
  }