@ray-js/lock-sdk 1.1.3-beta.5 → 1.1.3-beta.7

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/lib/api/temp.d.ts CHANGED
@@ -197,11 +197,31 @@ interface ReNameOfflineParams {
197
197
  unlockBindingId: string;
198
198
  }
199
199
  export declare const reNameOffline: (params: ReNameOfflineParams) => Promise<unknown>;
200
+ export interface SyncTempPwdScheduleItem {
201
+ allDay: boolean;
202
+ effectiveTime: number;
203
+ invalidTime: number;
204
+ workingDay: number;
205
+ }
206
+ export interface SyncTempPwdItem {
207
+ id: number;
208
+ opt: 4 | 5 | 6;
209
+ sn: number;
210
+ tyNum: number;
211
+ password: string;
212
+ availTime: number;
213
+ phase: number;
214
+ effectiveTime: number;
215
+ invalidTime: number;
216
+ scheduleList: SyncTempPwdScheduleItem[];
217
+ }
218
+ export interface GetSyncTempPasswordsResult {
219
+ count: number;
220
+ pwdList: SyncTempPwdItem[];
221
+ }
200
222
  interface GetSyncTempPasswordsParams {
201
223
  devId: string;
202
224
  isUpt: boolean;
203
225
  }
204
- export declare const getSyncTempPasswords: (params: GetSyncTempPasswordsParams) => Promise<{
205
- pwdList: any[];
206
- }>;
226
+ export declare const getSyncTempPasswords: (params: GetSyncTempPasswordsParams) => Promise<GetSyncTempPasswordsResult>;
207
227
  export {};
@@ -16,6 +16,7 @@ declare const dpCodes: {
16
16
  tempPwdDel: string;
17
17
  tempPwdEditW: string;
18
18
  tempPwdEdit: string;
19
+ bleSyncRequest: string;
19
20
  unlockPassword: string;
20
21
  unlockFingerprint: string;
21
22
  unlockFingerVein: string;
@@ -16,6 +16,7 @@ const dpCodes = {
16
16
  tempPwdDel: "temporary_password_delete",
17
17
  tempPwdEditW: "temporary_password_modify_w",
18
18
  tempPwdEdit: "temporary_password_modify",
19
+ bleSyncRequest: "ble_sync_request",
19
20
  unlockPassword: "unlock_password",
20
21
  unlockFingerprint: "unlock_fingerprint",
21
22
  unlockFingerVein: "unlock_finger_vein",
@@ -0,0 +1,3 @@
1
+ import { DpMap } from "@ray-js/tuya-dp-transform";
2
+ export declare const bleSyncRequest: DpMap;
3
+ export declare const reportBleSyncRequest: DpMap;
@@ -0,0 +1,17 @@
1
+ export const bleSyncRequest = [
2
+ {
3
+ name: "cmd",
4
+ },
5
+ ];
6
+ export const reportBleSyncRequest = [
7
+ {
8
+ name: "cmd",
9
+ },
10
+ {
11
+ condition: {
12
+ prop: "cmd",
13
+ value: 0x01,
14
+ },
15
+ name: "dataType",
16
+ },
17
+ ];
package/lib/state.js CHANGED
@@ -11,7 +11,7 @@ import { getRotateInfo } from "./api/video";
11
11
  import syncT0 from "./sync/t0";
12
12
  import syncRemoteSerectKey from "./sync/remote-serect-key";
13
13
  import { autoSyncUnlockMethod } from "./sync/unlock-method";
14
- import { autoSyncTemp } from "./sync/temp";
14
+ import { autoSyncTemp, handleBleSyncRequestReport } from "./sync/temp";
15
15
  import { DEVICE_STATUS_CHANGE_EVENT, SLEEP_EVENT } from "./event";
16
16
  import { getSleepPeriod, isSleep } from "./sleep";
17
17
  import { ProductCommunicationType } from "./constant";
@@ -165,6 +165,9 @@ const handleDpChange = (dps, notEmit) => {
165
165
  }, 2000);
166
166
  }
167
167
  checkOfflineDpUpdate(dpData);
168
+ if (typeof dpData[dpCodes.bleSyncRequest] !== "undefined") {
169
+ handleBleSyncRequestReport(dpData[dpCodes.bleSyncRequest]);
170
+ }
168
171
  }
169
172
  };
170
173
  const handleDeviceDpsChange = ({ dps, deviceId, }) => {
@@ -0,0 +1,2 @@
1
+ import { SyncTempPwdItem } from "../api/temp";
2
+ export declare function buildBleTempPwdSyncPayload(pwdList: SyncTempPwdItem[]): Promise<string>;
@@ -0,0 +1,91 @@
1
+ import config from "../config";
2
+ import { formatWeek, parseWeek } from "../utils";
3
+ import { bytesToHex, concatBytes, uintToBytesBE } from "../utils/byte";
4
+ import { decrypt } from "../utils/device";
5
+ const SUB_CMD = 0x2003;
6
+ const PROTO_VERSION = 0x01;
7
+ const DATA_TYPE_TEMP_PWD = 0x01;
8
+ const OP_ADD = 0x01;
9
+ const OP_DEL = 0x02;
10
+ const OP_MOD = 0x03;
11
+ const PHASE_WAIT_FROZEN = 5;
12
+ const PHASE_FROZEN = 4;
13
+ let s_msgId = 0;
14
+ function buildTimeSet(item) {
15
+ const schedule = item.scheduleList?.[0];
16
+ const allDay = schedule?.allDay !== false;
17
+ const loopType = allDay ? 0x00 : 0x02;
18
+ let loopFlags = new Uint8Array(4);
19
+ if (!allDay && schedule) {
20
+ const weeks = parseWeek(schedule.workingDay);
21
+ loopFlags = uintToBytesBE(formatWeek(weeks), 4);
22
+ }
23
+ const startHour = allDay ? 0 : Math.floor((schedule?.effectiveTime ?? 0) / 60);
24
+ const startMinute = allDay ? 0 : (schedule?.effectiveTime ?? 0) % 60;
25
+ const endHour = allDay ? 0 : Math.floor((schedule?.invalidTime ?? 0) / 60);
26
+ const endMinute = allDay ? 0 : (schedule?.invalidTime ?? 0) % 60;
27
+ return concatBytes(uintToBytesBE(item.effectiveTime, 4), uintToBytesBE(item.invalidTime, 4), new Uint8Array([loopType]), loopFlags, new Uint8Array([startHour, startMinute, endHour, endMinute]));
28
+ }
29
+ function mapAvailTime(availTime) {
30
+ if (availTime === 0) {
31
+ return 0x00;
32
+ }
33
+ if (availTime >= 0xff) {
34
+ return 0xff;
35
+ }
36
+ return availTime & 0xff;
37
+ }
38
+ function mapState(phase) {
39
+ if (phase === PHASE_FROZEN || phase === PHASE_WAIT_FROZEN) {
40
+ return 0x00;
41
+ }
42
+ return 0x01;
43
+ }
44
+ async function resolvePwdBytes(password) {
45
+ if (!password) {
46
+ return [];
47
+ }
48
+ const plainPwd = await decrypt(config.devInfo.devId, password);
49
+ return plainPwd.split("").map((char) => Number(char));
50
+ }
51
+ async function buildItemBody(item, opCode) {
52
+ if (opCode === OP_DEL) {
53
+ return uintToBytesBE(item.sn, 2);
54
+ }
55
+ const pwdBytes = await resolvePwdBytes(item.password);
56
+ const state = mapState(item.phase);
57
+ const timeSet = buildTimeSet(item);
58
+ const availTime = mapAvailTime(item.availTime);
59
+ const commonTail = concatBytes(new Uint8Array([state]), timeSet, new Uint8Array([availTime, pwdBytes.length]), new Uint8Array(pwdBytes));
60
+ if (opCode === OP_ADD) {
61
+ return concatBytes(uintToBytesBE(item.tyNum, 2), commonTail);
62
+ }
63
+ return concatBytes(uintToBytesBE(item.sn, 2), commonTail);
64
+ }
65
+ function cloudOptToOpCode(opt) {
66
+ switch (opt) {
67
+ case 5:
68
+ return OP_ADD;
69
+ case 4:
70
+ return OP_DEL;
71
+ case 6:
72
+ return OP_MOD;
73
+ default:
74
+ return null;
75
+ }
76
+ }
77
+ export async function buildBleTempPwdSyncPayload(pwdList) {
78
+ const items = [];
79
+ for (const item of pwdList) {
80
+ const opCode = cloudOptToOpCode(item.opt);
81
+ if (opCode === null) {
82
+ continue;
83
+ }
84
+ const body = await buildItemBody(item, opCode);
85
+ items.push(concatBytes(new Uint8Array([opCode]), uintToBytesBE(body.length, 2), body));
86
+ }
87
+ const itemsBuf = concatBytes(...items);
88
+ s_msgId = (s_msgId + 1) & 0xffff;
89
+ const header = concatBytes(uintToBytesBE(SUB_CMD, 2), new Uint8Array([PROTO_VERSION, DATA_TYPE_TEMP_PWD]), uintToBytesBE(s_msgId, 2), new Uint8Array([items.length]), uintToBytesBE(itemsBuf.length, 2), itemsBuf);
90
+ return bytesToHex(header);
91
+ }
@@ -1,2 +1,3 @@
1
- export declare const syncTemp: () => Promise<void>;
1
+ export declare const notifyBleSyncRequest: () => Promise<void>;
2
2
  export declare const autoSyncTemp: () => void;
3
+ export declare const handleBleSyncRequestReport: (rawValue: string) => Promise<void>;
package/lib/sync/temp.js CHANGED
@@ -1,78 +1,70 @@
1
1
  import { getSyncTempPasswords } from "../api/temp";
2
- import config from "../config";
2
+ import config, { hasDp } from "../config";
3
3
  import dpCodes from "../config/dp-code";
4
- import { addTempPwd as addTempPwdDpMap, removeTempPwd as removeTempPwdDpMap, } from "../config/dp-map/unlock-method";
5
- import { addTempPwd as addTempPwdBigDpMap, removeTempPwd as removeTempPwdBigDpMap, } from "../config/dp-map/unlock-method-big";
6
- import { isUseNearChannel, parseWeek } from "../utils";
7
- import { decrypt } from "../utils/device";
8
- import { publishDps } from "../utils/publishDps";
4
+ import { bleSyncRequest as bleSyncRequestMap, reportBleSyncRequest as reportBleSyncRequestMap, } from "../config/dp-map/ble-sync";
5
+ import { isUseNearChannel, parallelOnly } from "../utils";
6
+ import { publishBLETransparentData } from "../utils/device";
7
+ import { publishDpsOnly } from "../utils/publishDps";
8
+ import { buildBleTempPwdSyncPayload } from "./ble-temp-payload";
9
9
  import dpUtils from "@ray-js/tuya-dp-transform";
10
- let syncTempFirst = true;
11
- export const syncTemp = async () => {
10
+ const CMD_REQUEST_SYNC = 0x01;
11
+ const DATA_TYPE_TEMP_PWD = 0x01;
12
+ const NOTIFY_DEBOUNCE_MS = 2000;
13
+ let notifyTimer = null;
14
+ let isSyncingPayload = false;
15
+ const hasBleSyncDp = () => hasDp(dpCodes.bleSyncRequest);
16
+ const publishBleSyncNotify = async (hasPending) => {
17
+ await publishDpsOnly({
18
+ [dpCodes.bleSyncRequest]: dpUtils.format({ cmd: hasPending ? 0x01 : 0x00 }, bleSyncRequestMap),
19
+ });
20
+ };
21
+ export const notifyBleSyncRequest = parallelOnly(async () => {
22
+ if (!isUseNearChannel() || !hasBleSyncDp()) {
23
+ return;
24
+ }
25
+ const { count } = await getSyncTempPasswords({
26
+ devId: config.devInfo.devId,
27
+ isUpt: false,
28
+ });
29
+ await publishBleSyncNotify(count > 0);
30
+ });
31
+ export const autoSyncTemp = () => {
32
+ if (!isUseNearChannel() || !hasBleSyncDp()) {
33
+ return;
34
+ }
35
+ if (notifyTimer) {
36
+ clearTimeout(notifyTimer);
37
+ }
38
+ notifyTimer = setTimeout(() => {
39
+ notifyTimer = null;
40
+ notifyBleSyncRequest().catch((e) => {
41
+ console.warn("ble_sync_request notify error", e);
42
+ });
43
+ }, NOTIFY_DEBOUNCE_MS);
44
+ };
45
+ export const handleBleSyncRequestReport = async (rawValue) => {
46
+ if (!hasBleSyncDp() || !isUseNearChannel() || isSyncingPayload) {
47
+ return;
48
+ }
49
+ const report = dpUtils.parse(rawValue, reportBleSyncRequestMap);
50
+ if (report.cmd !== CMD_REQUEST_SYNC || report.dataType !== DATA_TYPE_TEMP_PWD) {
51
+ return;
52
+ }
53
+ isSyncingPayload = true;
12
54
  try {
13
- syncTempFirst = false;
14
- const { pwdList } = await getSyncTempPasswords({
55
+ const { count, pwdList } = await getSyncTempPasswords({
15
56
  devId: config.devInfo.devId,
16
57
  isUpt: false,
17
58
  });
18
- const delDpCode = config.supportBigData
19
- ? dpCodes.tempPwdDelW
20
- : dpCodes.tempPwdDel;
21
- const delDpMap = config.supportBigData
22
- ? removeTempPwdBigDpMap
23
- : removeTempPwdDpMap;
24
- const addDpCode = config.supportBigData
25
- ? dpCodes.tempPwdCreateW
26
- : dpCodes.tempPwdCreate;
27
- const addDpMap = config.supportBigData
28
- ? addTempPwdBigDpMap
29
- : addTempPwdDpMap;
30
- pwdList?.forEach(async (syncItem) => {
31
- try {
32
- if (syncItem?.opt === 4) {
33
- publishDps({
34
- [delDpCode]: dpUtils.format({
35
- unlockId: syncItem?.sn,
36
- }, delDpMap),
37
- });
38
- }
39
- if (syncItem?.opt === 5) {
40
- const { allDay, invalidTime, effectiveTime, workingDay } = syncItem.scheduleList[0];
41
- const pwd = await decrypt(config.devInfo.devId, syncItem.password);
42
- const pswData = pwd.split("").map((item) => Number(item));
43
- publishDps({
44
- [addDpCode]: dpUtils.format({
45
- cloudNo: syncItem.tyNum,
46
- valid: true,
47
- validConfig: {
48
- startTime: syncItem.effectiveTime,
49
- endTime: syncItem.invalidTime,
50
- loop: allDay ? 0x00 : 0x01,
51
- loopConfig: 0,
52
- weeks: parseWeek(workingDay),
53
- days: 0,
54
- startHour: Math.floor(effectiveTime / 60),
55
- startMinute: effectiveTime % 60,
56
- endHour: Math.floor(invalidTime / 60),
57
- endMinute: invalidTime % 60,
58
- },
59
- validNum: 0,
60
- pwdLength: pswData.length,
61
- pwd: pswData,
62
- }, addDpMap),
63
- });
64
- }
65
- }
66
- catch (e) {
67
- console.warn("sync temp password error", e);
68
- }
69
- });
59
+ if (count > 0 && pwdList?.length) {
60
+ const payload = await buildBleTempPwdSyncPayload(pwdList);
61
+ await publishBLETransparentData(config.devInfo.devId, payload);
62
+ }
70
63
  }
71
64
  catch (e) {
72
- console.warn(e, "sync temp password error");
65
+ console.warn("ble_sync_request data transfer error", e);
73
66
  }
74
- };
75
- export const autoSyncTemp = () => {
76
- if (isUseNearChannel()) {
67
+ finally {
68
+ isSyncingPayload = false;
77
69
  }
78
70
  };
@@ -228,7 +228,9 @@ const addUnlockMethodData = {
228
228
  canEmitEvents: true,
229
229
  pendingEvents: [],
230
230
  releaseEmitTimer: 0,
231
+ eventSessionId: 0,
231
232
  };
233
+ const ADD_UNLOCK_METHOD_EVENT_RELEASE_DELAY = 16;
232
234
  const dispatchAddUnlockMethodEvent = (eventData) => {
233
235
  if (emitter.hasListener(UNLOCK_METHOD_EVENT)) {
234
236
  emitter.emit(UNLOCK_METHOD_EVENT, eventData);
@@ -244,14 +246,22 @@ const emitAddUnlockMethodEvent = (eventData) => {
244
246
  }
245
247
  dispatchAddUnlockMethodEvent(eventData);
246
248
  };
247
- const releaseAddUnlockMethodEventsAfterStart = () => {
249
+ const releaseAddUnlockMethodEventsAfterStart = (eventSessionId) => {
248
250
  clearTimeout(addUnlockMethodData.releaseEmitTimer);
249
251
  addUnlockMethodData.releaseEmitTimer = setTimeout(() => {
250
- addUnlockMethodData.canEmitEvents = true;
251
- const pendingEvents = addUnlockMethodData.pendingEvents.splice(0);
252
- pendingEvents.forEach((eventData) => {
253
- dispatchAddUnlockMethodEvent(eventData);
254
- });
252
+ if (addUnlockMethodData.eventSessionId !== eventSessionId) {
253
+ return;
254
+ }
255
+ addUnlockMethodData.releaseEmitTimer = setTimeout(() => {
256
+ if (addUnlockMethodData.eventSessionId !== eventSessionId) {
257
+ return;
258
+ }
259
+ addUnlockMethodData.canEmitEvents = true;
260
+ const pendingEvents = addUnlockMethodData.pendingEvents.splice(0);
261
+ pendingEvents.forEach((eventData) => {
262
+ dispatchAddUnlockMethodEvent(eventData);
263
+ });
264
+ }, ADD_UNLOCK_METHOD_EVENT_RELEASE_DELAY);
255
265
  }, 0);
256
266
  };
257
267
  const handleAddTimeout = (timeout = 15000) => {
@@ -398,21 +408,24 @@ const monitoringAddReport = (option) => {
398
408
  clearTimeout(addUnlockMethodData.releaseEmitTimer);
399
409
  addUnlockMethodData.canEmitEvents = false;
400
410
  addUnlockMethodData.pendingEvents = [];
411
+ addUnlockMethodData.eventSessionId += 1;
401
412
  emitter.clearCache(UNLOCK_METHOD_EVENT);
402
413
  emitter.off(DPCHANGE, handleAddReport);
403
414
  emitter.on(DPCHANGE, handleAddReport);
404
415
  handleAddTimeout(option.timeout);
416
+ return addUnlockMethodData.eventSessionId;
405
417
  };
406
418
  export const startAddUnlockMethod = async (params) => {
407
419
  const { unlockMethodConfig, lockUserId, dpData, sn, addDpCode, addDpMap, addDpReportMap, dpId, } = await getUnlockMethodBase(params.type, params.userId);
408
420
  isCancelAddUnlockMethod = false;
409
421
  let addReportMonitoringStarted = false;
422
+ let addReportMonitoringSessionId = 0;
410
423
  const startAddReportMonitoring = (total) => {
411
424
  if (addReportMonitoringStarted) {
412
- return;
425
+ return addReportMonitoringSessionId;
413
426
  }
414
427
  addReportMonitoringStarted = true;
415
- monitoringAddReport({
428
+ addReportMonitoringSessionId = monitoringAddReport({
416
429
  timeout: params.timeout,
417
430
  unlockMethodConfig,
418
431
  total,
@@ -423,6 +436,7 @@ export const startAddUnlockMethod = async (params) => {
423
436
  userId: params.userId,
424
437
  unlockDpId: dpId,
425
438
  });
439
+ return addReportMonitoringSessionId;
426
440
  };
427
441
  const res = await publishDps({
428
442
  [addDpCode]: dpUtils.format(dpData, addDpMap),
@@ -455,8 +469,8 @@ export const startAddUnlockMethod = async (params) => {
455
469
  },
456
470
  });
457
471
  if (res.stage === 0) {
458
- startAddReportMonitoring(res.total);
459
- releaseAddUnlockMethodEventsAfterStart();
472
+ const eventSessionId = startAddReportMonitoring(res.total);
473
+ releaseAddUnlockMethodEventsAfterStart(eventSessionId);
460
474
  return { total: res.total };
461
475
  }
462
476
  if (res.stage === 0xfd) {
@@ -1,3 +1,5 @@
1
1
  export declare function bytesToUint(data: Uint8Array): number;
2
2
  export declare function bytesToHex(data: Uint8Array): string;
3
+ export declare function uintToBytesBE(value: number, byteLen: number): Uint8Array;
4
+ export declare function concatBytes(...parts: Uint8Array[]): Uint8Array;
3
5
  export declare function stringToBytes(str: string): Uint8Array<ArrayBuffer>;
package/lib/utils/byte.js CHANGED
@@ -35,6 +35,25 @@ export function bytesToHex(data) {
35
35
  .join("")
36
36
  .toUpperCase();
37
37
  }
38
+ export function uintToBytesBE(value, byteLen) {
39
+ const out = new Uint8Array(byteLen);
40
+ let num = value >>> 0;
41
+ for (let i = byteLen - 1; i >= 0; i--) {
42
+ out[i] = num & 0xff;
43
+ num >>>= 8;
44
+ }
45
+ return out;
46
+ }
47
+ export function concatBytes(...parts) {
48
+ const total = parts.reduce((sum, part) => sum + part.length, 0);
49
+ const out = new Uint8Array(total);
50
+ let offset = 0;
51
+ parts.forEach((part) => {
52
+ out.set(part, offset);
53
+ offset += part.length;
54
+ });
55
+ return out;
56
+ }
38
57
  export function stringToBytes(str) {
39
58
  const bytes = [];
40
59
  const len = str.length;
@@ -172,6 +172,7 @@ export declare const navigateTo: (params: ParamType<typeof ty.navigateTo>) => vo
172
172
  export declare const checkShowMatterMutilpleShare: (deviceId: string) => Promise<unknown>;
173
173
  export declare const getBLEOnlineState: (deviceId: string) => Promise<boolean>;
174
174
  export declare const connectBLEDevice: (deviceId: string) => Promise<void>;
175
+ export declare const publishBLETransparentData: (deviceId: string, data: string) => Promise<void>;
175
176
  export declare const onAppShow: (cb: () => void) => void;
176
177
  export declare const offAppShow: (cb: () => void) => void;
177
178
  export declare const onAppHide: (cb: () => void) => void;
@@ -364,6 +364,16 @@ export const connectBLEDevice = (deviceId) => {
364
364
  });
365
365
  });
366
366
  };
367
+ export const publishBLETransparentData = (deviceId, data) => {
368
+ return new Promise((resolve, reject) => {
369
+ ty.device.publishBLETransparentData({
370
+ deviceId,
371
+ data,
372
+ success: () => resolve(),
373
+ fail: reject,
374
+ });
375
+ });
376
+ };
367
377
  export const onAppShow = (cb) => {
368
378
  ty.onAppShow(cb);
369
379
  };
package/lib/utils/log.js CHANGED
@@ -12,12 +12,12 @@ const localRecordUserOperate = [
12
12
  ];
13
13
  const unlockMethodMap = [
14
14
  "finger",
15
- "face",
16
15
  "password",
17
16
  "card",
17
+ "face",
18
18
  "fingerVein",
19
- "hand",
20
19
  "eye",
20
+ "hand",
21
21
  "remoteControl",
22
22
  ];
23
23
  export const handleLocalOperation = (log, dpValue) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/lock-sdk",
3
- "version": "1.1.3-beta.5",
3
+ "version": "1.1.3-beta.7",
4
4
  "files": [
5
5
  "lib",
6
6
  "LICENSE.md"