@homebridge-eufy-security/eufy-security-client 3.7.2-dev.0

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.
Files changed (129) hide show
  1. package/.prettierignore/342/200/216 +8 -0
  2. package/.prettierrc +11 -0
  3. package/LICENSE +21 -0
  4. package/README.md +970 -0
  5. package/build/error.d.ts +138 -0
  6. package/build/error.js +190 -0
  7. package/build/error.js.map +1 -0
  8. package/build/eufysecurity.d.ts +180 -0
  9. package/build/eufysecurity.js +3148 -0
  10. package/build/eufysecurity.js.map +1 -0
  11. package/build/http/api.d.ts +119 -0
  12. package/build/http/api.js +1877 -0
  13. package/build/http/api.js.map +1 -0
  14. package/build/http/cache.d.ts +8 -0
  15. package/build/http/cache.js +34 -0
  16. package/build/http/cache.js.map +1 -0
  17. package/build/http/const.d.ts +8 -0
  18. package/build/http/const.js +3054 -0
  19. package/build/http/const.js.map +1 -0
  20. package/build/http/device.d.ts +490 -0
  21. package/build/http/device.js +5256 -0
  22. package/build/http/device.js.map +1 -0
  23. package/build/http/error.d.ts +73 -0
  24. package/build/http/error.js +101 -0
  25. package/build/http/error.js.map +1 -0
  26. package/build/http/index.d.ts +10 -0
  27. package/build/http/index.js +30 -0
  28. package/build/http/index.js.map +1 -0
  29. package/build/http/interfaces.d.ts +248 -0
  30. package/build/http/interfaces.js +3 -0
  31. package/build/http/interfaces.js.map +1 -0
  32. package/build/http/models.d.ts +608 -0
  33. package/build/http/models.js +3 -0
  34. package/build/http/models.js.map +1 -0
  35. package/build/http/parameter.d.ts +7 -0
  36. package/build/http/parameter.js +119 -0
  37. package/build/http/parameter.js.map +1 -0
  38. package/build/http/station.d.ts +382 -0
  39. package/build/http/station.js +15735 -0
  40. package/build/http/station.js.map +1 -0
  41. package/build/http/types.d.ts +1358 -0
  42. package/build/http/types.js +10333 -0
  43. package/build/http/types.js.map +1 -0
  44. package/build/http/utils.d.ts +89 -0
  45. package/build/http/utils.js +916 -0
  46. package/build/http/utils.js.map +1 -0
  47. package/build/index.d.ts +8 -0
  48. package/build/index.js +29 -0
  49. package/build/index.js.map +1 -0
  50. package/build/interfaces.d.ts +147 -0
  51. package/build/interfaces.js +7 -0
  52. package/build/interfaces.js.map +1 -0
  53. package/build/logging.d.ts +36 -0
  54. package/build/logging.js +119 -0
  55. package/build/logging.js.map +1 -0
  56. package/build/mqtt/interface.d.ts +6 -0
  57. package/build/mqtt/interface.js +3 -0
  58. package/build/mqtt/interface.js.map +1 -0
  59. package/build/mqtt/model.d.ts +24 -0
  60. package/build/mqtt/model.js +3 -0
  61. package/build/mqtt/model.js.map +1 -0
  62. package/build/mqtt/mqtt-eufy.crt +79 -0
  63. package/build/mqtt/proto/lock.proto +33 -0
  64. package/build/mqtt/service.d.ts +28 -0
  65. package/build/mqtt/service.js +196 -0
  66. package/build/mqtt/service.js.map +1 -0
  67. package/build/p2p/ble.d.ts +59 -0
  68. package/build/p2p/ble.js +281 -0
  69. package/build/p2p/ble.js.map +1 -0
  70. package/build/p2p/error.d.ts +49 -0
  71. package/build/p2p/error.js +69 -0
  72. package/build/p2p/error.js.map +1 -0
  73. package/build/p2p/index.d.ts +8 -0
  74. package/build/p2p/index.js +28 -0
  75. package/build/p2p/index.js.map +1 -0
  76. package/build/p2p/interfaces.d.ts +423 -0
  77. package/build/p2p/interfaces.js +3 -0
  78. package/build/p2p/interfaces.js.map +1 -0
  79. package/build/p2p/models.d.ts +295 -0
  80. package/build/p2p/models.js +3 -0
  81. package/build/p2p/models.js.map +1 -0
  82. package/build/p2p/session.d.ts +186 -0
  83. package/build/p2p/session.js +3737 -0
  84. package/build/p2p/session.js.map +1 -0
  85. package/build/p2p/talkback.d.ts +8 -0
  86. package/build/p2p/talkback.js +23 -0
  87. package/build/p2p/talkback.js.map +1 -0
  88. package/build/p2p/types.d.ts +1164 -0
  89. package/build/p2p/types.js +1219 -0
  90. package/build/p2p/types.js.map +1 -0
  91. package/build/p2p/utils.d.ts +72 -0
  92. package/build/p2p/utils.js +865 -0
  93. package/build/p2p/utils.js.map +1 -0
  94. package/build/push/client.d.ts +49 -0
  95. package/build/push/client.js +344 -0
  96. package/build/push/client.js.map +1 -0
  97. package/build/push/error.d.ts +73 -0
  98. package/build/push/error.js +101 -0
  99. package/build/push/error.js.map +1 -0
  100. package/build/push/index.d.ts +6 -0
  101. package/build/push/index.js +26 -0
  102. package/build/push/index.js.map +1 -0
  103. package/build/push/interfaces.d.ts +19 -0
  104. package/build/push/interfaces.js +3 -0
  105. package/build/push/interfaces.js.map +1 -0
  106. package/build/push/models.d.ts +328 -0
  107. package/build/push/models.js +38 -0
  108. package/build/push/models.js.map +1 -0
  109. package/build/push/parser.d.ts +25 -0
  110. package/build/push/parser.js +231 -0
  111. package/build/push/parser.js.map +1 -0
  112. package/build/push/proto/checkin.proto +266 -0
  113. package/build/push/proto/mcs.proto +328 -0
  114. package/build/push/service.d.ts +46 -0
  115. package/build/push/service.js +965 -0
  116. package/build/push/service.js.map +1 -0
  117. package/build/push/types.d.ts +220 -0
  118. package/build/push/types.js +244 -0
  119. package/build/push/types.js.map +1 -0
  120. package/build/push/utils.d.ts +7 -0
  121. package/build/push/utils.js +116 -0
  122. package/build/push/utils.js.map +1 -0
  123. package/build/utils.d.ts +115 -0
  124. package/build/utils.js +438 -0
  125. package/build/utils.js.map +1 -0
  126. package/eslint.config.mts +68 -0
  127. package/jest.config.js +14 -0
  128. package/package.json +85 -0
  129. package/scripts/cut_release.sh +31 -0
@@ -0,0 +1,3737 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.P2PClientProtocol = void 0;
4
+ const dgram_1 = require("dgram");
5
+ const tiny_typed_emitter_1 = require("tiny-typed-emitter");
6
+ const stream_1 = require("stream");
7
+ const sweet_collections_1 = require("sweet-collections");
8
+ const { parse } = require("date-and-time");
9
+ const utils_1 = require("./utils");
10
+ const types_1 = require("./types");
11
+ const types_2 = require("../http/types");
12
+ const device_1 = require("../http/device");
13
+ const utils_2 = require("../http/utils");
14
+ const talkback_1 = require("./talkback");
15
+ const error_1 = require("../error");
16
+ const types_3 = require("../push/types");
17
+ const ble_1 = require("./ble");
18
+ const http_1 = require("../http");
19
+ const utils_3 = require("../utils");
20
+ const logging_1 = require("../logging");
21
+ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
22
+ MAX_RETRIES = 10;
23
+ MAX_COMMAND_RESULT_WAIT = 30 * 1000;
24
+ MAX_GATEWAY_COMMAND_RESULT_WAIT = 5 * 1000;
25
+ MAX_CONNECTION_TIMEOUT = 25 * 1000;
26
+ MAX_AKNOWLEDGE_TIMEOUT = 5 * 1000;
27
+ MAX_LOOKUP_TIMEOUT = 20 * 1000;
28
+ LOCAL_LOOKUP_RETRY_TIMEOUT = 1 * 1000;
29
+ LOOKUP_RETRY_TIMEOUT = 1 * 1000;
30
+ LOOKUP2_TIMEOUT = 3 * 1000;
31
+ LOOKUP2_RETRY_TIMEOUT = 1 * 1000;
32
+ MAX_EXPECTED_SEQNO_WAIT = 20 * 1000;
33
+ HEARTBEAT_INTERVAL = 5 * 1000;
34
+ MAX_COMMAND_QUEUE_TIMEOUT = 120 * 1000;
35
+ AUDIO_CODEC_ANALYZE_TIMEOUT = 650;
36
+ KEEPALIVE_INTERVAL = 2 * 1000;
37
+ ESD_DISCONNECT_TIMEOUT = 30 * 1000;
38
+ MAX_STREAM_DATA_WAIT = 5 * 1000;
39
+ RESEND_NOT_ACKNOWLEDGED_COMMAND = 100;
40
+ UDP_RECVBUFFERSIZE_BYTES = 1048576;
41
+ MAX_PAYLOAD_BYTES = 1028;
42
+ MAX_PACKET_BYTES = 1024;
43
+ MAX_VIDEO_PACKET_BYTES = 655360;
44
+ P2P_DATA_HEADER_BYTES = 16;
45
+ MAX_SEQUENCE_NUMBER = 65535;
46
+ LOOP_RUNAWAY_LIMIT = 1000;
47
+ /*
48
+ * SEQUENCE_PROCESSING_BOUNDARY is used to determine if an incoming sequence number
49
+ * that is lower than the expected one was already processed.
50
+ * If it is within the boundary, it is determined as 'already processed',
51
+ * If it is even lower, it is assumed that the sequence count has reached
52
+ * MAX_SEQUENCE_NUMBER and restarted at 0.
53
+ * */
54
+ SEQUENCE_PROCESSING_BOUNDARY = 20000; // worth of approx. 90 seconds of continous streaming
55
+ socket;
56
+ binded = false;
57
+ connected = false;
58
+ connecting = false;
59
+ terminating = false;
60
+ p2pTurnHandshaking = {};
61
+ p2pTurnConfirmed = false;
62
+ seqNumber = 0;
63
+ offsetDataSeqNumber = 0;
64
+ videoSeqNumber = 0;
65
+ lockSeqNumber = -1;
66
+ expectedSeqNo = {};
67
+ currentMessageBuilder = {};
68
+ currentMessageState = {};
69
+ talkbackStream;
70
+ downloadTotalBytes = 0;
71
+ downloadReceivedBytes = 0;
72
+ cloudAddresses;
73
+ messageStates = new sweet_collections_1.SortedMap((a, b) => a - b);
74
+ messageVideoStates = new sweet_collections_1.SortedMap((a, b) => a - b);
75
+ sendQueue = new Array();
76
+ connectTimeout;
77
+ lookupTimeout;
78
+ localLookupRetryTimeout;
79
+ lookupRetryTimeout;
80
+ lookup2Timeout;
81
+ lookup2RetryTimeout;
82
+ heartbeatTimeout;
83
+ keepaliveTimeout;
84
+ esdDisconnectTimeout;
85
+ secondaryCommandTimeout;
86
+ connectTime = null;
87
+ lastPong = null;
88
+ lastPongData = undefined;
89
+ connectionType = types_1.P2PConnectionType.QUICKEST;
90
+ energySavingDevice = false;
91
+ p2pSeqMapping = new Map();
92
+ p2pDataSeqNumber = 0;
93
+ connectAddress = undefined;
94
+ localIPAddress = undefined;
95
+ preferredIPAddress = undefined;
96
+ listeningPort = 0;
97
+ dskKey = "";
98
+ dskExpiration = null;
99
+ deviceSNs = {};
100
+ api;
101
+ rawStation;
102
+ customDataStaging = {};
103
+ lockPublicKey;
104
+ lockAESKeys = new Map();
105
+ channel = http_1.Station.CHANNEL;
106
+ encryption = types_1.EncryptionType.NONE;
107
+ p2pKey;
108
+ enableEmbeddedPKCS1Support = false;
109
+ constructor(rawStation, api, ipAddress, listeningPort = 0, publicKey = "", enableEmbeddedPKCS1Support = false) {
110
+ super();
111
+ this.api = api;
112
+ this.lockPublicKey = publicKey;
113
+ this.preferredIPAddress = ipAddress;
114
+ if (listeningPort >= 0)
115
+ this.listeningPort = listeningPort;
116
+ this.enableEmbeddedPKCS1Support = enableEmbeddedPKCS1Support;
117
+ this.cloudAddresses = (0, utils_1.decodeP2PCloudIPs)(rawStation.app_conn);
118
+ logging_1.rootP2PLogger.debug("Loaded P2P cloud ip addresses", {
119
+ stationSN: rawStation.station_sn,
120
+ ipAddress: ipAddress,
121
+ cloudAddresses: this.cloudAddresses,
122
+ });
123
+ this.updateRawStation(rawStation);
124
+ this.socket = (0, dgram_1.createSocket)("udp4");
125
+ this.socket.on("message", (msg, rinfo) => this.handleMsg(msg, rinfo));
126
+ this.socket.on("error", (error) => this.onError(error));
127
+ this.socket.on("close", () => this.onClose());
128
+ this._initialize();
129
+ }
130
+ _incrementSequence(sequence) {
131
+ if (sequence < this.MAX_SEQUENCE_NUMBER)
132
+ return sequence + 1;
133
+ return 0;
134
+ }
135
+ _isBetween(n, lowBoundary, highBoundary) {
136
+ if (n < lowBoundary)
137
+ return false;
138
+ if (n >= highBoundary)
139
+ return false;
140
+ return true;
141
+ }
142
+ _wasSequenceNumberAlreadyProcessed(expectedSequence, receivedSequence) {
143
+ if (expectedSequence - this.SEQUENCE_PROCESSING_BOUNDARY > 0) {
144
+ // complete boundary without squence number reset
145
+ return this._isBetween(receivedSequence, expectedSequence - this.SEQUENCE_PROCESSING_BOUNDARY, expectedSequence);
146
+ }
147
+ else {
148
+ // there was a sequence number reset recently
149
+ const isInRangeAfterReset = this._isBetween(receivedSequence, 0, expectedSequence);
150
+ const isInRangeBeforeReset = this._isBetween(receivedSequence, this.MAX_SEQUENCE_NUMBER + (expectedSequence - this.SEQUENCE_PROCESSING_BOUNDARY), this.MAX_SEQUENCE_NUMBER);
151
+ return isInRangeBeforeReset || isInRangeAfterReset;
152
+ }
153
+ }
154
+ _initialize() {
155
+ let rsaKey;
156
+ this.connected = false;
157
+ this.p2pTurnHandshaking = {};
158
+ this.p2pTurnConfirmed = false;
159
+ this.connecting = false;
160
+ this.lastPong = null;
161
+ this.lastPongData = undefined;
162
+ this.connectTime = null;
163
+ this.seqNumber = 0;
164
+ this.offsetDataSeqNumber = 0;
165
+ this.videoSeqNumber = 0;
166
+ this.p2pDataSeqNumber = 0;
167
+ this.connectAddress = undefined;
168
+ this.customDataStaging = {};
169
+ this.encryption = types_1.EncryptionType.NONE;
170
+ this.p2pKey = undefined;
171
+ this.lockAESKeys.clear();
172
+ this._clearMessageStateTimeouts();
173
+ this._clearMessageVideoStateTimeouts();
174
+ this.messageStates.clear();
175
+ this.messageVideoStates.clear();
176
+ this.p2pSeqMapping.clear();
177
+ for (let datatype = 0; datatype < 4; datatype++) {
178
+ this.expectedSeqNo[datatype] = 0;
179
+ if (datatype === types_1.P2PDataType.VIDEO)
180
+ rsaKey = (0, utils_1.getNewRSAPrivateKey)(this.enableEmbeddedPKCS1Support);
181
+ else
182
+ rsaKey = null;
183
+ this.initializeMessageBuilder(datatype);
184
+ this.initializeMessageState(datatype, rsaKey);
185
+ this.initializeStream(datatype);
186
+ }
187
+ }
188
+ initializeMessageBuilder(datatype) {
189
+ this.currentMessageBuilder[datatype] = {
190
+ header: {
191
+ commandId: 0,
192
+ bytesToRead: 0,
193
+ channel: 0,
194
+ signCode: 0,
195
+ type: 0,
196
+ },
197
+ bytesRead: 0,
198
+ messages: {},
199
+ };
200
+ }
201
+ initializeMessageState(datatype, rsaKey = null) {
202
+ this.currentMessageState[datatype] = {
203
+ leftoverData: Buffer.from([]),
204
+ queuedData: new sweet_collections_1.SortedMap((a, b) => a - b),
205
+ rsaKey: rsaKey,
206
+ videoStream: null,
207
+ audioStream: null,
208
+ invalidStream: false,
209
+ p2pStreaming: false,
210
+ p2pStreamNotStarted: true,
211
+ p2pStreamChannel: -1,
212
+ p2pStreamFirstAudioDataReceived: false,
213
+ p2pStreamFirstVideoDataReceived: false,
214
+ p2pStreamMetadata: {
215
+ videoCodec: types_1.VideoCodec.H264,
216
+ videoFPS: 15,
217
+ videoHeight: 1080,
218
+ videoWidth: 1920,
219
+ audioCodec: types_1.AudioCodec.NONE,
220
+ },
221
+ rtspStream: {},
222
+ rtspStreaming: {},
223
+ receivedFirstIFrame: false,
224
+ preFrameVideoData: Buffer.from([]),
225
+ p2pTalkback: false,
226
+ p2pTalkbackChannel: -1,
227
+ };
228
+ }
229
+ _clearTimeout(timeout) {
230
+ if (!!timeout) {
231
+ clearTimeout(timeout);
232
+ }
233
+ }
234
+ _clearMessageStateTimeouts() {
235
+ for (const message of this.messageStates.values()) {
236
+ this._clearTimeout(message.timeout);
237
+ }
238
+ }
239
+ _clearMessageVideoStateTimeouts() {
240
+ for (const message of this.messageVideoStates.values()) {
241
+ this._clearTimeout(message.timeout);
242
+ }
243
+ }
244
+ _clearHeartbeatTimeout() {
245
+ this._clearTimeout(this.heartbeatTimeout);
246
+ this.heartbeatTimeout = undefined;
247
+ }
248
+ _clearKeepaliveTimeout() {
249
+ this._clearTimeout(this.keepaliveTimeout);
250
+ this.keepaliveTimeout = undefined;
251
+ }
252
+ _clearConnectTimeout() {
253
+ this._clearTimeout(this.connectTimeout);
254
+ this.connectTimeout = undefined;
255
+ }
256
+ _clearLookupTimeout() {
257
+ this._clearTimeout(this.lookupTimeout);
258
+ this.lookupTimeout = undefined;
259
+ }
260
+ _clearLocalLookupRetryTimeout() {
261
+ this._clearTimeout(this.localLookupRetryTimeout);
262
+ this.localLookupRetryTimeout = undefined;
263
+ }
264
+ _clearLookupRetryTimeout() {
265
+ this._clearTimeout(this.lookupRetryTimeout);
266
+ this.lookupRetryTimeout = undefined;
267
+ }
268
+ _clearLookup2RetryTimeout() {
269
+ this._clearTimeout(this.lookup2RetryTimeout);
270
+ this.lookup2RetryTimeout = undefined;
271
+ }
272
+ _clearLookup2Timeout() {
273
+ this._clearTimeout(this.lookup2Timeout);
274
+ this.lookup2Timeout = undefined;
275
+ }
276
+ _clearESDDisconnectTimeout() {
277
+ this._clearTimeout(this.esdDisconnectTimeout);
278
+ this.esdDisconnectTimeout = undefined;
279
+ }
280
+ _clearSecondaryCommandTimeout() {
281
+ this._clearTimeout(this.secondaryCommandTimeout);
282
+ this.secondaryCommandTimeout = undefined;
283
+ }
284
+ async sendMessage(errorSubject, address, msgID, payload) {
285
+ await (0, utils_1.sendMessage)(this.socket, address, msgID, payload).catch((err) => {
286
+ const error = (0, error_1.ensureError)(err);
287
+ logging_1.rootP2PLogger.error(`${errorSubject} - Error`, {
288
+ error: (0, utils_3.getError)(error),
289
+ stationSN: this.rawStation.station_sn,
290
+ address: address,
291
+ msgID: msgID.toString("hex"),
292
+ payload: payload?.toString("hex"),
293
+ });
294
+ });
295
+ }
296
+ _disconnected() {
297
+ this._clearHeartbeatTimeout();
298
+ this._clearKeepaliveTimeout();
299
+ this._clearLocalLookupRetryTimeout();
300
+ this._clearLookupRetryTimeout();
301
+ this._clearLookup2Timeout();
302
+ this._clearLookupTimeout();
303
+ this._clearConnectTimeout();
304
+ this._clearESDDisconnectTimeout();
305
+ this._clearSecondaryCommandTimeout();
306
+ this._clearMessageStateTimeouts();
307
+ this._clearMessageVideoStateTimeouts();
308
+ if (this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreaming) {
309
+ this.endStream(types_1.P2PDataType.VIDEO);
310
+ }
311
+ if (this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreaming) {
312
+ this.endStream(types_1.P2PDataType.BINARY);
313
+ }
314
+ for (const channel in this.currentMessageState[types_1.P2PDataType.DATA].rtspStreaming) {
315
+ this.endRTSPStream(Number.parseInt(channel));
316
+ }
317
+ this.sendQueue = this.sendQueue.filter((queue) => queue.p2pCommand.commandType !== types_1.CommandType.CMD_PING &&
318
+ queue.p2pCommand.commandType !== types_1.CommandType.CMD_GET_DEVICE_PING);
319
+ if (this.connected) {
320
+ this.emit("close");
321
+ }
322
+ else if (!this.terminating) {
323
+ this.emit("timeout");
324
+ }
325
+ this._initialize();
326
+ }
327
+ closeEnergySavingDevice() {
328
+ if (this.sendQueue.filter((queue) => queue.p2pCommand.commandType !== types_1.CommandType.CMD_PING &&
329
+ queue.p2pCommand.commandType !== types_1.CommandType.CMD_GET_DEVICE_PING).length === 0 &&
330
+ this.energySavingDevice &&
331
+ !this.isCurrentlyStreaming() &&
332
+ Array.from(this.messageStates.values()).filter((msgState) => msgState.acknowledged === false).length === 0) {
333
+ if (this.esdDisconnectTimeout === undefined) {
334
+ logging_1.rootP2PLogger.debug(`Energy saving device - No more p2p commands to execute or running streams, initiate disconnect timeout in ${this.ESD_DISCONNECT_TIMEOUT} milliseconds...`, { stationSN: this.rawStation.station_sn });
335
+ this.esdDisconnectTimeout = setTimeout(() => {
336
+ this.esdDisconnectTimeout = undefined;
337
+ this.sendMessage(`Closing of connection for battery saving`, this.connectAddress, types_1.RequestMessageType.END);
338
+ logging_1.rootP2PLogger.info(`Initiated closing of connection to station ${this.rawStation.station_sn} for saving battery.`);
339
+ this.terminating = true;
340
+ this._disconnected();
341
+ }, this.ESD_DISCONNECT_TIMEOUT);
342
+ }
343
+ }
344
+ }
345
+ async renewDSKKey() {
346
+ if (this.dskKey === "" || (this.dskExpiration && new Date().getTime() >= this.dskExpiration.getTime())) {
347
+ logging_1.rootP2PLogger.debug(`DSK keys not present or expired, get/renew it`, {
348
+ stationSN: this.rawStation.station_sn,
349
+ dskKey: this.dskKey,
350
+ dskExpiration: this.dskExpiration,
351
+ });
352
+ await this.getDSKKeys();
353
+ }
354
+ }
355
+ localLookup(host) {
356
+ logging_1.rootP2PLogger.debug(`Trying to local lookup address for station ${this.rawStation.station_sn} with host ${host}`);
357
+ this.localLookupByAddress({ host: host, port: 32108 });
358
+ this._clearLocalLookupRetryTimeout();
359
+ this.lookupRetryTimeout = setTimeout(() => {
360
+ this.localLookup(host);
361
+ }, this.LOCAL_LOOKUP_RETRY_TIMEOUT);
362
+ }
363
+ cloudLookup() {
364
+ this.cloudAddresses.map((address) => this.cloudLookupByAddress(address));
365
+ this._clearLookupRetryTimeout();
366
+ this.lookupRetryTimeout = setTimeout(() => {
367
+ this.cloudLookup();
368
+ }, this.LOOKUP_RETRY_TIMEOUT);
369
+ }
370
+ cloudLookup2() {
371
+ this.cloudAddresses.map((address) => this.cloudLookupByAddress2(address));
372
+ this._clearLookup2RetryTimeout();
373
+ this.lookup2RetryTimeout = setTimeout(() => {
374
+ this.cloudLookup2();
375
+ }, this.LOOKUP2_RETRY_TIMEOUT);
376
+ }
377
+ cloudLookupWithTurnServer(origAddress, data) {
378
+ this.cloudAddresses.map((address) => this.cloudLookupByAddressWithTurnServer(address, origAddress, data));
379
+ }
380
+ async localLookupByAddress(address) {
381
+ // Send lookup message
382
+ const msgId = types_1.RequestMessageType.LOCAL_LOOKUP;
383
+ const payload = Buffer.from([0, 0]);
384
+ await this.sendMessage(`Local lookup address`, address, msgId, payload);
385
+ }
386
+ async cloudLookupByAddress(address) {
387
+ // Send lookup message
388
+ const msgId = types_1.RequestMessageType.LOOKUP_WITH_KEY;
389
+ const payload = (0, utils_1.buildLookupWithKeyPayload)(this.socket, this.rawStation.p2p_did, this.dskKey);
390
+ await this.sendMessage(`Cloud lookup addresses`, address, msgId, payload);
391
+ }
392
+ async cloudLookupByAddress2(address) {
393
+ // Send lookup message2
394
+ const msgId = types_1.RequestMessageType.LOOKUP_WITH_KEY2;
395
+ const payload = (0, utils_1.buildLookupWithKeyPayload2)(this.rawStation.p2p_did, this.dskKey);
396
+ await this.sendMessage(`Cloud lookup addresses (2)`, address, msgId, payload);
397
+ }
398
+ async cloudLookupByAddressWithTurnServer(address, origAddress, data) {
399
+ // Send lookup message3
400
+ const msgId = types_1.RequestMessageType.TURN_LOOKUP_WITH_KEY;
401
+ const payload = (0, utils_1.buildLookupWithKeyPayload3)(this.rawStation.p2p_did, origAddress, data);
402
+ await this.sendMessage(`Cloud lookup addresses with turn server`, address, msgId, payload);
403
+ }
404
+ isConnected() {
405
+ return this.connected;
406
+ }
407
+ _startConnectTimeout() {
408
+ if (this.connectTimeout === undefined)
409
+ this.connectTimeout = setTimeout(() => {
410
+ logging_1.rootP2PLogger.warn(`Tried all hosts, no connection could be established to station ${this.rawStation.station_sn}.`);
411
+ this._disconnected();
412
+ }, this.MAX_CONNECTION_TIMEOUT);
413
+ }
414
+ _connect(address, p2p_did) {
415
+ logging_1.rootP2PLogger.debug(`Connecting to host ${address.host} on port ${address.port} (CHECK_CAM)`, {
416
+ stationSN: this.rawStation.station_sn,
417
+ address: address,
418
+ p2pDid: p2p_did,
419
+ });
420
+ this.sendCamCheck(address, p2p_did);
421
+ for (let i = address.port - 3; i < address.port; i++)
422
+ this.sendCamCheck({ host: address.host, port: i }, p2p_did);
423
+ for (let i = address.port + 1; i <= address.port + 3; i++)
424
+ this.sendCamCheck({ host: address.host, port: i }, p2p_did);
425
+ this._startConnectTimeout();
426
+ }
427
+ lookup(host) {
428
+ if (host === undefined) {
429
+ if (this.preferredIPAddress !== undefined) {
430
+ host = this.preferredIPAddress;
431
+ }
432
+ else if (this.localIPAddress !== undefined) {
433
+ host = this.localIPAddress;
434
+ }
435
+ else {
436
+ const localIP = (0, utils_1.getLocalIpAddress)();
437
+ host = localIP.substring(0, localIP.lastIndexOf(".") + 1).concat("255");
438
+ }
439
+ }
440
+ this.localLookup(host);
441
+ this.cloudLookup();
442
+ this._clearLookup2Timeout();
443
+ this.lookup2Timeout = setTimeout(() => {
444
+ this.cloudLookup2();
445
+ }, this.LOOKUP2_TIMEOUT);
446
+ this._clearLookupTimeout();
447
+ this.lookupTimeout = setTimeout(() => {
448
+ this.lookupTimeout = undefined;
449
+ logging_1.rootP2PLogger.error(`All address lookup tentatives failed.`, { stationSN: this.rawStation.station_sn });
450
+ if (this.localIPAddress !== undefined)
451
+ this.localIPAddress = undefined;
452
+ this._disconnected();
453
+ }, this.MAX_LOOKUP_TIMEOUT);
454
+ }
455
+ async connect(host) {
456
+ if (!this.connected && !this.connecting && this.rawStation.p2p_did !== undefined) {
457
+ this.connecting = true;
458
+ this.terminating = false;
459
+ await this.renewDSKKey();
460
+ if (!this.binded)
461
+ this.socket.bind(this.listeningPort, () => {
462
+ this.binded = true;
463
+ try {
464
+ this.socket.setRecvBufferSize(this.UDP_RECVBUFFERSIZE_BYTES);
465
+ this.socket.setBroadcast(true);
466
+ }
467
+ catch (err) {
468
+ const error = (0, error_1.ensureError)(err);
469
+ logging_1.rootP2PLogger.error(`connect - Error`, {
470
+ error: (0, utils_3.getError)(error),
471
+ stationSN: this.rawStation.station_sn,
472
+ host: host,
473
+ currentRecBufferSize: this.socket.getRecvBufferSize(),
474
+ recBufferRequestedSize: this.UDP_RECVBUFFERSIZE_BYTES,
475
+ });
476
+ }
477
+ this.lookup(host);
478
+ });
479
+ else {
480
+ this.lookup(host);
481
+ }
482
+ }
483
+ }
484
+ async sendCamCheck(address, p2p_did) {
485
+ const payload = (0, utils_1.buildCheckCamPayload)(p2p_did);
486
+ await this.sendMessage(`Send cam check`, address, types_1.RequestMessageType.CHECK_CAM, payload);
487
+ }
488
+ async sendCamCheck2(address, data) {
489
+ const payload = (0, utils_1.buildCheckCamPayload2)(this.rawStation.p2p_did, data);
490
+ await this.sendMessage(`Send cam check (2)`, address, types_1.RequestMessageType.CHECK_CAM2, payload);
491
+ }
492
+ async sendPing(address) {
493
+ if ((this.lastPong && (new Date().getTime() - this.lastPong) / this.getHeartbeatInterval() >= this.MAX_RETRIES) ||
494
+ (this.connectTime &&
495
+ !this.lastPong &&
496
+ (new Date().getTime() - this.connectTime) / this.getHeartbeatInterval() >= this.MAX_RETRIES)) {
497
+ if (!this.energySavingDevice)
498
+ logging_1.rootP2PLogger.warn(`Heartbeat check failed for station ${this.rawStation.station_sn}. Connection seems lost. Try to reconnect...`);
499
+ this._disconnected();
500
+ }
501
+ await this.sendMessage(`Send ping`, address, types_1.RequestMessageType.PING, this.lastPongData);
502
+ }
503
+ sendCommandWithIntString(p2pcommand, customData) {
504
+ if (p2pcommand.channel === undefined)
505
+ p2pcommand.channel = 0;
506
+ if (p2pcommand.value === undefined || typeof p2pcommand.value !== "number")
507
+ throw new TypeError("value must be a number");
508
+ this.sendCommand(p2pcommand, types_1.InternalP2PCommandType.WithIntString, undefined, customData);
509
+ }
510
+ sendCommandWithInt(p2pcommand, customData) {
511
+ if (p2pcommand.channel === undefined)
512
+ p2pcommand.channel = this.channel;
513
+ if (p2pcommand.value === undefined || typeof p2pcommand.value !== "number")
514
+ throw new TypeError("value must be a number");
515
+ this.sendCommand(p2pcommand, types_1.InternalP2PCommandType.WithInt, undefined, customData);
516
+ }
517
+ sendCommandWithStringPayload(p2pcommand, customData) {
518
+ if (p2pcommand.channel === undefined)
519
+ p2pcommand.channel = 0;
520
+ if (p2pcommand.value === undefined || typeof p2pcommand.value !== "string")
521
+ throw new TypeError("value must be a string");
522
+ let nested_commandType = undefined;
523
+ let nested_commandType2 = undefined;
524
+ logging_1.rootP2PLogger.debug(`sendCommandWithStringPayload:`, { p2pcommand: p2pcommand, customData: customData });
525
+ if (p2pcommand.commandType == types_1.CommandType.CMD_SET_PAYLOAD) {
526
+ try {
527
+ const json = JSON.parse(p2pcommand.value);
528
+ nested_commandType = json.cmd;
529
+ if (json.payload && json.payload.apiCommand !== undefined) {
530
+ nested_commandType2 = json.payload.apiCommand;
531
+ }
532
+ }
533
+ catch (err) {
534
+ const error = (0, error_1.ensureError)(err);
535
+ logging_1.rootP2PLogger.error(`sendCommandWithStringPayload CMD_SET_PAYLOAD - Error`, {
536
+ error: (0, utils_3.getError)(error),
537
+ stationSN: this.rawStation.station_sn,
538
+ p2pcommand: p2pcommand,
539
+ customData: customData,
540
+ });
541
+ }
542
+ }
543
+ else if (p2pcommand.commandType == types_1.CommandType.CMD_DOORBELL_SET_PAYLOAD) {
544
+ try {
545
+ const json = JSON.parse(p2pcommand.value);
546
+ nested_commandType = json.commandType;
547
+ }
548
+ catch (err) {
549
+ const error = (0, error_1.ensureError)(err);
550
+ logging_1.rootP2PLogger.error(`sendCommandWithStringPayload CMD_DOORBELL_SET_PAYLOAD - Error`, {
551
+ error: (0, utils_3.getError)(error),
552
+ stationSN: this.rawStation.station_sn,
553
+ p2pcommand: p2pcommand,
554
+ customData: customData,
555
+ });
556
+ }
557
+ }
558
+ this.sendCommand(p2pcommand, types_1.InternalP2PCommandType.WithStringPayload, nested_commandType, customData, nested_commandType2);
559
+ }
560
+ sendCommandWithString(p2pcommand, customData) {
561
+ if (p2pcommand.channel === undefined)
562
+ p2pcommand.channel = this.channel;
563
+ if (p2pcommand.strValue === undefined)
564
+ throw new TypeError("strValue must be defined");
565
+ if (p2pcommand.strValueSub === undefined)
566
+ throw new TypeError("strValueSub must be defined");
567
+ this.sendCommand(p2pcommand, types_1.InternalP2PCommandType.WithString, p2pcommand.commandType, customData);
568
+ }
569
+ sendCommandPing(channel = this.channel) {
570
+ this.sendCommand({
571
+ commandType: types_1.CommandType.CMD_PING,
572
+ channel: channel,
573
+ }, types_1.InternalP2PCommandType.WithoutData);
574
+ }
575
+ sendCommandDevicePing(channel = this.channel) {
576
+ this.sendCommand({
577
+ commandType: types_1.CommandType.CMD_GET_DEVICE_PING,
578
+ channel: channel,
579
+ }, types_1.InternalP2PCommandType.WithoutData);
580
+ }
581
+ sendCommandWithoutData(commandType, channel = this.channel) {
582
+ this.sendCommand({
583
+ commandType: commandType,
584
+ channel: channel,
585
+ }, types_1.InternalP2PCommandType.WithoutData);
586
+ }
587
+ sendQueuedMessage() {
588
+ if (this.sendQueue.length > 0) {
589
+ if (this.connected) {
590
+ let queuedMessage;
591
+ while ((queuedMessage = this.sendQueue.shift()) !== undefined) {
592
+ let exists = false;
593
+ let waitingAcknowledge = false;
594
+ this.messageStates.forEach((stateMessage) => {
595
+ if (stateMessage.commandType === queuedMessage.p2pCommand.commandType &&
596
+ stateMessage.nestedCommandType === queuedMessage.nestedCommandType &&
597
+ !stateMessage.acknowledged) {
598
+ exists = true;
599
+ }
600
+ if (!stateMessage.acknowledged || stateMessage.commandType === types_1.CommandType.CMD_GATEWAYINFO) {
601
+ waitingAcknowledge = true;
602
+ }
603
+ });
604
+ if (!exists && !waitingAcknowledge) {
605
+ this._sendCommand(queuedMessage);
606
+ break;
607
+ }
608
+ else {
609
+ this.sendQueue.unshift(queuedMessage);
610
+ break;
611
+ }
612
+ }
613
+ }
614
+ else if (!this.connected &&
615
+ this.sendQueue.filter((queue) => queue.p2pCommand.commandType !== types_1.CommandType.CMD_PING &&
616
+ queue.p2pCommand.commandType !== types_1.CommandType.CMD_GET_DEVICE_PING).length > 0) {
617
+ logging_1.rootP2PLogger.debug(`Initiate station p2p connection to send queued data`, {
618
+ stationSN: this.rawStation.station_sn,
619
+ queuedDataCount: this.sendQueue.filter((queue) => queue.p2pCommand.commandType !== types_1.CommandType.CMD_PING &&
620
+ queue.p2pCommand.commandType !== types_1.CommandType.CMD_GET_DEVICE_PING).length,
621
+ });
622
+ this.connect();
623
+ }
624
+ }
625
+ this.closeEnergySavingDevice();
626
+ }
627
+ sendCommand(p2pcommand, p2pcommandType, nestedCommandType, customData, nested_commandType2) {
628
+ const message = {
629
+ p2pCommand: p2pcommand,
630
+ nestedCommandType: nestedCommandType,
631
+ nestedCommandType2: nested_commandType2,
632
+ timestamp: +new Date(),
633
+ customData: customData,
634
+ p2pCommandType: p2pcommandType,
635
+ };
636
+ this.sendQueue.push(message);
637
+ if (p2pcommand.commandType !== types_1.CommandType.CMD_PING && p2pcommand.commandType !== types_1.CommandType.CMD_GET_DEVICE_PING)
638
+ this._clearESDDisconnectTimeout();
639
+ this.sendQueuedMessage();
640
+ }
641
+ resendNotAcknowledgedCommand(sequence) {
642
+ const messageState = this.messageStates.get(sequence);
643
+ if (messageState) {
644
+ this._clearTimeout(messageState.retryTimeout);
645
+ messageState.retryTimeout = setTimeout(() => {
646
+ if (this.connectAddress) {
647
+ (0, utils_1.sendMessage)(this.socket, this.connectAddress, types_1.RequestMessageType.DATA, messageState.data).catch((err) => {
648
+ const error = (0, error_1.ensureError)(err);
649
+ logging_1.rootP2PLogger.error(`resendNotAcknowledgedCommand - Error`, {
650
+ error: (0, utils_3.getError)(error),
651
+ stationSN: this.rawStation.station_sn,
652
+ sequence: sequence,
653
+ });
654
+ });
655
+ this.resendNotAcknowledgedCommand(sequence);
656
+ }
657
+ }, this.RESEND_NOT_ACKNOWLEDGED_COMMAND);
658
+ }
659
+ }
660
+ async _sendCommand(message) {
661
+ if ((0, utils_1.isP2PQueueMessage)(message)) {
662
+ const ageing = +new Date() - message.timestamp;
663
+ if (ageing <= this.MAX_COMMAND_QUEUE_TIMEOUT) {
664
+ const commandHeader = (0, utils_1.buildCommandHeader)(this.seqNumber, message.p2pCommand.commandType);
665
+ let payload;
666
+ const channel = message.p2pCommand.channel !== undefined ? message.p2pCommand.channel : 0;
667
+ switch (message.p2pCommandType) {
668
+ case types_1.InternalP2PCommandType.WithInt:
669
+ payload = (0, utils_1.buildIntCommandPayload)(this.encryption, this.p2pKey, this.rawStation.station_sn, this.rawStation.p2p_did, message.p2pCommand.commandType, message.p2pCommand.value, message.p2pCommand.strValue === undefined ? "" : message.p2pCommand.strValue, channel);
670
+ break;
671
+ case types_1.InternalP2PCommandType.WithIntString:
672
+ payload = (0, utils_1.buildIntStringCommandPayload)(this.encryption, this.p2pKey, this.rawStation.station_sn, this.rawStation.p2p_did, message.p2pCommand.commandType, message.p2pCommand.value, message.p2pCommand.valueSub === undefined ? 0 : message.p2pCommand.valueSub, message.p2pCommand.strValue === undefined ? "" : message.p2pCommand.strValue, message.p2pCommand.strValueSub === undefined ? "" : message.p2pCommand.strValueSub, channel);
673
+ //TODO: Check if this "if" can be moved elsewhere
674
+ if (message.p2pCommand.commandType === types_1.CommandType.CMD_NAS_TEST) {
675
+ this.currentMessageState[types_1.P2PDataType.DATA].rtspStream[channel] =
676
+ message.p2pCommand.value === 1 ? true : false;
677
+ }
678
+ break;
679
+ case types_1.InternalP2PCommandType.WithString:
680
+ payload = (0, utils_1.buildStringTypeCommandPayload)(this.encryption, this.p2pKey, this.rawStation.station_sn, this.rawStation.p2p_did, message.p2pCommand.commandType, message.p2pCommand.strValue, message.p2pCommand.strValueSub, channel);
681
+ break;
682
+ case types_1.InternalP2PCommandType.WithStringPayload:
683
+ payload = (0, utils_1.buildCommandWithStringTypePayload)(this.encryption, this.p2pKey, this.rawStation.station_sn, this.rawStation.p2p_did, message.p2pCommand.commandType, message.p2pCommand.value, channel);
684
+ break;
685
+ default:
686
+ payload = (0, utils_1.buildVoidCommandPayload)(channel);
687
+ break;
688
+ }
689
+ const data = Buffer.concat([commandHeader, payload]);
690
+ const messageState = {
691
+ sequence: this.seqNumber,
692
+ commandType: message.p2pCommand.commandType,
693
+ nestedCommandType: message.nestedCommandType,
694
+ nestedCommandType2: message.nestedCommandType2,
695
+ channel: channel,
696
+ data: data,
697
+ retries: 0,
698
+ acknowledged: false,
699
+ customData: message.customData,
700
+ };
701
+ message = messageState;
702
+ this.seqNumber = this._incrementSequence(this.seqNumber);
703
+ }
704
+ else if (message.p2pCommand.commandType === types_1.CommandType.CMD_PING ||
705
+ message.p2pCommand.commandType === types_1.CommandType.CMD_GET_DEVICE_PING) {
706
+ return;
707
+ }
708
+ else {
709
+ logging_1.rootP2PLogger.warn(`Command aged out from send queue for station ${this.rawStation.station_sn}`, {
710
+ commandType: message.p2pCommand.commandType,
711
+ nestedCommandType: message.nestedCommandType,
712
+ channel: message.p2pCommand.channel,
713
+ ageing: ageing,
714
+ maxAgeing: this.MAX_COMMAND_QUEUE_TIMEOUT,
715
+ });
716
+ this.emit("command", {
717
+ command_type: message.nestedCommandType !== undefined ? message.nestedCommandType : message.p2pCommand.commandType,
718
+ channel: message.p2pCommand.channel,
719
+ return_code: types_1.ErrorCode.ERROR_CONNECT_TIMEOUT,
720
+ customData: message.customData,
721
+ });
722
+ return;
723
+ }
724
+ }
725
+ else {
726
+ if (message.retries < this.MAX_RETRIES && message.returnCode !== types_1.ErrorCode.ERROR_CONNECT_TIMEOUT) {
727
+ if (message.returnCode === types_1.ErrorCode.ERROR_FAILED_TO_REQUEST) {
728
+ this.messageStates.delete(message.sequence);
729
+ message.sequence = this.seqNumber;
730
+ message.data.writeUInt16BE(message.sequence, 2);
731
+ this.seqNumber = this._incrementSequence(this.seqNumber);
732
+ this.messageStates.set(message.sequence, message);
733
+ }
734
+ message.retries++;
735
+ }
736
+ else {
737
+ logging_1.rootP2PLogger.error(`Max p2p command send retries reached.`, {
738
+ stationSN: this.rawStation.station_sn,
739
+ sequence: message.sequence,
740
+ commandType: message.commandType,
741
+ channel: message.channel,
742
+ retries: message.retries,
743
+ returnCode: message.returnCode,
744
+ });
745
+ this.emit("command", {
746
+ command_type: message.nestedCommandType !== undefined ? message.nestedCommandType : message.commandType,
747
+ channel: message.channel,
748
+ return_code: types_1.ErrorCode.ERROR_COMMAND_TIMEOUT,
749
+ customData: message.customData,
750
+ });
751
+ if (message.commandType === types_1.CommandType.CMD_START_REALTIME_MEDIA ||
752
+ (message.nestedCommandType === types_1.CommandType.CMD_START_REALTIME_MEDIA &&
753
+ message.commandType === types_1.CommandType.CMD_SET_PAYLOAD) ||
754
+ message.commandType === types_1.CommandType.CMD_RECORD_VIEW ||
755
+ (message.nestedCommandType === http_1.ParamType.COMMAND_START_LIVESTREAM &&
756
+ message.commandType === types_1.CommandType.CMD_DOORBELL_SET_PAYLOAD)) {
757
+ if (this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreaming &&
758
+ message.channel === this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreamChannel) {
759
+ this.endStream(types_1.P2PDataType.VIDEO);
760
+ }
761
+ }
762
+ else if (message.commandType === types_1.CommandType.CMD_DOWNLOAD_VIDEO) {
763
+ if (this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreaming &&
764
+ message.channel === this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreamChannel) {
765
+ this.endStream(types_1.P2PDataType.BINARY);
766
+ }
767
+ }
768
+ this.messageStates.delete(message.sequence);
769
+ this.sendQueuedMessage();
770
+ return;
771
+ }
772
+ }
773
+ const messageState = message;
774
+ messageState.timeout = setTimeout(() => {
775
+ this._clearTimeout(messageState.retryTimeout);
776
+ this._sendCommand(messageState);
777
+ this._clearESDDisconnectTimeout();
778
+ this.closeEnergySavingDevice();
779
+ }, this.MAX_AKNOWLEDGE_TIMEOUT);
780
+ this.messageStates.set(messageState.sequence, messageState);
781
+ messageState.retryTimeout = setTimeout(() => {
782
+ this.resendNotAcknowledgedCommand(messageState.sequence);
783
+ }, this.RESEND_NOT_ACKNOWLEDGED_COMMAND);
784
+ if (messageState.commandType !== types_1.CommandType.CMD_PING) {
785
+ this.p2pSeqMapping.set(this.p2pDataSeqNumber, message.sequence);
786
+ logging_1.rootP2PLogger.trace(`Added sequence number mapping`, {
787
+ stationSN: this.rawStation.station_sn,
788
+ commandType: message.commandType,
789
+ seqNumber: message.sequence,
790
+ p2pDataSeqNumber: this.p2pDataSeqNumber,
791
+ p2pSeqMappingCount: this.p2pSeqMapping.size,
792
+ });
793
+ this.p2pDataSeqNumber = this._incrementSequence(this.p2pDataSeqNumber);
794
+ }
795
+ logging_1.rootP2PLogger.debug("Sending p2p command...", {
796
+ station: this.rawStation.station_sn,
797
+ sequence: messageState.sequence,
798
+ commandType: messageState.commandType,
799
+ channel: messageState.channel,
800
+ retries: messageState.retries,
801
+ messageStatesSize: this.messageStates.size,
802
+ });
803
+ await this.sendMessage(`Send p2p command`, this.connectAddress, types_1.RequestMessageType.DATA, messageState.data);
804
+ if (messageState.retries === 0) {
805
+ if (messageState.commandType === types_1.CommandType.CMD_START_REALTIME_MEDIA ||
806
+ (messageState.nestedCommandType === types_1.CommandType.CMD_START_REALTIME_MEDIA &&
807
+ messageState.commandType === types_1.CommandType.CMD_SET_PAYLOAD) ||
808
+ messageState.commandType === types_1.CommandType.CMD_RECORD_VIEW ||
809
+ (messageState.nestedCommandType === http_1.ParamType.COMMAND_START_LIVESTREAM &&
810
+ messageState.commandType === types_1.CommandType.CMD_DOORBELL_SET_PAYLOAD)) {
811
+ if (this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreaming &&
812
+ messageState.channel !== this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreamChannel) {
813
+ this.endStream(types_1.P2PDataType.VIDEO);
814
+ }
815
+ this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreaming = true;
816
+ this.currentMessageState[types_1.P2PDataType.VIDEO].p2pStreamChannel = messageState.channel;
817
+ this.waitForStreamData(types_1.P2PDataType.VIDEO);
818
+ }
819
+ else if (messageState.commandType === types_1.CommandType.CMD_DOWNLOAD_VIDEO ||
820
+ (messageState.nestedCommandType === types_1.CommandType.CMD_DOWNLOAD_VIDEO &&
821
+ messageState.commandType === types_1.CommandType.CMD_SET_PAYLOAD)) {
822
+ if (this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreaming &&
823
+ messageState.channel !== this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreamChannel) {
824
+ this.endStream(types_1.P2PDataType.BINARY);
825
+ }
826
+ this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreaming = true;
827
+ this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreamChannel = message.channel;
828
+ this.waitForStreamData(types_1.P2PDataType.BINARY);
829
+ }
830
+ else if (messageState.commandType === types_1.CommandType.CMD_STOP_REALTIME_MEDIA) {
831
+ //TODO: CommandType.CMD_RECORD_PLAY_CTRL only if stop
832
+ this.endStream(types_1.P2PDataType.VIDEO);
833
+ }
834
+ else if (messageState.commandType === types_1.CommandType.CMD_DOWNLOAD_CANCEL) {
835
+ this.endStream(types_1.P2PDataType.BINARY);
836
+ }
837
+ else if (messageState.commandType === types_1.CommandType.CMD_NAS_TEST) {
838
+ if (this.currentMessageState[types_1.P2PDataType.DATA].rtspStream[messageState.channel]) {
839
+ this.currentMessageState[types_1.P2PDataType.DATA].rtspStreaming[messageState.channel] = true;
840
+ this.emit("rtsp livestream started", messageState.channel);
841
+ }
842
+ else {
843
+ this.endRTSPStream(messageState.channel);
844
+ }
845
+ }
846
+ }
847
+ }
848
+ handleMsg(msg, rinfo) {
849
+ if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.LOCAL_LOOKUP_RESP)) {
850
+ if (!this.connected) {
851
+ this._clearLookupTimeout();
852
+ this._clearLocalLookupRetryTimeout();
853
+ const p2pDid = `${msg
854
+ .subarray(4, 12)
855
+ .toString("utf8")
856
+ .replace(/[\0]+$/g, "")}-${msg.subarray(12, 16).readUInt32BE().toString().padStart(6, "0")}-${msg
857
+ .subarray(16, 24)
858
+ .toString("utf8")
859
+ .replace(/[\0]+$/g, "")}`;
860
+ logging_1.rootP2PLogger.trace(`Received message - LOCAL_LOOKUP_RESP - Got response`, {
861
+ stationSN: this.rawStation.station_sn,
862
+ ip: rinfo.address,
863
+ port: rinfo.port,
864
+ p2pDid: p2pDid,
865
+ });
866
+ if (p2pDid === this.rawStation.p2p_did) {
867
+ logging_1.rootP2PLogger.debug(`Received message - LOCAL_LOOKUP_RESP - Wanted device was found, connect to it`, {
868
+ stationSN: this.rawStation.station_sn,
869
+ ip: rinfo.address,
870
+ port: rinfo.port,
871
+ p2pDid: p2pDid,
872
+ });
873
+ this._connect({ host: rinfo.address, port: rinfo.port }, p2pDid);
874
+ }
875
+ else {
876
+ logging_1.rootP2PLogger.debug(`Received message - LOCAL_LOOKUP_RESP - Unwanted device was found, don't connect to it`, {
877
+ stationSN: this.rawStation.station_sn,
878
+ ip: rinfo.address,
879
+ port: rinfo.port,
880
+ p2pDid: p2pDid,
881
+ });
882
+ }
883
+ }
884
+ }
885
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.LOOKUP_ADDR)) {
886
+ if (!this.connected) {
887
+ const port = msg.subarray(6, 8).readUInt16LE();
888
+ const ip = `${msg[11]}.${msg[10]}.${msg[9]}.${msg[8]}`;
889
+ logging_1.rootP2PLogger.trace(`Received message - LOOKUP_ADDR - Got response`, {
890
+ stationSN: this.rawStation.station_sn,
891
+ remoteAddress: rinfo.address,
892
+ remotePort: rinfo.port,
893
+ response: { ip: ip, port: port },
894
+ });
895
+ if (ip === "0.0.0.0") {
896
+ logging_1.rootP2PLogger.trace(`Received message - LOOKUP_ADDR - Got invalid ip address 0.0.0.0, ignoring response...`, {
897
+ stationSN: this.rawStation.station_sn,
898
+ remoteAddress: rinfo.address,
899
+ remotePort: rinfo.port,
900
+ response: { ip: ip, port: port },
901
+ });
902
+ return;
903
+ }
904
+ if ((0, utils_1.isPrivateIp)(ip))
905
+ this.localIPAddress = ip;
906
+ if (this.connectionType === types_1.P2PConnectionType.ONLY_LOCAL) {
907
+ if ((0, utils_1.isPrivateIp)(ip)) {
908
+ this._clearLookupTimeout();
909
+ this._clearLookupRetryTimeout();
910
+ logging_1.rootP2PLogger.debug(`Trying to connect in ONLY_LOCAL mode...`, {
911
+ stationSN: this.rawStation.station_sn,
912
+ ip: ip,
913
+ port: port,
914
+ });
915
+ this._connect({ host: ip, port: port }, this.rawStation.p2p_did);
916
+ }
917
+ }
918
+ else if (this.connectionType === types_1.P2PConnectionType.QUICKEST) {
919
+ this._clearLookupTimeout();
920
+ this._clearLookupRetryTimeout();
921
+ logging_1.rootP2PLogger.debug(`Trying to connect in QUICKEST mode...`, {
922
+ stationSN: this.rawStation.station_sn,
923
+ ip: ip,
924
+ port: port,
925
+ });
926
+ this._connect({ host: ip, port: port }, this.rawStation.p2p_did);
927
+ }
928
+ }
929
+ }
930
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.CAM_ID) || (0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.TURN_SERVER_CAM_ID)) {
931
+ // Answer from the device to a CAM_CHECK message
932
+ if (!this.connected) {
933
+ logging_1.rootP2PLogger.debug(`Received message - CAM_ID - Connected to station ${this.rawStation.station_sn} on host ${rinfo.address} port ${rinfo.port}`);
934
+ this._clearLocalLookupRetryTimeout();
935
+ this._clearLookupRetryTimeout();
936
+ this._clearLookup2RetryTimeout();
937
+ this._clearLookupTimeout();
938
+ this._clearConnectTimeout();
939
+ this._clearLookup2Timeout();
940
+ this.connected = true;
941
+ this.connectTime = new Date().getTime();
942
+ this.lastPong = null;
943
+ this.lastPongData = undefined;
944
+ this.connectAddress = { host: rinfo.address, port: rinfo.port };
945
+ if ((0, utils_1.isPrivateIp)(rinfo.address))
946
+ this.localIPAddress = rinfo.address;
947
+ this.scheduleHeartbeat();
948
+ if (device_1.Device.isSmartSafe(this.rawStation.device_type)) {
949
+ const payload = (0, utils_1.buildVoidCommandPayload)(http_1.Station.CHANNEL);
950
+ const data = Buffer.concat([(0, utils_1.buildCommandHeader)(this.seqNumber, types_1.CommandType.CMD_GATEWAYINFO), payload]);
951
+ const message = {
952
+ sequence: this.seqNumber,
953
+ commandType: types_1.CommandType.CMD_GATEWAYINFO,
954
+ channel: http_1.Station.CHANNEL,
955
+ data: data,
956
+ retries: 0,
957
+ acknowledged: false,
958
+ };
959
+ this.messageStates.set(message.sequence, message);
960
+ message.retryTimeout = setTimeout(() => {
961
+ this.resendNotAcknowledgedCommand(message.sequence);
962
+ }, this.RESEND_NOT_ACKNOWLEDGED_COMMAND);
963
+ this.seqNumber = this._incrementSequence(this.seqNumber);
964
+ this.sendMessage(`Send smartsafe gateway command to station`, this.connectAddress, types_1.RequestMessageType.DATA, data);
965
+ const tmpSendQueue = [...this.sendQueue];
966
+ this.sendQueue = [];
967
+ this.sendCommandPing(http_1.Station.CHANNEL);
968
+ tmpSendQueue.forEach((element) => {
969
+ this.sendQueue.push(element);
970
+ });
971
+ }
972
+ else if (device_1.Device.isLockWifiR10(this.rawStation.device_type) ||
973
+ device_1.Device.isLockWifiR20(this.rawStation.device_type)) {
974
+ const tmpSendQueue = [...this.sendQueue];
975
+ this.sendQueue = [];
976
+ const payload = (0, utils_1.buildVoidCommandPayload)(http_1.Station.CHANNEL);
977
+ const data = Buffer.concat([
978
+ (0, utils_1.buildCommandHeader)(0, types_1.CommandType.CMD_GATEWAYINFO),
979
+ payload.subarray(0, payload.length - 2),
980
+ (0, utils_1.buildCommandHeader)(0, types_1.CommandType.CMD_PING).subarray(2),
981
+ payload,
982
+ ]);
983
+ const message = {
984
+ sequence: this.seqNumber,
985
+ commandType: types_1.CommandType.CMD_PING,
986
+ channel: http_1.Station.CHANNEL,
987
+ data: data,
988
+ retries: 0,
989
+ acknowledged: false,
990
+ };
991
+ this.messageStates.set(message.sequence, message);
992
+ message.retryTimeout = setTimeout(() => {
993
+ this.resendNotAcknowledgedCommand(message.sequence);
994
+ }, this.RESEND_NOT_ACKNOWLEDGED_COMMAND);
995
+ this.seqNumber = this._incrementSequence(this.seqNumber);
996
+ this.sendMessage(`Send lock wifi gateway v12 command to station`, this.connectAddress, types_1.RequestMessageType.DATA, data);
997
+ tmpSendQueue.forEach((element) => {
998
+ this.sendQueue.push(element);
999
+ });
1000
+ }
1001
+ else if (this.rawStation.devices !== undefined &&
1002
+ this.rawStation.devices !== null &&
1003
+ this.rawStation.devices.length !== undefined &&
1004
+ this.rawStation.devices.length > 0 &&
1005
+ device_1.Device.isLockWifiVideo(this.rawStation.devices[0]?.device_type)) {
1006
+ const tmpSendQueue = [...this.sendQueue];
1007
+ this.sendQueue = [];
1008
+ const payload = (0, utils_1.buildVoidCommandPayload)(http_1.Station.CHANNEL);
1009
+ const data = Buffer.concat([
1010
+ (0, utils_1.buildCommandHeader)(0, types_1.CommandType.CMD_GATEWAYINFO),
1011
+ payload.subarray(0, payload.length - 2),
1012
+ (0, utils_1.buildCommandHeader)(0, types_1.CommandType.CMD_PING).subarray(2),
1013
+ payload,
1014
+ ]);
1015
+ const message = {
1016
+ sequence: this.seqNumber,
1017
+ commandType: types_1.CommandType.CMD_PING,
1018
+ channel: http_1.Station.CHANNEL,
1019
+ data: data,
1020
+ retries: 0,
1021
+ acknowledged: false,
1022
+ };
1023
+ this.messageStates.set(message.sequence, message);
1024
+ message.retryTimeout = setTimeout(() => {
1025
+ this.resendNotAcknowledgedCommand(message.sequence);
1026
+ }, this.RESEND_NOT_ACKNOWLEDGED_COMMAND);
1027
+ this.seqNumber = this._incrementSequence(this.seqNumber);
1028
+ this.sendMessage(`Send lock wifi gateway command to station`, this.connectAddress, types_1.RequestMessageType.DATA, data);
1029
+ tmpSendQueue.forEach((element) => {
1030
+ this.sendQueue.push(element);
1031
+ });
1032
+ }
1033
+ else {
1034
+ const tmpSendQueue = [...this.sendQueue];
1035
+ this.sendQueue = [];
1036
+ this.sendCommandWithoutData(types_1.CommandType.CMD_GATEWAYINFO, http_1.Station.CHANNEL);
1037
+ tmpSendQueue.forEach((element) => {
1038
+ this.sendQueue.push(element);
1039
+ });
1040
+ }
1041
+ this.sendQueuedMessage();
1042
+ if (this.energySavingDevice) {
1043
+ this.scheduleP2PKeepalive();
1044
+ }
1045
+ this.emit("connect", this.connectAddress);
1046
+ }
1047
+ }
1048
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.PONG)) {
1049
+ // Response to a ping from our side
1050
+ this.lastPong = new Date().getTime();
1051
+ if (msg.length > 4)
1052
+ this.lastPongData = msg.subarray(4);
1053
+ else
1054
+ this.lastPongData = undefined;
1055
+ return;
1056
+ }
1057
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.PING)) {
1058
+ // Response with PONG to keep alive
1059
+ this.sendMessage(`Send pong`, { host: rinfo.address, port: rinfo.port }, types_1.RequestMessageType.PONG);
1060
+ return;
1061
+ }
1062
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.END)) {
1063
+ // Connection is closed by device
1064
+ logging_1.rootP2PLogger.debug(`Received message - END`, {
1065
+ stationSN: this.rawStation.station_sn,
1066
+ remoteAddress: rinfo.address,
1067
+ remotePort: rinfo.port,
1068
+ });
1069
+ if (this.energySavingDevice && this.connected) {
1070
+ this.closeEnergySavingDevice();
1071
+ }
1072
+ this.onClose();
1073
+ return;
1074
+ }
1075
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.ACK)) {
1076
+ // Device ACK a message from our side
1077
+ // Number of Acks sended in the message
1078
+ const dataTypeBuffer = msg.subarray(4, 6);
1079
+ const dataType = this.getDataType(dataTypeBuffer);
1080
+ const numAcksBuffer = msg.subarray(6, 8);
1081
+ const numAcks = numAcksBuffer.readUIntBE(0, numAcksBuffer.length);
1082
+ for (let i = 1; i <= numAcks; i++) {
1083
+ const idx = 6 + i * 2;
1084
+ const seqBuffer = msg.subarray(idx, idx + 2);
1085
+ const ackedSeqNo = seqBuffer.readUIntBE(0, seqBuffer.length);
1086
+ // -> Message with seqNo was received at the station
1087
+ logging_1.rootP2PLogger.trace(`Received message - ACK ${types_1.P2PDataType[dataType]} - sequence ${ackedSeqNo}`, {
1088
+ stationSN: this.rawStation.station_sn,
1089
+ remoteAddress: rinfo.address,
1090
+ remotePort: rinfo.port,
1091
+ ackedSeqNo: ackedSeqNo,
1092
+ dataType: types_1.P2PDataType[dataType],
1093
+ });
1094
+ if (dataType === types_1.P2PDataType.DATA) {
1095
+ const msg_state = this.messageStates.get(ackedSeqNo);
1096
+ if (msg_state && !msg_state.acknowledged) {
1097
+ this._clearTimeout(msg_state.retryTimeout);
1098
+ this._clearTimeout(msg_state.timeout);
1099
+ if (msg_state.commandType === types_1.CommandType.CMD_PING ||
1100
+ msg_state.commandType === types_1.CommandType.CMD_GET_DEVICE_PING) {
1101
+ this.messageStates.delete(ackedSeqNo);
1102
+ this.sendQueuedMessage();
1103
+ }
1104
+ else {
1105
+ msg_state.acknowledged = true;
1106
+ msg_state.timeout = setTimeout(() => {
1107
+ //TODO: Retry command in these case?
1108
+ if (msg_state.commandType !== types_1.CommandType.CMD_GATEWAYINFO) {
1109
+ logging_1.rootP2PLogger.warn(`Result data for command not received`, {
1110
+ stationSN: this.rawStation.station_sn,
1111
+ message: {
1112
+ sequence: msg_state.sequence,
1113
+ commandType: msg_state.commandType,
1114
+ nestedCommandType: msg_state.nestedCommandType,
1115
+ channel: msg_state.channel,
1116
+ acknowledged: msg_state.acknowledged,
1117
+ retries: msg_state.retries,
1118
+ returnCode: msg_state.returnCode,
1119
+ data: msg_state.data,
1120
+ },
1121
+ });
1122
+ this.emit("command", {
1123
+ command_type: msg_state.nestedCommandType !== undefined ? msg_state.nestedCommandType : msg_state.commandType,
1124
+ channel: msg_state.channel,
1125
+ return_code: types_1.ErrorCode.ERROR_COMMAND_TIMEOUT,
1126
+ customData: msg_state.customData,
1127
+ });
1128
+ }
1129
+ else {
1130
+ this.p2pSeqMapping.forEach((value, key, map) => {
1131
+ if (value === ackedSeqNo) {
1132
+ map.delete(key);
1133
+ }
1134
+ });
1135
+ this.p2pDataSeqNumber--;
1136
+ logging_1.rootP2PLogger.debug(`Result data for command CMD_GATEWAYINFO not received`, {
1137
+ stationSN: this.rawStation.station_sn,
1138
+ message: {
1139
+ sequence: msg_state.sequence,
1140
+ commandType: msg_state.commandType,
1141
+ nestedCommandType: msg_state.nestedCommandType,
1142
+ channel: msg_state.channel,
1143
+ acknowledged: msg_state.acknowledged,
1144
+ retries: msg_state.retries,
1145
+ returnCode: msg_state.returnCode,
1146
+ data: msg_state.data,
1147
+ },
1148
+ });
1149
+ }
1150
+ this.messageStates.delete(ackedSeqNo);
1151
+ this.sendQueuedMessage();
1152
+ }, msg_state.commandType !== types_1.CommandType.CMD_GATEWAYINFO
1153
+ ? this.MAX_COMMAND_RESULT_WAIT
1154
+ : this.MAX_GATEWAY_COMMAND_RESULT_WAIT);
1155
+ this.messageStates.set(ackedSeqNo, msg_state);
1156
+ this.sendQueuedMessage();
1157
+ }
1158
+ }
1159
+ }
1160
+ else if (dataType === types_1.P2PDataType.VIDEO) {
1161
+ const msg_state = this.messageVideoStates.get(ackedSeqNo);
1162
+ if (msg_state) {
1163
+ this._clearTimeout(msg_state.timeout);
1164
+ this.messageVideoStates.delete(ackedSeqNo);
1165
+ }
1166
+ }
1167
+ }
1168
+ }
1169
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.DATA)) {
1170
+ if (this.connected) {
1171
+ const seqNo = msg.subarray(6, 8).readUInt16BE();
1172
+ const dataTypeBuffer = msg.subarray(4, 6);
1173
+ const dataType = this.getDataType(dataTypeBuffer);
1174
+ const message = {
1175
+ bytesToRead: msg.subarray(2, 4).readUInt16BE(),
1176
+ type: dataType,
1177
+ seqNo: seqNo,
1178
+ data: msg.subarray(8),
1179
+ };
1180
+ this.sendAck({ host: rinfo.address, port: rinfo.port }, dataTypeBuffer, seqNo);
1181
+ logging_1.rootP2PLogger.debug(`Received message - DATA ${types_1.P2PDataType[message.type]} - Processing sequence ${message.seqNo}...`, {
1182
+ stationSN: this.rawStation.station_sn,
1183
+ remoteAddress: rinfo.address,
1184
+ remotePort: rinfo.port,
1185
+ dataType: types_1.P2PDataType[message.type],
1186
+ seqNo: message.seqNo,
1187
+ });
1188
+ if (message.seqNo === this.expectedSeqNo[dataType]) {
1189
+ // expected seq packet arrived
1190
+ const timeout = this.currentMessageState[dataType].waitForSeqNoTimeout;
1191
+ if (!!timeout) {
1192
+ clearTimeout(timeout);
1193
+ this.currentMessageState[dataType].waitForSeqNoTimeout = undefined;
1194
+ }
1195
+ this.expectedSeqNo[dataType] = this._incrementSequence(this.expectedSeqNo[dataType]);
1196
+ this.parseDataMessage(message);
1197
+ logging_1.rootP2PLogger.trace(`Received message - DATA ${types_1.P2PDataType[message.type]} - Received expected sequence`, {
1198
+ stationSN: this.rawStation.station_sn,
1199
+ remoteAddress: rinfo.address,
1200
+ remotePort: rinfo.port,
1201
+ dataType: types_1.P2PDataType[message.type],
1202
+ seqNo: message.seqNo,
1203
+ expectedSeqNo: this.expectedSeqNo[dataType],
1204
+ queuedDataSize: this.currentMessageState[dataType].queuedData.size,
1205
+ });
1206
+ let queuedMessage = this.currentMessageState[dataType].queuedData.get(this.expectedSeqNo[dataType]);
1207
+ while (queuedMessage) {
1208
+ logging_1.rootP2PLogger.trace(`Received message - DATA ${types_1.P2PDataType[queuedMessage.type]} - Work off queued data`, {
1209
+ stationSN: this.rawStation.station_sn,
1210
+ remoteAddress: rinfo.address,
1211
+ remotePort: rinfo.port,
1212
+ dataType: types_1.P2PDataType[message.type],
1213
+ seqNo: queuedMessage.seqNo,
1214
+ expectedSeqNo: this.expectedSeqNo[dataType],
1215
+ queuedDataSize: this.currentMessageState[dataType].queuedData.size,
1216
+ });
1217
+ this.expectedSeqNo[dataType] = this._incrementSequence(this.expectedSeqNo[dataType]);
1218
+ this.parseDataMessage(queuedMessage);
1219
+ this.currentMessageState[dataType].queuedData.delete(queuedMessage.seqNo);
1220
+ queuedMessage = this.currentMessageState[dataType].queuedData.get(this.expectedSeqNo[dataType]);
1221
+ }
1222
+ }
1223
+ else if (this._wasSequenceNumberAlreadyProcessed(this.expectedSeqNo[dataType], message.seqNo)) {
1224
+ // We have already seen this message, skip!
1225
+ // This can happen because the device is sending the message till it gets a ACK
1226
+ // which can take some time.
1227
+ logging_1.rootP2PLogger.trace(`Received message - DATA ${types_1.P2PDataType[message.type]} - Received already processed sequence`, {
1228
+ stationSN: this.rawStation.station_sn,
1229
+ remoteAddress: rinfo.address,
1230
+ remotePort: rinfo.port,
1231
+ dataType: types_1.P2PDataType[message.type],
1232
+ seqNo: message.seqNo,
1233
+ expectedSeqNo: this.expectedSeqNo[dataType],
1234
+ queuedDataSize: this.currentMessageState[dataType].queuedData.size,
1235
+ });
1236
+ return;
1237
+ }
1238
+ else {
1239
+ if (!this.currentMessageState[dataType].waitForSeqNoTimeout)
1240
+ this.currentMessageState[dataType].waitForSeqNoTimeout = setTimeout(() => {
1241
+ this.endStream(dataType, true);
1242
+ this.currentMessageState[dataType].waitForSeqNoTimeout = undefined;
1243
+ }, this.MAX_EXPECTED_SEQNO_WAIT);
1244
+ if (!this.currentMessageState[dataType].queuedData.get(message.seqNo)) {
1245
+ this.currentMessageState[dataType].queuedData.set(message.seqNo, message);
1246
+ logging_1.rootP2PLogger.trace(`Received message - DATA ${types_1.P2PDataType[message.type]} - Received not expected sequence, added to the queue for future processing`, {
1247
+ stationSN: this.rawStation.station_sn,
1248
+ remoteAddress: rinfo.address,
1249
+ remotePort: rinfo.port,
1250
+ dataType: types_1.P2PDataType[message.type],
1251
+ seqNo: message.seqNo,
1252
+ expectedSeqNo: this.expectedSeqNo[dataType],
1253
+ queuedDataSize: this.currentMessageState[dataType].queuedData.size,
1254
+ });
1255
+ }
1256
+ else {
1257
+ logging_1.rootP2PLogger.trace(`Received message - DATA ${types_1.P2PDataType[message.type]} - Received not expected sequence, discarded since already present in queue for future processing`, {
1258
+ stationSN: this.rawStation.station_sn,
1259
+ remoteAddress: rinfo.address,
1260
+ remotePort: rinfo.port,
1261
+ dataType: types_1.P2PDataType[message.type],
1262
+ seqNo: message.seqNo,
1263
+ expectedSeqNo: this.expectedSeqNo[dataType],
1264
+ queuedDataSize: this.currentMessageState[dataType].queuedData.size,
1265
+ });
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.LOOKUP_ADDR2)) {
1271
+ if (!this.connected) {
1272
+ const port = msg.subarray(6, 8).readUInt16LE();
1273
+ const ip = `${msg[11]}.${msg[10]}.${msg[9]}.${msg[8]}`;
1274
+ const data = msg.subarray(20, 24);
1275
+ this._clearLookupTimeout();
1276
+ this._clearLookupRetryTimeout();
1277
+ this._clearLookup2RetryTimeout();
1278
+ logging_1.rootP2PLogger.trace(`Received message - LOOKUP_ADDR2 - Got response`, {
1279
+ stationSN: this.rawStation.station_sn,
1280
+ remoteAddress: rinfo.address,
1281
+ remotePort: rinfo.port,
1282
+ response: { ip: ip, port: port, data: data.toString("hex") },
1283
+ });
1284
+ logging_1.rootP2PLogger.debug(`Connecting to host ${ip} on port ${port} (CHECK_CAM2)...`, {
1285
+ stationSN: this.rawStation.station_sn,
1286
+ ip: ip,
1287
+ port: port,
1288
+ data: data.toString("hex"),
1289
+ });
1290
+ for (let i = 0; i < 4; i++)
1291
+ this.sendCamCheck2({ host: ip, port: port }, data);
1292
+ this._startConnectTimeout();
1293
+ if (this.p2pTurnHandshaking[ip] === undefined) {
1294
+ this.p2pTurnHandshaking[ip] = true;
1295
+ this.sendMessage(`Send TURN_SERVER_INIT`, { host: ip, port: port }, types_1.RequestMessageType.TURN_SERVER_INIT);
1296
+ }
1297
+ }
1298
+ }
1299
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.TURN_SERVER_OK)) {
1300
+ if (!this.connected && !this.p2pTurnConfirmed) {
1301
+ logging_1.rootP2PLogger.trace(`Received message - TURN_SERVER_OK - Got response`, {
1302
+ stationSN: this.rawStation.station_sn,
1303
+ remoteAddress: rinfo.address,
1304
+ remotePort: rinfo.port,
1305
+ response: { message: msg.toString("hex"), length: msg.length },
1306
+ });
1307
+ this.sendMessage(`Send TURN_CLIENT_OK`, { host: rinfo.address, port: rinfo.port }, types_1.RequestMessageType.TURN_CLIENT_OK);
1308
+ this.p2pTurnConfirmed = true;
1309
+ }
1310
+ }
1311
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.TURN_SERVER_TOKEN)) {
1312
+ if (!this.connected) {
1313
+ const ip = `${msg[7]}.${msg[6]}.${msg[5]}.${msg[4]}`;
1314
+ const port = msg.subarray(8, 10).readUInt16BE();
1315
+ const binaryIP = msg.subarray(4, 8);
1316
+ logging_1.rootP2PLogger.debug(`Connecting to host ${ip} on port ${port} (CHECK_CAM2)...`, {
1317
+ stationSN: this.rawStation.station_sn,
1318
+ ip: ip,
1319
+ port: port,
1320
+ binaryIP: binaryIP.toString("hex"),
1321
+ });
1322
+ for (let i = 0; i < 4; i++)
1323
+ this.sendCamCheck2({ host: rinfo.address, port: port }, binaryIP);
1324
+ logging_1.rootP2PLogger.trace(`Received message - TURN_SERVER_TOKEN - Got response`, {
1325
+ stationSN: this.rawStation.station_sn,
1326
+ remoteAddress: rinfo.address,
1327
+ remotePort: rinfo.port,
1328
+ response: { port: port, binaryIP: binaryIP.toString("hex") },
1329
+ });
1330
+ this.cloudLookupWithTurnServer({ host: rinfo.address, port: port }, binaryIP);
1331
+ }
1332
+ }
1333
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.TURN_SERVER_LOOKUP_OK) ||
1334
+ (0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.UNKNOWN_83) ||
1335
+ (0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.TURN_SERVER_LIST)) {
1336
+ // Do nothing / ignore
1337
+ }
1338
+ else if ((0, utils_1.hasHeader)(msg, types_1.ResponseMessageType.LOOKUP_RESP)) {
1339
+ if (!this.connected) {
1340
+ const responseCode = msg.subarray(4, 6).readUInt16LE();
1341
+ logging_1.rootP2PLogger.trace(`Received message - LOOKUP_RESP - Got response`, {
1342
+ stationSN: this.rawStation.station_sn,
1343
+ remoteAddress: rinfo.address,
1344
+ remotePort: rinfo.port,
1345
+ response: { responseCode: responseCode },
1346
+ });
1347
+ }
1348
+ }
1349
+ else {
1350
+ logging_1.rootP2PLogger.debug(`Received unknown message`, {
1351
+ stationSN: this.rawStation.station_sn,
1352
+ remoteAddress: rinfo.address,
1353
+ remotePort: rinfo.port,
1354
+ response: { message: msg.toString("hex"), length: msg.length },
1355
+ });
1356
+ }
1357
+ }
1358
+ parseDataMessage(message) {
1359
+ if ((message.type === types_1.P2PDataType.BINARY || message.type === types_1.P2PDataType.VIDEO) &&
1360
+ !this.currentMessageState[message.type].p2pStreaming) {
1361
+ logging_1.rootP2PLogger.trace(`Parsing message - DATA ${types_1.P2PDataType[message.type]} - Stream not started ignore this data`, {
1362
+ stationSN: this.rawStation.station_sn,
1363
+ seqNo: message.seqNo,
1364
+ header: this.currentMessageBuilder[message.type].header,
1365
+ bytesRead: this.currentMessageBuilder[message.type].bytesRead,
1366
+ bytesToRead: message.bytesToRead,
1367
+ messageSize: message.data.length,
1368
+ });
1369
+ }
1370
+ else {
1371
+ if (this.currentMessageState[message.type].leftoverData.length > 0) {
1372
+ message.data = Buffer.concat([this.currentMessageState[message.type].leftoverData, message.data]);
1373
+ this.currentMessageState[message.type].leftoverData = Buffer.from([]);
1374
+ }
1375
+ let data = message.data;
1376
+ let runaway_limit = 0;
1377
+ do {
1378
+ // is this the first message?
1379
+ const firstPartMessage = data.subarray(0, 4).toString() === utils_1.MAGIC_WORD;
1380
+ if (firstPartMessage) {
1381
+ const header = {
1382
+ commandId: 0,
1383
+ bytesToRead: 0,
1384
+ channel: 0,
1385
+ signCode: 0,
1386
+ type: 0,
1387
+ };
1388
+ header.commandId = data.subarray(4, 6).readUIntLE(0, 2);
1389
+ header.bytesToRead = data.subarray(6, 10).readUIntLE(0, 4);
1390
+ header.channel = data.subarray(12, 13).readUInt8();
1391
+ header.signCode = data.subarray(13, 14).readUInt8();
1392
+ header.type = data.subarray(14, 15).readUInt8();
1393
+ this.currentMessageBuilder[message.type].header = header;
1394
+ data = data.subarray(this.P2P_DATA_HEADER_BYTES);
1395
+ if (data.length >= header.bytesToRead) {
1396
+ const payload = data.subarray(0, header.bytesToRead);
1397
+ this.currentMessageBuilder[message.type].messages[message.seqNo] = payload;
1398
+ this.currentMessageBuilder[message.type].bytesRead = payload.byteLength;
1399
+ data = data.subarray(header.bytesToRead);
1400
+ if (data.length <= this.P2P_DATA_HEADER_BYTES) {
1401
+ this.currentMessageState[message.type].leftoverData = data;
1402
+ data = Buffer.from([]);
1403
+ }
1404
+ }
1405
+ else {
1406
+ if (data.length <= this.P2P_DATA_HEADER_BYTES) {
1407
+ this.currentMessageState[message.type].leftoverData = data;
1408
+ }
1409
+ else {
1410
+ this.currentMessageBuilder[message.type].messages[message.seqNo] = data;
1411
+ this.currentMessageBuilder[message.type].bytesRead = data.byteLength;
1412
+ }
1413
+ data = Buffer.from([]);
1414
+ }
1415
+ }
1416
+ else {
1417
+ // finish message and print
1418
+ if (this.currentMessageBuilder[message.type].header.bytesToRead -
1419
+ this.currentMessageBuilder[message.type].bytesRead ===
1420
+ 0 &&
1421
+ data.length > this.P2P_DATA_HEADER_BYTES) {
1422
+ logging_1.rootP2PLogger.debug(`Parsing message - DATA ${types_1.P2PDataType[message.type]} - Discarding unexpected data (infinite loop prevention)`, {
1423
+ stationSN: this.rawStation.station_sn,
1424
+ seqNo: message.seqNo,
1425
+ dataSize: data.length,
1426
+ data: data.toString("hex"),
1427
+ });
1428
+ data = Buffer.from([]);
1429
+ }
1430
+ else if (this.currentMessageBuilder[message.type].header.bytesToRead -
1431
+ this.currentMessageBuilder[message.type].bytesRead <=
1432
+ data.length) {
1433
+ const payload = data.subarray(0, this.currentMessageBuilder[message.type].header.bytesToRead -
1434
+ this.currentMessageBuilder[message.type].bytesRead);
1435
+ this.currentMessageBuilder[message.type].messages[message.seqNo] = payload;
1436
+ this.currentMessageBuilder[message.type].bytesRead += payload.byteLength;
1437
+ data = data.subarray(payload.byteLength);
1438
+ if (data.length <= this.P2P_DATA_HEADER_BYTES) {
1439
+ this.currentMessageState[message.type].leftoverData = data;
1440
+ data = Buffer.from([]);
1441
+ }
1442
+ }
1443
+ else {
1444
+ if (data.length <= this.P2P_DATA_HEADER_BYTES) {
1445
+ this.currentMessageState[message.type].leftoverData = data;
1446
+ }
1447
+ else {
1448
+ this.currentMessageBuilder[message.type].messages[message.seqNo] = data;
1449
+ this.currentMessageBuilder[message.type].bytesRead += data.byteLength;
1450
+ }
1451
+ data = Buffer.from([]);
1452
+ }
1453
+ }
1454
+ logging_1.rootP2PLogger.trace(`Parsing message - DATA ${types_1.P2PDataType[message.type]} - Received data`, {
1455
+ stationSN: this.rawStation.station_sn,
1456
+ seqNo: message.seqNo,
1457
+ header: this.currentMessageBuilder[message.type].header,
1458
+ bytesRead: this.currentMessageBuilder[message.type].bytesRead,
1459
+ bytesToRead: this.currentMessageBuilder[message.type].header.bytesToRead,
1460
+ firstPartMessage: firstPartMessage,
1461
+ messageSize: message.data.length,
1462
+ runaway_limit: runaway_limit,
1463
+ });
1464
+ if (this.currentMessageBuilder[message.type].bytesRead ===
1465
+ this.currentMessageBuilder[message.type].header.bytesToRead) {
1466
+ const completeMessage = (0, utils_1.sortP2PMessageParts)(this.currentMessageBuilder[message.type].messages);
1467
+ const data_message = {
1468
+ ...this.currentMessageBuilder[message.type].header,
1469
+ seqNo: message.seqNo + this.offsetDataSeqNumber,
1470
+ dataType: message.type,
1471
+ data: completeMessage,
1472
+ };
1473
+ this.handleData(data_message);
1474
+ this.initializeMessageBuilder(message.type);
1475
+ if (data.length > 0 && message.type === types_1.P2PDataType.DATA) {
1476
+ logging_1.rootP2PLogger.debug(`Parsing message - DATA ${types_1.P2PDataType[message.type]} - Parsed data`, {
1477
+ stationSN: this.rawStation.station_sn,
1478
+ seqNo: message.seqNo,
1479
+ data_message: data_message,
1480
+ datalen: data.length,
1481
+ data: data.toString("hex"),
1482
+ offsetDataSeqNumber: this.offsetDataSeqNumber,
1483
+ seqNumber: this.seqNumber,
1484
+ p2pDataSeqNumber: this.p2pDataSeqNumber,
1485
+ });
1486
+ this.offsetDataSeqNumber++;
1487
+ }
1488
+ }
1489
+ runaway_limit++;
1490
+ } while (data.length > 0 && runaway_limit < this.LOOP_RUNAWAY_LIMIT);
1491
+ if (runaway_limit >= this.LOOP_RUNAWAY_LIMIT) {
1492
+ logging_1.rootP2PLogger.warn(`Infinite loop detected (limit >= ${this.LOOP_RUNAWAY_LIMIT}) during parsing of p2p message`, {
1493
+ stationSN: this.rawStation.station_sn,
1494
+ seqNo: message.seqNo,
1495
+ dataType: types_1.P2PDataType[message.type],
1496
+ header: this.currentMessageBuilder[message.type].header,
1497
+ bytesRead: this.currentMessageBuilder[message.type].bytesRead,
1498
+ bytesToRead: this.currentMessageBuilder[message.type].header.bytesToRead,
1499
+ message: message.data.toString("hex"),
1500
+ messageSize: message.data.length,
1501
+ });
1502
+ this.initializeMessageBuilder(message.type);
1503
+ }
1504
+ }
1505
+ }
1506
+ handleData(message) {
1507
+ if (message.dataType === types_1.P2PDataType.CONTROL) {
1508
+ this.handleDataControl(message);
1509
+ }
1510
+ else if (message.dataType === types_1.P2PDataType.DATA) {
1511
+ const commandStr = types_1.CommandType[message.commandId];
1512
+ const result_msg = message.type === 1 ? true : false;
1513
+ if (result_msg) {
1514
+ let return_code = 0;
1515
+ let resultData;
1516
+ if (message.bytesToRead > 0) {
1517
+ if (message.signCode > 0) {
1518
+ try {
1519
+ message.data = (0, utils_1.decryptP2PData)(message.data, this.p2pKey);
1520
+ }
1521
+ catch (err) {
1522
+ const error = (0, error_1.ensureError)(err);
1523
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Decrypt Error`, {
1524
+ error: (0, utils_3.getError)(error),
1525
+ stationSN: this.rawStation.station_sn,
1526
+ message: {
1527
+ seqNo: message.seqNo,
1528
+ channel: message.channel,
1529
+ commandType: types_1.CommandType[message.commandId],
1530
+ signCode: message.signCode,
1531
+ type: message.type,
1532
+ dataType: types_1.P2PDataType[message.dataType],
1533
+ data: message.data.toString("hex"),
1534
+ },
1535
+ });
1536
+ }
1537
+ }
1538
+ return_code = message.data.subarray(0, 4).readUInt32LE() | 0;
1539
+ resultData = message.data.subarray(4);
1540
+ }
1541
+ const error_codeStr = types_1.ErrorCode[return_code];
1542
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Received data`, {
1543
+ stationSN: this.rawStation.station_sn,
1544
+ seqNo: message.seqNo,
1545
+ commandIdName: commandStr,
1546
+ commandId: message.commandId,
1547
+ resultCodeName: error_codeStr,
1548
+ resultCode: return_code,
1549
+ resultData: resultData?.toString("hex"),
1550
+ data: message.data.toString("hex"),
1551
+ seqNumber: this.seqNumber,
1552
+ p2pDataSeqNumber: this.p2pDataSeqNumber,
1553
+ offsetDataSeqNumber: this.offsetDataSeqNumber,
1554
+ });
1555
+ let msg_state = this.messageStates.get(message.seqNo);
1556
+ const goodSeqNumber = this.p2pSeqMapping.get(message.seqNo);
1557
+ if (goodSeqNumber) {
1558
+ this.p2pSeqMapping.delete(message.seqNo);
1559
+ msg_state = this.messageStates.get(goodSeqNumber);
1560
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Result data received - Detecting correct sequence number`, {
1561
+ stationSN: this.rawStation.station_sn,
1562
+ commandIdName: commandStr,
1563
+ commandId: message.commandId,
1564
+ seqNumber: message.seqNo,
1565
+ newSeqNumber: goodSeqNumber,
1566
+ p2pSeqMappingCount: this.p2pSeqMapping.size,
1567
+ });
1568
+ message.seqNo = goodSeqNumber;
1569
+ }
1570
+ if (msg_state) {
1571
+ if (msg_state.commandType === message.commandId) {
1572
+ this._clearTimeout(msg_state.timeout);
1573
+ const command_type = msg_state.nestedCommandType !== undefined ? msg_state.nestedCommandType : msg_state.commandType;
1574
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Result data for command received`, {
1575
+ stationSN: this.rawStation.station_sn,
1576
+ message: {
1577
+ sequence: msg_state.sequence,
1578
+ commandType: msg_state.commandType,
1579
+ nestedCommandType: msg_state.nestedCommandType,
1580
+ channel: msg_state.channel,
1581
+ acknowledged: msg_state.acknowledged,
1582
+ retries: msg_state.retries,
1583
+ returnCode: return_code,
1584
+ data: msg_state.data,
1585
+ customData: msg_state.customData,
1586
+ },
1587
+ resultCodeName: error_codeStr,
1588
+ resultCode: return_code,
1589
+ });
1590
+ if (return_code === types_1.ErrorCode.ERROR_FAILED_TO_REQUEST) {
1591
+ msg_state.returnCode = return_code;
1592
+ this._sendCommand(msg_state);
1593
+ }
1594
+ else {
1595
+ if (command_type !== types_1.CommandType.CMD_GATEWAYINFO) {
1596
+ this.emit("command", {
1597
+ command_type: command_type,
1598
+ channel: msg_state.channel,
1599
+ return_code: return_code,
1600
+ customData: msg_state.customData,
1601
+ });
1602
+ }
1603
+ this.messageStates.delete(message.seqNo);
1604
+ if (command_type === types_1.CommandType.CMD_SMARTSAFE_SETTINGS ||
1605
+ command_type === types_1.CommandType.CMD_SET_PAYLOAD_LOCKV12 ||
1606
+ command_type === types_1.CommandType.CMD_TRANSFER_PAYLOAD) {
1607
+ if (msg_state.customData) {
1608
+ for (const index of Object.keys(this.customDataStaging)) {
1609
+ const id = Number.parseInt(index);
1610
+ if (new Date().getTime() - this.customDataStaging[id].timestamp > 2 * 60 * 1000) {
1611
+ delete this.customDataStaging[id];
1612
+ }
1613
+ }
1614
+ this.customDataStaging[msg_state.nestedCommandType2
1615
+ ? msg_state.nestedCommandType2
1616
+ : msg_state.nestedCommandType
1617
+ ? msg_state.nestedCommandType
1618
+ : msg_state.commandType] = {
1619
+ channel: msg_state.channel,
1620
+ customData: msg_state.customData,
1621
+ timestamp: new Date().getTime(),
1622
+ };
1623
+ }
1624
+ this.secondaryCommandTimeout = setTimeout(() => {
1625
+ logging_1.rootP2PLogger.warn(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Result data for secondary command not received`, {
1626
+ stationSN: this.rawStation.station_sn,
1627
+ message: {
1628
+ sequence: msg_state.sequence,
1629
+ commandType: msg_state.commandType,
1630
+ nestedCommandType: msg_state.nestedCommandType,
1631
+ channel: msg_state.channel,
1632
+ acknowledged: msg_state.acknowledged,
1633
+ retries: msg_state.retries,
1634
+ returnCode: msg_state.returnCode,
1635
+ data: msg_state.data,
1636
+ customData: msg_state.customData,
1637
+ },
1638
+ });
1639
+ this.secondaryCommandTimeout = undefined;
1640
+ this.emit("secondary command", {
1641
+ command_type: msg_state.nestedCommandType !== undefined
1642
+ ? msg_state.nestedCommandType
1643
+ : msg_state.commandType,
1644
+ channel: msg_state.channel,
1645
+ return_code: types_1.ErrorCode.ERROR_COMMAND_TIMEOUT,
1646
+ customData: msg_state.customData,
1647
+ });
1648
+ this._clearESDDisconnectTimeout();
1649
+ this.sendQueuedMessage();
1650
+ }, this.MAX_COMMAND_RESULT_WAIT);
1651
+ }
1652
+ else {
1653
+ this._clearESDDisconnectTimeout();
1654
+ this.sendQueuedMessage();
1655
+ }
1656
+ if (msg_state.commandType === types_1.CommandType.CMD_START_REALTIME_MEDIA ||
1657
+ (msg_state.nestedCommandType === types_1.CommandType.CMD_START_REALTIME_MEDIA &&
1658
+ msg_state.commandType === types_1.CommandType.CMD_SET_PAYLOAD) ||
1659
+ msg_state.commandType === types_1.CommandType.CMD_RECORD_VIEW ||
1660
+ (msg_state.nestedCommandType === http_1.ParamType.COMMAND_START_LIVESTREAM &&
1661
+ msg_state.commandType === types_1.CommandType.CMD_DOORBELL_SET_PAYLOAD)) {
1662
+ this.waitForStreamData(types_1.P2PDataType.VIDEO, true);
1663
+ }
1664
+ else if (msg_state.commandType === types_1.CommandType.CMD_DOWNLOAD_VIDEO) {
1665
+ this.waitForStreamData(types_1.P2PDataType.BINARY, true);
1666
+ }
1667
+ else if (msg_state.commandType === types_1.CommandType.CMD_START_TALKBACK ||
1668
+ (msg_state.commandType === types_1.CommandType.CMD_DOORBELL_SET_PAYLOAD &&
1669
+ msg_state.nestedCommandType === types_1.IndoorSoloSmartdropCommandType.CMD_START_SPEAK)) {
1670
+ if (return_code === types_1.ErrorCode.ERROR_PPCS_SUCCESSFUL) {
1671
+ this.startTalkback(msg_state.channel);
1672
+ }
1673
+ else if (return_code === types_1.ErrorCode.ERROR_NOT_FIND_DEV) {
1674
+ this.emit("talkback error", msg_state.channel, new error_1.TalkbackError("Someone is responding now.", {
1675
+ context: { station: this.rawStation.station_sn, channel: msg_state.channel },
1676
+ }));
1677
+ }
1678
+ else if (return_code === types_1.ErrorCode.ERROR_DEV_BUSY) {
1679
+ this.emit("talkback error", msg_state.channel, new error_1.TalkbackError("Wait a second, device is busy.", {
1680
+ context: { station: this.rawStation.station_sn, channel: msg_state.channel },
1681
+ }));
1682
+ }
1683
+ else {
1684
+ this.emit("talkback error", msg_state.channel, new error_1.TalkbackError("Connect failed please try again later.", {
1685
+ context: { station: this.rawStation.station_sn, channel: msg_state.channel },
1686
+ }));
1687
+ }
1688
+ }
1689
+ else if (msg_state.commandType === types_1.CommandType.CMD_STOP_TALKBACK ||
1690
+ (msg_state.commandType === types_1.CommandType.CMD_DOORBELL_SET_PAYLOAD &&
1691
+ msg_state.nestedCommandType === types_1.IndoorSoloSmartdropCommandType.CMD_END_SPEAK)) {
1692
+ this.stopTalkback(msg_state.channel);
1693
+ }
1694
+ else if (msg_state.commandType === types_1.CommandType.CMD_SDINFO_EX && resultData && resultData.length >= 8) {
1695
+ const totalCapacity = resultData.subarray(0, 4).readUInt32LE();
1696
+ const availableCapacity = resultData.subarray(4, 8).readUInt32LE();
1697
+ if (return_code >= -1) {
1698
+ this.emit("sd info ex", return_code, totalCapacity, availableCapacity);
1699
+ }
1700
+ }
1701
+ }
1702
+ }
1703
+ else {
1704
+ this.messageStates.delete(message.seqNo);
1705
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Expected different command type for received sequencenumber!`, {
1706
+ stationSN: this.rawStation.station_sn,
1707
+ msg_sequence: msg_state.sequence,
1708
+ msg_channel: msg_state.channel,
1709
+ msg_commandType: msg_state.commandType,
1710
+ seqNumber: this.seqNumber,
1711
+ p2pDataSeqNumber: this.p2pDataSeqNumber,
1712
+ offsetDataSeqNumber: this.offsetDataSeqNumber,
1713
+ message: {
1714
+ seqNo: message.seqNo,
1715
+ commandType: types_1.CommandType[message.commandId],
1716
+ channel: message.channel,
1717
+ signCode: message.signCode,
1718
+ data: message.data.toString("hex"),
1719
+ },
1720
+ });
1721
+ logging_1.rootP2PLogger.warn(`P2P protocol instability detected for station ${this.rawStation.station_sn}. Please reinitialise the connection to solve the problem!`);
1722
+ }
1723
+ }
1724
+ else if (message.commandId !== types_1.CommandType.CMD_PING &&
1725
+ message.commandId !== types_1.CommandType.CMD_GET_DEVICE_PING &&
1726
+ message.commandId !== types_1.CommandType.CMD_GATEWAYINFO) {
1727
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Received unexpected data!`, {
1728
+ stationSN: this.rawStation.station_sn,
1729
+ seqNumber: this.seqNumber,
1730
+ p2pDataSeqNumber: this.p2pDataSeqNumber,
1731
+ offsetDataSeqNumber: this.offsetDataSeqNumber,
1732
+ message: {
1733
+ seqNo: message.seqNo,
1734
+ commandType: types_1.CommandType[message.commandId],
1735
+ channel: message.channel,
1736
+ signCode: message.signCode,
1737
+ data: message.data.toString("hex"),
1738
+ },
1739
+ });
1740
+ }
1741
+ }
1742
+ else {
1743
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Unsupported response`, {
1744
+ stationSN: this.rawStation.station_sn,
1745
+ message: {
1746
+ seqNo: message.seqNo,
1747
+ commandType: message.commandId,
1748
+ channel: message.channel,
1749
+ signCode: message.signCode,
1750
+ data: message.data.toString("hex"),
1751
+ },
1752
+ });
1753
+ }
1754
+ }
1755
+ else if (message.dataType === types_1.P2PDataType.VIDEO || message.dataType === types_1.P2PDataType.BINARY) {
1756
+ this.handleDataBinaryAndVideo(message);
1757
+ }
1758
+ else {
1759
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented data type`, {
1760
+ stationSN: this.rawStation.station_sn,
1761
+ message: {
1762
+ seqNo: message.seqNo,
1763
+ commandType: message.commandId,
1764
+ channel: message.channel,
1765
+ signCode: message.signCode,
1766
+ data: message.data.toString("hex"),
1767
+ },
1768
+ });
1769
+ }
1770
+ }
1771
+ isIFrame(data, isKeyFrame) {
1772
+ if (this.rawStation.station_sn.startsWith("T8410") ||
1773
+ this.rawStation.station_sn.startsWith("T8400") ||
1774
+ this.rawStation.station_sn.startsWith("T8401") ||
1775
+ this.rawStation.station_sn.startsWith("T8411") ||
1776
+ this.rawStation.station_sn.startsWith("T8202") ||
1777
+ this.rawStation.station_sn.startsWith("T8422") ||
1778
+ this.rawStation.station_sn.startsWith("T8424") ||
1779
+ this.rawStation.station_sn.startsWith("T8423") ||
1780
+ this.rawStation.station_sn.startsWith("T8130") ||
1781
+ this.rawStation.station_sn.startsWith("T8131") ||
1782
+ this.rawStation.station_sn.startsWith("T8420") ||
1783
+ this.rawStation.station_sn.startsWith("T8440") ||
1784
+ this.rawStation.station_sn.startsWith("T8171") ||
1785
+ this.rawStation.station_sn.startsWith("T8426") ||
1786
+ this.rawStation.station_sn.startsWith("T8441") ||
1787
+ this.rawStation.station_sn.startsWith("T8442") ||
1788
+ (0, utils_1.checkT8420)(this.rawStation.station_sn)) {
1789
+ //TODO: Need to add battery doorbells as seen in source => T8210,T8220,T8221,T8222
1790
+ return isKeyFrame;
1791
+ }
1792
+ const iframe = (0, utils_1.isIFrame)(data);
1793
+ if (iframe === false) {
1794
+ // Fallback
1795
+ return isKeyFrame;
1796
+ }
1797
+ return iframe;
1798
+ }
1799
+ waitForStreamData(dataType, sendStopCommand = false) {
1800
+ if (this.currentMessageState[dataType].p2pStreamingTimeout) {
1801
+ clearTimeout(this.currentMessageState[dataType].p2pStreamingTimeout);
1802
+ }
1803
+ this.currentMessageState[dataType].p2pStreamingTimeout = setTimeout(() => {
1804
+ logging_1.rootP2PLogger.info(`Stopping the station stream for the device ${this.deviceSNs[this.currentMessageState[dataType].p2pStreamChannel]?.sn}, because we haven't received any data for ${this.MAX_STREAM_DATA_WAIT / 1000} seconds`);
1805
+ this.endStream(dataType, sendStopCommand);
1806
+ }, this.MAX_STREAM_DATA_WAIT);
1807
+ }
1808
+ handleDataBinaryAndVideo(message) {
1809
+ if (!this.currentMessageState[message.dataType].invalidStream) {
1810
+ switch (message.commandId) {
1811
+ case types_1.CommandType.CMD_VIDEO_FRAME:
1812
+ this.waitForStreamData(message.dataType, true);
1813
+ const videoMetaData = {
1814
+ streamType: 0,
1815
+ videoSeqNo: 0,
1816
+ videoFPS: 15,
1817
+ videoWidth: 1920,
1818
+ videoHeight: 1080,
1819
+ videoTimestamp: 0,
1820
+ videoDataLength: 0,
1821
+ aesKey: "",
1822
+ };
1823
+ const data_length = message.data.readUInt32LE();
1824
+ const isKeyFrame = message.data.subarray(4, 5).readUInt8() === 1 ? true : false;
1825
+ videoMetaData.videoDataLength = message.data.subarray(0, 4).readUInt32LE();
1826
+ videoMetaData.streamType = message.data.subarray(5, 6).readUInt8();
1827
+ videoMetaData.videoSeqNo = message.data.subarray(6, 8).readUInt16LE();
1828
+ videoMetaData.videoFPS = message.data.subarray(8, 10).readUInt16LE();
1829
+ videoMetaData.videoWidth = message.data.subarray(10, 12).readUInt16LE();
1830
+ videoMetaData.videoHeight = message.data.subarray(12, 14).readUInt16LE();
1831
+ videoMetaData.videoTimestamp = message.data.subarray(14, 20).readUIntLE(0, 6);
1832
+ let payloadStart = 22;
1833
+ if (message.signCode > 0 && data_length >= 128) {
1834
+ const key = message.data.subarray(22, 150);
1835
+ const rsaKey = this.currentMessageState[message.dataType].rsaKey;
1836
+ if (rsaKey) {
1837
+ try {
1838
+ videoMetaData.aesKey = rsaKey.decrypt(key).toString("hex");
1839
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Decrypted AES key`, {
1840
+ stationSN: this.rawStation.station_sn,
1841
+ key: videoMetaData.aesKey,
1842
+ });
1843
+ }
1844
+ catch (err) {
1845
+ const error = (0, error_1.ensureError)(err);
1846
+ logging_1.rootP2PLogger.warn(`Error: AES key could not be decrypted! The entire stream is discarded.`, {
1847
+ error: (0, utils_3.getError)(error),
1848
+ stationSN: this.rawStation.station_sn,
1849
+ key: key.toString("hex"),
1850
+ });
1851
+ this.currentMessageState[message.dataType].invalidStream = true;
1852
+ this.emit("livestream error", message.channel, new error_1.LivestreamError("Station AES key could not be decrypted! The entire stream is discarded.", {
1853
+ context: { station: this.rawStation.station_sn },
1854
+ }));
1855
+ return;
1856
+ }
1857
+ }
1858
+ else {
1859
+ logging_1.rootP2PLogger.warn(`Private RSA key is missing! Stream could not be decrypted. The entire stream for station ${this.rawStation.station_sn} is discarded.`);
1860
+ this.currentMessageState[message.dataType].invalidStream = true;
1861
+ this.emit("livestream error", message.channel, new error_1.LivestreamError("Station Private RSA key is missing! Stream could not be decrypted. The entire stream is discarded.", { context: { station: this.rawStation.station_sn } }));
1862
+ return;
1863
+ }
1864
+ payloadStart = 151;
1865
+ }
1866
+ let video_data;
1867
+ if (videoMetaData.aesKey !== "") {
1868
+ const encrypted_data = message.data.subarray(payloadStart, payloadStart + 128);
1869
+ const unencrypted_data = message.data.subarray(payloadStart + 128, payloadStart + videoMetaData.videoDataLength);
1870
+ video_data = Buffer.concat([(0, utils_1.decryptAESData)(videoMetaData.aesKey, encrypted_data), unencrypted_data]);
1871
+ }
1872
+ else {
1873
+ video_data = message.data.subarray(payloadStart, payloadStart + videoMetaData.videoDataLength);
1874
+ }
1875
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME`, {
1876
+ stationSN: this.rawStation.station_sn,
1877
+ dataSize: message.data.length,
1878
+ metadata: videoMetaData,
1879
+ videoDataSize: video_data.length,
1880
+ });
1881
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoFPS = videoMetaData.videoFPS;
1882
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoHeight = videoMetaData.videoHeight;
1883
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoWidth = videoMetaData.videoWidth;
1884
+ if (!this.currentMessageState[message.dataType].p2pStreamFirstVideoDataReceived) {
1885
+ if (this.rawStation.station_sn.startsWith("T8410") ||
1886
+ this.rawStation.station_sn.startsWith("T8400") ||
1887
+ this.rawStation.station_sn.startsWith("T8401") ||
1888
+ this.rawStation.station_sn.startsWith("T8411") ||
1889
+ this.rawStation.station_sn.startsWith("T8202") ||
1890
+ this.rawStation.station_sn.startsWith("T8422") ||
1891
+ this.rawStation.station_sn.startsWith("T8424") ||
1892
+ this.rawStation.station_sn.startsWith("T8423") ||
1893
+ this.rawStation.station_sn.startsWith("T8130") ||
1894
+ this.rawStation.station_sn.startsWith("T8131") ||
1895
+ this.rawStation.station_sn.startsWith("T8420") ||
1896
+ this.rawStation.station_sn.startsWith("T8440") ||
1897
+ this.rawStation.station_sn.startsWith("T8171") ||
1898
+ this.rawStation.station_sn.startsWith("T8426") ||
1899
+ this.rawStation.station_sn.startsWith("T8441") ||
1900
+ this.rawStation.station_sn.startsWith("T8442") ||
1901
+ (0, utils_1.checkT8420)(this.rawStation.station_sn)) {
1902
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoCodec =
1903
+ videoMetaData.streamType === 1
1904
+ ? types_1.VideoCodec.H264
1905
+ : videoMetaData.streamType === 2
1906
+ ? types_1.VideoCodec.H265
1907
+ : (0, utils_1.getVideoCodec)(video_data);
1908
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME - Video codec information received from packet`, {
1909
+ stationSN: this.rawStation.station_sn,
1910
+ commandIdName: types_1.CommandType[message.commandId],
1911
+ commandId: message.commandId,
1912
+ channel: message.channel,
1913
+ metadata: videoMetaData,
1914
+ });
1915
+ }
1916
+ else if (this.isIFrame(video_data, isKeyFrame)) {
1917
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoCodec = (0, utils_1.getVideoCodec)(video_data);
1918
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME - Video codec extracted from video data`, {
1919
+ stationSN: this.rawStation.station_sn,
1920
+ commandIdName: types_1.CommandType[message.commandId],
1921
+ commandId: message.commandId,
1922
+ channel: message.channel,
1923
+ metadata: videoMetaData,
1924
+ });
1925
+ }
1926
+ else {
1927
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoCodec = (0, utils_1.getVideoCodec)(video_data);
1928
+ if (this.currentMessageState[message.dataType].p2pStreamMetadata.videoCodec === types_1.VideoCodec.UNKNOWN) {
1929
+ this.currentMessageState[message.dataType].p2pStreamMetadata.videoCodec =
1930
+ videoMetaData.streamType === 1
1931
+ ? types_1.VideoCodec.H264
1932
+ : videoMetaData.streamType === 2
1933
+ ? types_1.VideoCodec.H265
1934
+ : types_1.VideoCodec.UNKNOWN;
1935
+ if (this.currentMessageState[message.dataType].p2pStreamMetadata.videoCodec === types_1.VideoCodec.UNKNOWN) {
1936
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME - Unknown video codec`, {
1937
+ stationSN: this.rawStation.station_sn,
1938
+ commandIdName: types_1.CommandType[message.commandId],
1939
+ commandId: message.commandId,
1940
+ channel: message.channel,
1941
+ metadata: videoMetaData,
1942
+ });
1943
+ }
1944
+ else {
1945
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME - Fallback, using video codec information received from packet`, {
1946
+ stationSN: this.rawStation.station_sn,
1947
+ commandIdName: types_1.CommandType[message.commandId],
1948
+ commandId: message.commandId,
1949
+ channel: message.channel,
1950
+ metadata: videoMetaData,
1951
+ });
1952
+ }
1953
+ }
1954
+ else {
1955
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME - Fallback, video codec extracted from video data`, {
1956
+ stationSN: this.rawStation.station_sn,
1957
+ commandIdName: types_1.CommandType[message.commandId],
1958
+ commandId: message.commandId,
1959
+ channel: message.channel,
1960
+ metadata: videoMetaData,
1961
+ });
1962
+ }
1963
+ }
1964
+ this.currentMessageState[message.dataType].p2pStreamFirstVideoDataReceived = true;
1965
+ if (!this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived) {
1966
+ this.currentMessageState[message.dataType].waitForAudioData = setTimeout(() => {
1967
+ this.currentMessageState[message.dataType].waitForAudioData = undefined;
1968
+ this.currentMessageState[message.dataType].p2pStreamMetadata.audioCodec = types_1.AudioCodec.NONE;
1969
+ this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived = true;
1970
+ if (this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived &&
1971
+ this.currentMessageState[message.dataType].p2pStreamFirstVideoDataReceived &&
1972
+ this.currentMessageState[message.dataType].p2pStreamNotStarted) {
1973
+ this.emitStreamStartEvent(message.dataType);
1974
+ }
1975
+ }, this.AUDIO_CODEC_ANALYZE_TIMEOUT);
1976
+ }
1977
+ }
1978
+ if (this.currentMessageState[message.dataType].p2pStreamNotStarted) {
1979
+ if (this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived &&
1980
+ this.currentMessageState[message.dataType].p2pStreamFirstVideoDataReceived) {
1981
+ this.emitStreamStartEvent(message.dataType);
1982
+ }
1983
+ }
1984
+ if (message.dataType === types_1.P2PDataType.VIDEO) {
1985
+ if ((0, utils_1.findStartCode)(video_data)) {
1986
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME: startcode found`, {
1987
+ stationSN: this.rawStation.station_sn,
1988
+ isKeyFrame: isKeyFrame,
1989
+ preFrameVideoDataLength: this.currentMessageState[message.dataType].preFrameVideoData.length,
1990
+ });
1991
+ if (!this.currentMessageState[message.dataType].receivedFirstIFrame)
1992
+ this.currentMessageState[message.dataType].receivedFirstIFrame = this.isIFrame(video_data, isKeyFrame);
1993
+ if (this.currentMessageState[message.dataType].receivedFirstIFrame) {
1994
+ if (this.currentMessageState[message.dataType].preFrameVideoData.length > this.MAX_VIDEO_PACKET_BYTES)
1995
+ this.currentMessageState[message.dataType].preFrameVideoData = Buffer.from([]);
1996
+ if (this.currentMessageState[message.dataType].preFrameVideoData.length > 0) {
1997
+ this.currentMessageState[message.dataType].videoStream?.push(this.currentMessageState[message.dataType].preFrameVideoData);
1998
+ }
1999
+ this.currentMessageState[message.dataType].preFrameVideoData = Buffer.from(video_data);
2000
+ }
2001
+ else {
2002
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME: Skipping because first frame is not an I frame.`, { stationSN: this.rawStation.station_sn });
2003
+ }
2004
+ }
2005
+ else {
2006
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_VIDEO_FRAME: No startcode found`, {
2007
+ stationSN: this.rawStation.station_sn,
2008
+ isKeyFrame: isKeyFrame,
2009
+ preFrameVideoDataLength: this.currentMessageState[message.dataType].preFrameVideoData.length,
2010
+ });
2011
+ if (this.currentMessageState[message.dataType].preFrameVideoData.length > 0) {
2012
+ this.currentMessageState[message.dataType].preFrameVideoData = Buffer.concat([
2013
+ this.currentMessageState[message.dataType].preFrameVideoData,
2014
+ video_data,
2015
+ ]);
2016
+ }
2017
+ }
2018
+ }
2019
+ else if (message.dataType === types_1.P2PDataType.BINARY) {
2020
+ this.currentMessageState[message.dataType].videoStream?.push(video_data);
2021
+ }
2022
+ break;
2023
+ case types_1.CommandType.CMD_AUDIO_FRAME:
2024
+ this.waitForStreamData(message.dataType, true);
2025
+ const audioMetaData = {
2026
+ audioType: types_1.AudioCodec.NONE,
2027
+ audioSeqNo: 0,
2028
+ audioTimestamp: 0,
2029
+ audioDataLength: 0,
2030
+ };
2031
+ audioMetaData.audioDataLength = message.data.subarray(0, 4).readUInt32LE();
2032
+ audioMetaData.audioType = message.data.subarray(5, 6).readUInt8();
2033
+ audioMetaData.audioSeqNo = message.data.subarray(6, 8).readUInt16LE();
2034
+ audioMetaData.audioTimestamp = message.data.subarray(8, 14).readUIntLE(0, 6);
2035
+ const audio_data = Buffer.from(message.data.subarray(16));
2036
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_AUDIO_FRAME`, {
2037
+ stationSN: this.rawStation.station_sn,
2038
+ dataSize: message.data.length,
2039
+ metadata: audioMetaData,
2040
+ audioDataSize: audio_data.length,
2041
+ });
2042
+ if (this.currentMessageState[message.dataType].waitForAudioData !== undefined) {
2043
+ clearTimeout(this.currentMessageState[message.dataType].waitForAudioData);
2044
+ }
2045
+ if (!this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived) {
2046
+ this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived = true;
2047
+ this.currentMessageState[message.dataType].p2pStreamMetadata.audioCodec =
2048
+ audioMetaData.audioType === 0
2049
+ ? types_1.AudioCodec.AAC
2050
+ : audioMetaData.audioType === 1
2051
+ ? types_1.AudioCodec.AAC_LC
2052
+ : audioMetaData.audioType === 7
2053
+ ? types_1.AudioCodec.AAC_ELD
2054
+ : types_1.AudioCodec.UNKNOWN;
2055
+ }
2056
+ if (this.currentMessageState[message.dataType].p2pStreamNotStarted) {
2057
+ if (this.currentMessageState[message.dataType].p2pStreamFirstAudioDataReceived &&
2058
+ this.currentMessageState[message.dataType].p2pStreamFirstVideoDataReceived) {
2059
+ this.emitStreamStartEvent(message.dataType);
2060
+ }
2061
+ }
2062
+ this.currentMessageState[message.dataType].audioStream?.push(audio_data);
2063
+ break;
2064
+ default:
2065
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented message`, {
2066
+ stationSN: this.rawStation.station_sn,
2067
+ commandIdName: types_1.CommandType[message.commandId],
2068
+ commandId: message.commandId,
2069
+ channel: message.channel,
2070
+ data: message.data.toString("hex"),
2071
+ });
2072
+ break;
2073
+ }
2074
+ }
2075
+ else {
2076
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Invalid stream data, dropping complete stream`, {
2077
+ stationSN: this.rawStation.station_sn,
2078
+ commandIdName: types_1.CommandType[message.commandId],
2079
+ commandId: message.commandId,
2080
+ channel: message.channel,
2081
+ data: message.data.toString("hex"),
2082
+ });
2083
+ }
2084
+ }
2085
+ handleDataControl(message) {
2086
+ try {
2087
+ let data = message.data;
2088
+ if (message.signCode > 0) {
2089
+ //data = decryptP2PData(message.data, this.p2pKey!);
2090
+ try {
2091
+ data = (0, utils_1.decryptP2PData)(message.data, Buffer.from((0, utils_1.getP2PCommandEncryptionKey)(this.rawStation.station_sn, this.rawStation.p2p_did)));
2092
+ }
2093
+ catch (err) {
2094
+ const error = (0, error_1.ensureError)(err);
2095
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Decrypt Error`, {
2096
+ error: (0, utils_3.getError)(error),
2097
+ stationSN: this.rawStation.station_sn,
2098
+ message: {
2099
+ seqNo: message.seqNo,
2100
+ channel: message.channel,
2101
+ commandType: types_1.CommandType[message.commandId],
2102
+ signCode: message.signCode,
2103
+ type: message.type,
2104
+ dataType: types_1.P2PDataType[message.dataType],
2105
+ data: message.data.toString("hex"),
2106
+ },
2107
+ });
2108
+ }
2109
+ }
2110
+ logging_1.rootP2PLogger.trace(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Received data`, {
2111
+ stationSN: this.rawStation.station_sn,
2112
+ commandIdName: types_1.CommandType[message.commandId],
2113
+ commandId: message.commandId,
2114
+ data: message.data.toString("hex"),
2115
+ seqNumber: this.seqNumber,
2116
+ p2pDataSeqNumber: this.p2pDataSeqNumber,
2117
+ offsetDataSeqNumber: this.offsetDataSeqNumber,
2118
+ });
2119
+ switch (message.commandId) {
2120
+ case types_1.CommandType.CMD_GET_ALARM_MODE:
2121
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Alarm mode changed to: ${types_2.AlarmMode[data.readUIntBE(0, 1)]}`, { stationSN: this.rawStation.station_sn });
2122
+ this.emit("alarm mode", data.readUIntBE(0, 1));
2123
+ break;
2124
+ case types_1.CommandType.CMD_CAMERA_INFO:
2125
+ try {
2126
+ const cameraData = (0, utils_1.getNullTerminatedString)(data, "utf8");
2127
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Camera info`, {
2128
+ stationSN: this.rawStation.station_sn,
2129
+ cameraInfo: cameraData,
2130
+ });
2131
+ this.emit("camera info", (0, utils_3.parseJSON)(cameraData, logging_1.rootP2PLogger));
2132
+ }
2133
+ catch (err) {
2134
+ const error = (0, error_1.ensureError)(err);
2135
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Camera info - Error`, {
2136
+ error: (0, utils_3.getError)(error),
2137
+ stationSN: this.rawStation.station_sn,
2138
+ message: {
2139
+ seqNo: message.seqNo,
2140
+ channel: message.channel,
2141
+ commandType: types_1.CommandType[message.commandId],
2142
+ signCode: message.signCode,
2143
+ type: message.type,
2144
+ dataType: types_1.P2PDataType[message.dataType],
2145
+ data: message.data.toString("hex"),
2146
+ },
2147
+ });
2148
+ }
2149
+ break;
2150
+ case types_1.CommandType.CMD_CONVERT_MP4_OK:
2151
+ const totalBytes = data.subarray(1).readUInt32LE();
2152
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_CONVERT_MP4_OK`, {
2153
+ stationSN: this.rawStation.station_sn,
2154
+ channel: message.channel,
2155
+ totalBytes: totalBytes,
2156
+ });
2157
+ this.downloadTotalBytes = totalBytes;
2158
+ this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreaming = true;
2159
+ // HB3 sends channel 255 (broadcast) which is not a valid device channel
2160
+ if (message.channel !== 255) {
2161
+ this.currentMessageState[types_1.P2PDataType.BINARY].p2pStreamChannel = message.channel;
2162
+ }
2163
+ break;
2164
+ case types_1.CommandType.CMD_WIFI_CONFIG:
2165
+ const rssi = data.readInt32LE();
2166
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_WIFI_CONFIG`, {
2167
+ stationSN: this.rawStation.station_sn,
2168
+ channel: message.channel,
2169
+ rssi: rssi,
2170
+ });
2171
+ this.emit("wifi rssi", message.channel, rssi);
2172
+ break;
2173
+ case types_1.CommandType.CMD_DOWNLOAD_FINISH:
2174
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DOWNLOAD_FINISH`, {
2175
+ stationSN: this.rawStation.station_sn,
2176
+ channel: message.channel,
2177
+ });
2178
+ this.endStream(types_1.P2PDataType.BINARY);
2179
+ break;
2180
+ case types_1.CommandType.CMD_DOORBELL_NOTIFY_PAYLOAD:
2181
+ try {
2182
+ const str = (0, utils_1.getNullTerminatedString)(data, "utf8");
2183
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DOORBELL_NOTIFY_PAYLOAD`, {
2184
+ stationSN: this.rawStation.station_sn,
2185
+ payload: str,
2186
+ });
2187
+ //TODO: Finish implementation, emit an event...
2188
+ //VDBStreamInfo (1005) and VoltageEvent (1015)
2189
+ //this.emit("", parseJSON(str, this.log) as xy);
2190
+ }
2191
+ catch (err) {
2192
+ const error = (0, error_1.ensureError)(err);
2193
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DOORBELL_NOTIFY_PAYLOAD - Error`, {
2194
+ error: (0, utils_3.getError)(error),
2195
+ stationSN: this.rawStation.station_sn,
2196
+ message: {
2197
+ seqNo: message.seqNo,
2198
+ channel: message.channel,
2199
+ commandType: types_1.CommandType[message.commandId],
2200
+ signCode: message.signCode,
2201
+ type: message.type,
2202
+ dataType: types_1.P2PDataType[message.dataType],
2203
+ data: message.data.toString("hex"),
2204
+ },
2205
+ });
2206
+ }
2207
+ break;
2208
+ case types_1.CommandType.CMD_NAS_SWITCH:
2209
+ try {
2210
+ const str = (0, utils_1.getNullTerminatedString)(data, "utf8");
2211
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NAS_SWITCH`, {
2212
+ stationSN: this.rawStation.station_sn,
2213
+ payload: str,
2214
+ });
2215
+ this.emit("rtsp url", message.channel, str);
2216
+ }
2217
+ catch (err) {
2218
+ const error = (0, error_1.ensureError)(err);
2219
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NAS_SWITCH - Error`, {
2220
+ error: (0, utils_3.getError)(error),
2221
+ stationSN: this.rawStation.station_sn,
2222
+ message: {
2223
+ seqNo: message.seqNo,
2224
+ channel: message.channel,
2225
+ commandType: types_1.CommandType[message.commandId],
2226
+ signCode: message.signCode,
2227
+ type: message.type,
2228
+ dataType: types_1.P2PDataType[message.dataType],
2229
+ data: message.data.toString("hex"),
2230
+ },
2231
+ });
2232
+ }
2233
+ break;
2234
+ case types_1.CommandType.SUB1G_REP_UNPLUG_POWER_LINE:
2235
+ try {
2236
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - SUB1G_REP_UNPLUG_POWER_LINE`, {
2237
+ stationSN: this.rawStation.station_sn,
2238
+ payload: data.toString(),
2239
+ });
2240
+ const chargeType = data.subarray(0, 4).readUInt32LE();
2241
+ const batteryLevel = data.subarray(4, 8).readUInt32LE();
2242
+ this.emit("charging state", message.channel, chargeType, batteryLevel);
2243
+ }
2244
+ catch (err) {
2245
+ const error = (0, error_1.ensureError)(err);
2246
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - SUB1G_REP_UNPLUG_POWER_LINE - Error`, {
2247
+ error: (0, utils_3.getError)(error),
2248
+ stationSN: this.rawStation.station_sn,
2249
+ message: {
2250
+ seqNo: message.seqNo,
2251
+ channel: message.channel,
2252
+ commandType: types_1.CommandType[message.commandId],
2253
+ signCode: message.signCode,
2254
+ type: message.type,
2255
+ dataType: types_1.P2PDataType[message.dataType],
2256
+ data: message.data.toString("hex"),
2257
+ },
2258
+ });
2259
+ }
2260
+ break;
2261
+ case types_1.CommandType.SUB1G_REP_RUNTIME_STATE:
2262
+ try {
2263
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - SUB1G_REP_RUNTIME_STATE`, {
2264
+ stationSN: this.rawStation.station_sn,
2265
+ payload: data.toString(),
2266
+ });
2267
+ const batteryLevel = data.subarray(0, 4).readUInt32LE();
2268
+ const temperature = data.subarray(4, 8).readUInt32LE();
2269
+ this.emit("runtime state", message.channel, batteryLevel, temperature);
2270
+ }
2271
+ catch (err) {
2272
+ const error = (0, error_1.ensureError)(err);
2273
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - SUB1G_REP_RUNTIME_STATE - Error`, {
2274
+ error: (0, utils_3.getError)(error),
2275
+ stationSN: this.rawStation.station_sn,
2276
+ message: {
2277
+ seqNo: message.seqNo,
2278
+ channel: message.channel,
2279
+ commandType: types_1.CommandType[message.commandId],
2280
+ signCode: message.signCode,
2281
+ type: message.type,
2282
+ dataType: types_1.P2PDataType[message.dataType],
2283
+ data: message.data.toString("hex"),
2284
+ },
2285
+ });
2286
+ }
2287
+ break;
2288
+ case types_1.CommandType.CMD_SET_FLOODLIGHT_MANUAL_SWITCH:
2289
+ try {
2290
+ const enabled = data.readUIntBE(0, 1) === 1 ? true : false;
2291
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_SET_FLOODLIGHT_MANUAL_SWITCH`, {
2292
+ stationSN: this.rawStation.station_sn,
2293
+ enabled: enabled,
2294
+ payload: data.toString(),
2295
+ });
2296
+ this.emit("floodlight manual switch", message.channel, enabled);
2297
+ }
2298
+ catch (err) {
2299
+ const error = (0, error_1.ensureError)(err);
2300
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_SET_FLOODLIGHT_MANUAL_SWITCH - Error`, {
2301
+ error: (0, utils_3.getError)(error),
2302
+ stationSN: this.rawStation.station_sn,
2303
+ message: {
2304
+ seqNo: message.seqNo,
2305
+ channel: message.channel,
2306
+ commandType: types_1.CommandType[message.commandId],
2307
+ signCode: message.signCode,
2308
+ type: message.type,
2309
+ dataType: types_1.P2PDataType[message.dataType],
2310
+ data: message.data.toString("hex"),
2311
+ },
2312
+ });
2313
+ }
2314
+ break;
2315
+ case types_1.CommandType.CMD_GET_DEVICE_PING:
2316
+ try {
2317
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GET_DEVICE_PING`, {
2318
+ stationSN: this.rawStation.station_sn,
2319
+ payload: data.toString(),
2320
+ });
2321
+ this.sendCommandDevicePing(message.channel);
2322
+ }
2323
+ catch (err) {
2324
+ const error = (0, error_1.ensureError)(err);
2325
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GET_DEVICE_PING - Error`, {
2326
+ error: (0, utils_3.getError)(error),
2327
+ stationSN: this.rawStation.station_sn,
2328
+ message: {
2329
+ seqNo: message.seqNo,
2330
+ channel: message.channel,
2331
+ commandType: types_1.CommandType[message.commandId],
2332
+ signCode: message.signCode,
2333
+ type: message.type,
2334
+ dataType: types_1.P2PDataType[message.dataType],
2335
+ data: message.data.toString("hex"),
2336
+ },
2337
+ });
2338
+ }
2339
+ break;
2340
+ case types_1.CommandType.CMD_NOTIFY_PAYLOAD:
2341
+ try {
2342
+ logging_1.rootP2PLogger.debug(`Station Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD`, {
2343
+ stationSN: this.rawStation.station_sn,
2344
+ payload: data.toString(),
2345
+ });
2346
+ const json = (0, utils_3.parseJSON)((0, utils_1.getNullTerminatedString)(data, "utf8"), logging_1.rootP2PLogger);
2347
+ if (json !== undefined) {
2348
+ if (device_1.Device.isLockWifi(this.rawStation.device_type, this.rawStation.station_sn)) {
2349
+ //TODO: Implement notification payload or T8520
2350
+ if (json.cmd === types_1.CommandType.P2P_ADD_PW ||
2351
+ json.cmd === types_1.CommandType.P2P_QUERY_PW ||
2352
+ json.cmd === types_1.CommandType.P2P_GET_LOCK_PARAM ||
2353
+ json.cmd === types_1.CommandType.P2P_GET_USER_AND_PW_ID) {
2354
+ // encrypted data
2355
+ //TODO: Handle decryption of encrypted Data (AES) - For decryption use the cached aeskey used for sending the command!
2356
+ const aesKey = this.getLockAESKey(json.cmd);
2357
+ if (aesKey !== undefined) {
2358
+ const decryptedPayload = (0, utils_1.decryptPayloadData)(Buffer.from(json.payload, "base64"), Buffer.from(aesKey, "hex"), Buffer.from((0, utils_1.getLockVectorBytes)(this.rawStation.station_sn), "hex")).toString();
2359
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Lock - Received`, {
2360
+ stationSN: this.rawStation.station_sn,
2361
+ commandIdName: types_1.CommandType[json.cmd],
2362
+ commandId: json.cmd,
2363
+ decryptedPayload: decryptedPayload,
2364
+ aesKey: aesKey,
2365
+ });
2366
+ switch (json.cmd) {
2367
+ case types_1.CommandType.P2P_ADD_PW:
2368
+ // decryptedPayload: {"code":0,"passwordId":"002C"}
2369
+ break;
2370
+ }
2371
+ }
2372
+ }
2373
+ else if (json.cmd === types_1.CommandType.P2P_QUERY_STATUS_IN_LOCK) {
2374
+ // Example: {"code":0,"slBattery":"82","slState":"4","trigger":2}
2375
+ const payload = json.payload;
2376
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_BATTERY_LEVEL, payload.slBattery);
2377
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_STATUS, payload.slState);
2378
+ }
2379
+ else {
2380
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Not implemented`, {
2381
+ stationSN: this.rawStation.station_sn,
2382
+ commandIdName: types_1.CommandType[json.cmd],
2383
+ commandId: json.cmd,
2384
+ message: data.toString(),
2385
+ });
2386
+ }
2387
+ }
2388
+ else if (json.cmd === types_1.CommandType.P2P_QUERY_STATUS_IN_LOCK) {
2389
+ // Example: {"code":0,"slBattery":"99","slState":"4","slOpenDirection":"1","trigger":2}
2390
+ const payload = json.payload;
2391
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_GET_BATTERY, payload.slBattery);
2392
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_DOORLOCK_GET_STATE, payload.slState);
2393
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_NIGHT_VISION_SIDE, payload.slOpenDirection);
2394
+ }
2395
+ else if (json.cmd === types_1.CommandType.CMD_DOORLOCK_P2P_SEQ) {
2396
+ if (device_1.Device.isLockWifi(this.rawStation.devices[0]?.device_type, this.rawStation.devices[0]?.device_sn) ||
2397
+ device_1.Device.isLockWifiNoFinger(this.rawStation.devices[0]?.device_type)) {
2398
+ const payload = json.payload;
2399
+ if (payload.seq_num !== undefined) {
2400
+ this.lockSeqNumber = payload.seq_num;
2401
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Lock sequence number`, { stationSN: this.rawStation.station_sn, lockSeqNumber: this.lockSeqNumber, payload: payload });
2402
+ }
2403
+ if (payload.lock_cmd > 0) {
2404
+ this.emit("sequence error", message.channel, payload.lock_cmd, payload.seq_num, payload.stationSn);
2405
+ }
2406
+ }
2407
+ else {
2408
+ const payload = json.payload;
2409
+ if (payload.seq_num !== undefined) {
2410
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Lock sequence number`, {
2411
+ stationSN: this.rawStation.station_sn,
2412
+ oldSequenceNumber: this.lockSeqNumber,
2413
+ newSequenceNumber: this.lockSeqNumber + 1,
2414
+ payload: payload,
2415
+ });
2416
+ this.lockSeqNumber = payload.seq_num + 1;
2417
+ }
2418
+ if (payload.lock_cmd > 0) {
2419
+ if (device_1.Device.isLockWifiR10(this.rawStation.devices[0]?.device_type) ||
2420
+ device_1.Device.isLockWifiR20(this.rawStation.devices[0]?.device_type)) {
2421
+ this.emit("sequence error", message.channel, types_1.ESLCommand[types_1.ESLBleCommand[payload.lock_cmd]], payload.seq_num, payload.dev_sn);
2422
+ }
2423
+ else if (device_1.Device.isLockWifiT8506(this.rawStation.devices[0]?.device_type) ||
2424
+ device_1.Device.isLockWifiT8502(this.rawStation.devices[0]?.device_type) ||
2425
+ device_1.Device.isLockWifiT8510P(this.rawStation.devices[0]?.device_type, this.rawStation.devices[0]?.device_sn) ||
2426
+ device_1.Device.isLockWifiT8520P(this.rawStation.devices[0]?.device_type, this.rawStation.devices[0]?.device_sn) ||
2427
+ device_1.Device.isLockWifiT85V0(this.rawStation.devices[0]?.device_type, this.rawStation.devices[0]?.device_sn) ||
2428
+ device_1.Device.isLockWifiT8531(this.rawStation.devices[0]?.device_type) ||
2429
+ device_1.Device.isLockWifiT85L0(this.rawStation.devices[0]?.device_type)) {
2430
+ this.emit("sequence error", message.channel, types_1.SmartLockCommand[payload.bus_type == types_1.SmartLockFunctionType.TYPE_2
2431
+ ? types_1.SmartLockBleCommandFunctionType2[payload.lock_cmd]
2432
+ : types_1.SmartLockBleCommandFunctionType1[payload.lock_cmd]], payload.seq_num, payload.dev_sn);
2433
+ }
2434
+ else {
2435
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Lock sequence number - Unknown device`, {
2436
+ stationSN: this.rawStation.station_sn,
2437
+ oldSequenceNumber: this.lockSeqNumber,
2438
+ newSequenceNumber: this.lockSeqNumber + 1,
2439
+ payload: payload,
2440
+ });
2441
+ }
2442
+ }
2443
+ }
2444
+ }
2445
+ else if (json.cmd === types_1.CommandType.CMD_DOORLOCK_DATA_PASS_THROUGH) {
2446
+ const payload = json.payload;
2447
+ if (this.deviceSNs[message.channel] !== undefined) {
2448
+ if (payload.lock_payload !== undefined) {
2449
+ const decoded = (0, utils_1.decodeBase64)((0, utils_1.decodeLockPayload)(Buffer.from(payload.lock_payload)));
2450
+ const key = (0, utils_1.generateBasicLockAESKey)(this.deviceSNs[message.channel].adminUserId, this.rawStation.station_sn);
2451
+ const iv = (0, utils_1.getLockVectorBytes)(this.rawStation.station_sn);
2452
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DOORLOCK_DATA_PASS_THROUGH`, {
2453
+ stationSN: this.rawStation.station_sn,
2454
+ commandIdName: types_1.CommandType[json.cmd],
2455
+ commandId: json.cmd,
2456
+ key: key,
2457
+ iv: iv,
2458
+ decoded: decoded.toString("hex"),
2459
+ });
2460
+ payload.lock_payload = (0, utils_1.decryptLockAESData)(key, iv, decoded).toString("hex");
2461
+ switch (payload.lock_cmd) {
2462
+ case types_1.ESLBleCommand.NOTIFY:
2463
+ const notifyBuffer = Buffer.from(payload.lock_payload, "hex");
2464
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_GET_BATTERY, notifyBuffer.subarray(3, 4).readInt8().toString());
2465
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_DOORLOCK_GET_STATE, notifyBuffer.subarray(6, 7).readInt8().toString());
2466
+ break;
2467
+ default:
2468
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DOORLOCK_DATA_PASS_THROUGH - Not implemented`, { stationSN: this.rawStation.station_sn, message: data.toString() });
2469
+ break;
2470
+ }
2471
+ }
2472
+ }
2473
+ }
2474
+ else if (json.cmd === types_1.CommandType.CMD_SET_PAYLOAD_LOCKV12) {
2475
+ const payload = json.payload;
2476
+ if (payload.lock_payload !== undefined) {
2477
+ const fac = ble_1.BleCommandFactory.parseLockV12(payload.lock_payload);
2478
+ if (fac.getCommandCode() !== types_1.ESLBleCommand.NOTIFY) {
2479
+ const aesKey = this.getLockAESKey(fac.getCommandCode());
2480
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Lock V12 - Received`, { stationSN: this.rawStation.station_sn, fac: fac.toString(), aesKey: aesKey });
2481
+ let data = fac.getData();
2482
+ try {
2483
+ if (aesKey !== undefined) {
2484
+ data = (0, utils_1.decryptPayloadData)(data, Buffer.from(aesKey, "hex"), Buffer.from((0, utils_1.getLockVectorBytes)(this.rawStation.station_sn), "hex"));
2485
+ }
2486
+ const returnCode = data.readInt8(0);
2487
+ const commandType = Number.parseInt(types_1.ESLCommand[types_1.ESLBleCommand[fac.getCommandCode()]]);
2488
+ const customData = {
2489
+ ...this.customDataStaging[commandType],
2490
+ };
2491
+ if (this.customDataStaging[commandType]) {
2492
+ const result = {
2493
+ channel: customData.channel,
2494
+ command_type: commandType,
2495
+ return_code: returnCode,
2496
+ customData: customData.customData,
2497
+ };
2498
+ this.emit("secondary command", result);
2499
+ delete this.customDataStaging[commandType];
2500
+ }
2501
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Lock V12 return code: ${returnCode}`, {
2502
+ stationSN: this.rawStation.station_sn,
2503
+ commandIdName: types_1.CommandType[json.cmd],
2504
+ commandId: json.cmd,
2505
+ decoded: data.toString("hex"),
2506
+ bleCommandCode: types_1.ESLBleCommand[fac.getCommandCode()],
2507
+ returnCode: returnCode,
2508
+ channel: customData?.channel,
2509
+ customData: customData?.customData,
2510
+ });
2511
+ const parsePayload = new utils_2.ParsePayload(data.subarray(1));
2512
+ switch (fac.getCommandCode()) {
2513
+ case types_1.ESLBleCommand.QUERY_STATUS_IN_LOCK:
2514
+ case types_1.ESLBleCommand.NOTIFY:
2515
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_BATTERY_LEVEL, parsePayload.readInt8(ble_1.BleParameterIndex.ONE).toString());
2516
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_STATUS, parsePayload.readInt8(ble_1.BleParameterIndex.TWO).toString());
2517
+ break;
2518
+ case types_1.ESLBleCommand.GET_LOCK_PARAM:
2519
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_LOCK_SOUND, parsePayload.readInt8(ble_1.BleParameterIndex.ONE).toString());
2520
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK, parsePayload.readInt8(ble_1.BleParameterIndex.TWO).toString());
2521
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_TIMER, parsePayload.readUint16LE(ble_1.BleParameterIndex.THREE).toString());
2522
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_SCHEDULE, parsePayload.readInt8(ble_1.BleParameterIndex.FOUR).toString());
2523
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_SCHEDULE_STARTTIME, parsePayload.readStringHex(ble_1.BleParameterIndex.FIVE));
2524
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_SCHEDULE_ENDTIME, parsePayload.readStringHex(ble_1.BleParameterIndex.SIX));
2525
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_ONE_TOUCH_LOCK, parsePayload.readInt8(ble_1.BleParameterIndex.SEVEN).toString());
2526
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_WRONG_TRY_PROTECT, parsePayload.readInt8(ble_1.BleParameterIndex.EIGHT).toString());
2527
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_WRONG_TRY_LOCKDOWN, parsePayload.readUint16LE(ble_1.BleParameterIndex.NINE).toString());
2528
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_WRONG_TRY_ATTEMPTS, parsePayload.readInt8(ble_1.BleParameterIndex.TEN).toString());
2529
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_SCRAMBLE_PASSCODE, parsePayload.readInt8(ble_1.BleParameterIndex.ELEVEN).toString());
2530
+ //this.emit("parameter", message.channel, CommandType.CMD_SMARTLOCK_LOG, parsePayload.readInt8(BleParameterIndex.TWELVE).toString());
2531
+ //this.emit("parameter", message.channel, CommandType.CMD_SMARTLOCK_WIFI_STATUS, parsePayload.readInt8(BleParameterIndex.THIRTEEN).toString());
2532
+ break;
2533
+ case types_1.ESLBleCommand.ADD_PW:
2534
+ if (customData &&
2535
+ customData.customData &&
2536
+ customData.customData.command &&
2537
+ customData.customData.command.name === http_1.CommandName.DeviceAddUser) {
2538
+ this.api.updateUserPassword(customData.customData.command.value?.deviceSN, customData.customData.command.value?.shortUserId, parsePayload.readStringHex(ble_1.BleParameterIndex.ONE), customData.customData.command.value?.schedule);
2539
+ }
2540
+ break;
2541
+ default:
2542
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Not implemented`, {
2543
+ stationSN: this.rawStation.station_sn,
2544
+ fac: fac.toString(),
2545
+ returnCode: returnCode,
2546
+ channel: customData?.channel,
2547
+ customData: customData?.customData,
2548
+ });
2549
+ break;
2550
+ }
2551
+ }
2552
+ catch (err) {
2553
+ const error = (0, error_1.ensureError)(err);
2554
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Lock V12 unable to decrypt (maybe event from other lock connection)`, {
2555
+ error: (0, utils_3.getError)(error),
2556
+ stationSN: this.rawStation.station_sn,
2557
+ commandIdName: types_1.CommandType[json.cmd],
2558
+ commandId: json.cmd,
2559
+ payload: payload,
2560
+ });
2561
+ }
2562
+ this._clearSecondaryCommandTimeout();
2563
+ this.sendQueuedMessage();
2564
+ }
2565
+ else {
2566
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Lock V12 - Received notify`, { stationSN: this.rawStation.station_sn, fac: fac.toString() });
2567
+ }
2568
+ }
2569
+ else {
2570
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Lock V12 - Unexpected response`, {
2571
+ stationSN: this.rawStation.station_sn,
2572
+ commandIdName: types_1.CommandType[json.cmd],
2573
+ commandId: json.cmd,
2574
+ message: data.toString(),
2575
+ });
2576
+ }
2577
+ }
2578
+ else if (json.cmd === types_1.CommandType.CMD_TRANSFER_PAYLOAD) {
2579
+ const payload = json.payload;
2580
+ if (payload.lock_payload !== undefined) {
2581
+ try {
2582
+ const fac = ble_1.BleCommandFactory.parseSmartLock(payload.lock_payload);
2583
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Received`, { stationSN: this.rawStation.station_sn, fac: fac.toString() });
2584
+ if (!fac.isPartial()) {
2585
+ let data = fac.getData();
2586
+ if (data) {
2587
+ if (fac.isEncrypted()) {
2588
+ const aesKey = (0, utils_1.generateSmartLockAESKey)(this.rawStation.member.admin_user_id, Number.parseInt(payload.time, 16));
2589
+ const iv = (0, utils_1.getLockVectorBytes)(payload.dev_sn);
2590
+ if (aesKey !== undefined) {
2591
+ data = (0, utils_1.decryptPayloadData)(data, aesKey, Buffer.from(iv, "hex"));
2592
+ }
2593
+ }
2594
+ if (data.length > 0 && fac.getDataType() !== undefined) {
2595
+ const returnCode = data.readInt8(0);
2596
+ const functionTypeCommand = fac.getDataType() === types_1.SmartLockFunctionType.TYPE_2
2597
+ ? types_1.SmartLockBleCommandFunctionType2
2598
+ : types_1.SmartLockBleCommandFunctionType1;
2599
+ const commandType = Number.parseInt(types_1.SmartLockCommand[functionTypeCommand[fac.getCommandCode()]]);
2600
+ const customData = {
2601
+ ...this.customDataStaging[commandType],
2602
+ };
2603
+ if (this.customDataStaging[commandType]) {
2604
+ const result = {
2605
+ channel: customData.channel,
2606
+ command_type: commandType,
2607
+ return_code: returnCode,
2608
+ customData: customData.customData,
2609
+ };
2610
+ this.emit("secondary command", result);
2611
+ delete this.customDataStaging[commandType];
2612
+ }
2613
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock return code: ${returnCode}`, {
2614
+ stationSN: this.rawStation.station_sn,
2615
+ commandIdName: types_1.CommandType[json.cmd],
2616
+ commandId: json.cmd,
2617
+ decoded: data.toString("hex"),
2618
+ bleCommandCode: functionTypeCommand[fac.getCommandCode()],
2619
+ returnCode: returnCode,
2620
+ channel: message.channel,
2621
+ });
2622
+ const parsePayload = new utils_2.ParsePayload(data.subarray(1));
2623
+ if (fac.getDataType() === types_1.SmartLockFunctionType.TYPE_2) {
2624
+ switch (fac.getCommandCode()) {
2625
+ case types_1.SmartLockBleCommandFunctionType2.QUERY_STATUS_IN_LOCK:
2626
+ case types_1.SmartLockBleCommandFunctionType2.NOTIFY:
2627
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_BATTERY_LEVEL, parsePayload.readInt8(ble_1.BleParameterIndex.ONE).toString());
2628
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_STATUS, parsePayload.readInt8(ble_1.BleParameterIndex.TWO).toString());
2629
+ break;
2630
+ case types_1.SmartLockBleCommandFunctionType2.GET_LOCK_PARAM:
2631
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK, parsePayload.readInt8(ble_1.BleParameterIndex.ONE).toString());
2632
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_TIMER, parsePayload.readUint16LE(ble_1.BleParameterIndex.TWO).toString());
2633
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_SCHEDULE, parsePayload.readInt8(ble_1.BleParameterIndex.THREE).toString());
2634
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_SCHEDULE_STARTTIME, parsePayload.readStringHex(ble_1.BleParameterIndex.FOUR));
2635
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_AUTO_LOCK_SCHEDULE_ENDTIME, parsePayload.readStringHex(ble_1.BleParameterIndex.FIVE));
2636
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_ONE_TOUCH_LOCK, parsePayload.readInt8(ble_1.BleParameterIndex.SIX).toString());
2637
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_SCRAMBLE_PASSCODE, parsePayload.readInt8(ble_1.BleParameterIndex.SEVEN).toString());
2638
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_WRONG_TRY_PROTECT, parsePayload.readInt8(ble_1.BleParameterIndex.EIGHT).toString());
2639
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_WRONG_TRY_ATTEMPTS, parsePayload.readInt8(ble_1.BleParameterIndex.NINE).toString());
2640
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_WRONG_TRY_LOCKDOWN, parsePayload.readUint16LE(ble_1.BleParameterIndex.TEN).toString());
2641
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_LOCK_SOUND, parsePayload.readInt8(ble_1.BleParameterIndex.ELEVEN).toString());
2642
+ //this.emit("parameter", message.channel, CommandType.CMD_SMARTLOCK_WIFI_STATUS, parsePayload.readInt8(BleParameterIndex.TWELVE).toString());
2643
+ //this.emit("parameter", message.channel, CommandType.CMD_SMARTLOCK_LOG, parsePayload.readInt8(BleParameterIndex.THIRTEEN).toString());
2644
+ break;
2645
+ case types_1.SmartLockBleCommandFunctionType2.ADD_PW:
2646
+ if (customData &&
2647
+ customData.customData &&
2648
+ customData.customData.command &&
2649
+ customData.customData.command.name === http_1.CommandName.DeviceAddUser) {
2650
+ this.api.updateUserPassword(customData.customData.command.value?.deviceSN, customData.customData.command.value?.shortUserId, parsePayload.readStringHex(ble_1.BleParameterIndex.ONE), customData.customData.command.value?.schedule);
2651
+ }
2652
+ break;
2653
+ case types_1.SmartLockBleCommandFunctionType2.ON_OFF_LOCK:
2654
+ // Lock/unlock command response received
2655
+ // The actual lock state will be updated via push notification or next status query
2656
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - ON_OFF_LOCK response`, {
2657
+ stationSN: this.rawStation.station_sn,
2658
+ returnCode: returnCode,
2659
+ channel: message.channel,
2660
+ });
2661
+ break;
2662
+ default:
2663
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Not implemented`, {
2664
+ stationSN: this.rawStation.station_sn,
2665
+ fac: fac.toString(),
2666
+ returnCode: returnCode,
2667
+ channel: customData?.channel,
2668
+ customData: customData?.customData,
2669
+ });
2670
+ break;
2671
+ }
2672
+ }
2673
+ else {
2674
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Not implemented`, {
2675
+ stationSN: this.rawStation.station_sn,
2676
+ fac: fac.toString(),
2677
+ returnCode: returnCode,
2678
+ channel: customData?.channel,
2679
+ customData: customData?.customData,
2680
+ });
2681
+ }
2682
+ }
2683
+ else {
2684
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Unsupported data length or missint function type`, { stationSN: this.rawStation.station_sn, fac: fac.toString() });
2685
+ }
2686
+ }
2687
+ else {
2688
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - No data`, { stationSN: this.rawStation.station_sn, fac: fac.toString() });
2689
+ }
2690
+ }
2691
+ else {
2692
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Partial data`, { stationSN: this.rawStation.station_sn, fac: fac.toString() });
2693
+ }
2694
+ }
2695
+ catch (err) {
2696
+ const error = (0, error_1.ensureError)(err);
2697
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock Error`, {
2698
+ error: (0, utils_3.getError)(error),
2699
+ stationSN: this.rawStation.station_sn,
2700
+ commandIdName: types_1.CommandType[json.cmd],
2701
+ commandId: json.cmd,
2702
+ payload: payload,
2703
+ });
2704
+ }
2705
+ this._clearSecondaryCommandTimeout();
2706
+ this.sendQueuedMessage();
2707
+ }
2708
+ else {
2709
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Smart Lock - Unexpected response`, {
2710
+ stationSN: this.rawStation.station_sn,
2711
+ commandIdName: types_1.CommandType[json.cmd],
2712
+ commandId: json.cmd,
2713
+ message: data.toString(),
2714
+ });
2715
+ }
2716
+ }
2717
+ else if (device_1.Device.isSmartSafe(this.rawStation.device_type)) {
2718
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD SmartSafe`, {
2719
+ stationSN: this.rawStation.station_sn,
2720
+ commandIdName: types_1.CommandType[json.cmd],
2721
+ commandId: json.cmd,
2722
+ });
2723
+ switch (json.cmd) {
2724
+ case types_1.CommandType.CMD_SMARTSAFE_SETTINGS: {
2725
+ const payload = json.payload;
2726
+ try {
2727
+ const data = (0, utils_1.decodeSmartSafeData)(this.rawStation.station_sn, Buffer.from(payload.data, "hex"));
2728
+ const returnCode = data.data.readInt8(0);
2729
+ const customData = {
2730
+ ...this.customDataStaging[payload.prj_id],
2731
+ };
2732
+ if (this.customDataStaging[payload.prj_id]) {
2733
+ const result = {
2734
+ channel: customData.channel,
2735
+ command_type: payload.prj_id,
2736
+ return_code: returnCode,
2737
+ customData: customData.customData,
2738
+ };
2739
+ this.emit("secondary command", result);
2740
+ delete this.customDataStaging[payload.prj_id];
2741
+ }
2742
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD SmartSafe return code: ${data.data.readInt8(0)}`, {
2743
+ stationSN: this.rawStation.station_sn,
2744
+ commandIdName: types_1.CommandType[json.cmd],
2745
+ commandId: json.cmd,
2746
+ decoded: data,
2747
+ commandCode: types_1.SmartSafeCommandCode[data.commandCode],
2748
+ returnCode: returnCode,
2749
+ channel: customData?.channel,
2750
+ customData: customData?.customData,
2751
+ });
2752
+ }
2753
+ catch (err) {
2754
+ const error = (0, error_1.ensureError)(err);
2755
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD SmartSafe Error`, {
2756
+ error: (0, utils_3.getError)(error),
2757
+ stationSN: this.rawStation.station_sn,
2758
+ commandIdName: types_1.CommandType[json.cmd],
2759
+ commandId: json.cmd,
2760
+ payload: payload,
2761
+ });
2762
+ }
2763
+ this._clearSecondaryCommandTimeout();
2764
+ this.sendQueuedMessage();
2765
+ break;
2766
+ }
2767
+ case types_1.CommandType.CMD_SMARTSAFE_STATUS_UPDATE: {
2768
+ const payload = json.payload;
2769
+ switch (payload.event_type) {
2770
+ case types_3.SmartSafeEvent.LOCK_STATUS: {
2771
+ const eventValues = payload.event_value;
2772
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD SmartSafe Status update - LOCK_STATUS`, { stationSN: this.rawStation.station_sn, eventValues: eventValues });
2773
+ /*
2774
+ type values:
2775
+ 1: Unlocked by PIN
2776
+ 2: Unlocked by User
2777
+ 3: Unlocked by key
2778
+ 4: Unlocked by App
2779
+ 5: Unlocked by Dual Unlock
2780
+ */
2781
+ if (eventValues.action === 0) {
2782
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTSAFE_LOCK_STATUS, "0");
2783
+ }
2784
+ else if (eventValues.action === 1) {
2785
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTSAFE_LOCK_STATUS, "1");
2786
+ }
2787
+ else if (eventValues.action === 2) {
2788
+ this.emit("jammed", message.channel);
2789
+ }
2790
+ else if (eventValues.action === 3) {
2791
+ this.emit("low battery", message.channel);
2792
+ }
2793
+ break;
2794
+ }
2795
+ case types_3.SmartSafeEvent.SHAKE_ALARM:
2796
+ this.emit("shake alarm", message.channel, payload.event_value);
2797
+ break;
2798
+ case types_3.SmartSafeEvent.ALARM_911:
2799
+ this.emit("911 alarm", message.channel, payload.event_value);
2800
+ break;
2801
+ //case SmartSafeEvent.BATTERY_STATUS:
2802
+ // break;
2803
+ case types_3.SmartSafeEvent.INPUT_ERR_MAX:
2804
+ this.emit("wrong try-protect alarm", message.channel);
2805
+ break;
2806
+ default:
2807
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD SmartSafe Status update - Not implemented`, { stationSN: this.rawStation.station_sn, message: data.toString() });
2808
+ break;
2809
+ }
2810
+ break;
2811
+ }
2812
+ default:
2813
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD SmartSafe - Not implemented`, { stationSN: this.rawStation.station_sn, message: data.toString() });
2814
+ break;
2815
+ }
2816
+ }
2817
+ else if (json.cmd === types_1.CommandType.CMD_ENTRY_SENSOR_STATUS) {
2818
+ // {"cmd":1550,"payload":{"status":1}}
2819
+ const payload = json.payload;
2820
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD EntrySensor Status update`, { stationSN: this.rawStation.station_sn, status: payload?.status });
2821
+ if (payload) {
2822
+ this.emit("sensor status", message.channel, payload.status);
2823
+ }
2824
+ }
2825
+ else if (json.cmd === types_1.CommandType.CMD_CAMERA_GARAGE_DOOR_STATUS) {
2826
+ // {"cmd":7500,"payload":{"type":24,"notify_tag":"","door_id":2}}
2827
+ const payload = json.payload;
2828
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD GarageDoor Status update`, {
2829
+ stationSN: this.rawStation.station_sn,
2830
+ doorId: payload?.door_id,
2831
+ status: payload?.type,
2832
+ notify_tag: payload?.notify_tag,
2833
+ });
2834
+ if (payload) {
2835
+ this.emit("garage door status", message.channel, payload.door_id, payload.type);
2836
+ }
2837
+ }
2838
+ else if (json.cmd === types_1.CommandType.CMD_STORAGE_INFO_HB3) {
2839
+ const payload = json.payload;
2840
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD StorageInfo HB3 update`, { stationSN: this.rawStation.station_sn, body: payload?.body });
2841
+ if (payload) {
2842
+ this.emit("storage info hb3", message.channel, payload.body);
2843
+ }
2844
+ }
2845
+ else {
2846
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Not implemented`, {
2847
+ stationSN: this.rawStation.station_sn,
2848
+ commandIdName: types_1.CommandType[json.cmd],
2849
+ commandId: json.cmd,
2850
+ message: data.toString(),
2851
+ });
2852
+ }
2853
+ }
2854
+ }
2855
+ catch (err) {
2856
+ const error = (0, error_1.ensureError)(err);
2857
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Error`, {
2858
+ error: (0, utils_3.getError)(error),
2859
+ stationSN: this.rawStation.station_sn,
2860
+ message: {
2861
+ seqNo: message.seqNo,
2862
+ channel: message.channel,
2863
+ commandType: types_1.CommandType[message.commandId],
2864
+ signCode: message.signCode,
2865
+ type: message.type,
2866
+ dataType: types_1.P2PDataType[message.dataType],
2867
+ data: message.data.toString(),
2868
+ },
2869
+ });
2870
+ }
2871
+ break;
2872
+ case types_1.CommandType.CMD_GET_DELAY_ALARM:
2873
+ try {
2874
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GET_DELAY_ALARM :`, {
2875
+ stationSN: this.rawStation.station_sn,
2876
+ payload: data.toString("hex"),
2877
+ });
2878
+ //When the alarm is armed, CMD_GET_DELAY_ALARM is called with event data 0, so ignore it
2879
+ const alarmEventNumber = data.subarray(0, 4).readUInt32LE();
2880
+ const alarmDelay = data.subarray(4, 8).readUInt32LE();
2881
+ if (alarmEventNumber === 0) {
2882
+ this.emit("alarm armed");
2883
+ }
2884
+ else {
2885
+ this.emit("alarm delay", alarmEventNumber, alarmDelay);
2886
+ }
2887
+ }
2888
+ catch (err) {
2889
+ const error = (0, error_1.ensureError)(err);
2890
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GET_DELAY_ALARM - Error`, {
2891
+ error: (0, utils_3.getError)(error),
2892
+ stationSN: this.rawStation.station_sn,
2893
+ message: {
2894
+ seqNo: message.seqNo,
2895
+ channel: message.channel,
2896
+ commandType: types_1.CommandType[message.commandId],
2897
+ signCode: message.signCode,
2898
+ type: message.type,
2899
+ dataType: types_1.P2PDataType[message.dataType],
2900
+ data: message.data.toString("hex"),
2901
+ },
2902
+ });
2903
+ }
2904
+ break;
2905
+ case types_1.CommandType.CMD_SET_TONE_FILE:
2906
+ try {
2907
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_SET_TONE_FILE :`, {
2908
+ stationSN: this.rawStation.station_sn,
2909
+ payload: data.toString("hex"),
2910
+ });
2911
+ const alarmEventNumber = data.subarray(0, 4).readUInt32LE();
2912
+ this.emit("alarm event", alarmEventNumber);
2913
+ }
2914
+ catch (err) {
2915
+ const error = (0, error_1.ensureError)(err);
2916
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_SET_TONE_FILE - Error`, {
2917
+ error: (0, utils_3.getError)(error),
2918
+ stationSN: this.rawStation.station_sn,
2919
+ message: {
2920
+ seqNo: message.seqNo,
2921
+ channel: message.channel,
2922
+ commandType: types_1.CommandType[message.commandId],
2923
+ signCode: message.signCode,
2924
+ type: message.type,
2925
+ dataType: types_1.P2PDataType[message.dataType],
2926
+ data: message.data.toString("hex"),
2927
+ },
2928
+ });
2929
+ }
2930
+ break;
2931
+ case types_1.CommandType.CMD_SET_SNOOZE_MODE:
2932
+ // Received for station managed devices when snooze time ends
2933
+ try {
2934
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_SET_SNOOZE_MODE`, {
2935
+ stationSN: this.rawStation.station_sn,
2936
+ payload: Buffer.from(data.toString(), "base64").toString(),
2937
+ });
2938
+ this.emit("parameter", message.channel, types_1.CommandType.CMD_SET_SNOOZE_MODE, data.toString());
2939
+ }
2940
+ catch (err) {
2941
+ const error = (0, error_1.ensureError)(err);
2942
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_SET_SNOOZE_MODE - Error`, {
2943
+ error: (0, utils_3.getError)(error),
2944
+ stationSN: this.rawStation.station_sn,
2945
+ message: {
2946
+ seqNo: message.seqNo,
2947
+ channel: message.channel,
2948
+ commandType: types_1.CommandType[message.commandId],
2949
+ signCode: message.signCode,
2950
+ type: message.type,
2951
+ dataType: types_1.P2PDataType[message.dataType],
2952
+ data: message.data.toString("hex"),
2953
+ },
2954
+ });
2955
+ }
2956
+ break;
2957
+ case types_1.CommandType.CMD_PING:
2958
+ // Ignore
2959
+ break;
2960
+ case types_1.CommandType.CMD_DATABASE_IMAGE:
2961
+ // Received data for preview image download
2962
+ try {
2963
+ const str = (0, utils_1.getNullTerminatedString)(data, "utf8");
2964
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DATABASE_IMAGE`, {
2965
+ stationSN: this.rawStation.station_sn,
2966
+ message: str,
2967
+ });
2968
+ const image = (0, utils_3.parseJSON)(str, logging_1.rootP2PLogger);
2969
+ this.emit("image download", image.file, (0, utils_2.decodeImage)(this.rawStation.p2p_did, Buffer.from(image.content, "base64")));
2970
+ }
2971
+ catch (err) {
2972
+ const error = (0, error_1.ensureError)(err);
2973
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DATABASE_IMAGE - Error`, {
2974
+ error: (0, utils_3.getError)(error),
2975
+ stationSN: this.rawStation.station_sn,
2976
+ message: {
2977
+ seqNo: message.seqNo,
2978
+ channel: message.channel,
2979
+ commandType: types_1.CommandType[message.commandId],
2980
+ signCode: message.signCode,
2981
+ type: message.type,
2982
+ dataType: types_1.P2PDataType[message.dataType],
2983
+ data: message.data.toString(),
2984
+ },
2985
+ });
2986
+ }
2987
+ break;
2988
+ case types_1.CommandType.CMD_GET_TFCARD_STATUS:
2989
+ try {
2990
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GET_TFCARD_STATUS :`, {
2991
+ stationSN: this.rawStation.station_sn,
2992
+ payload: data.toString("hex"),
2993
+ });
2994
+ const tfCardStatus = data.subarray(0, 4).readUInt32LE();
2995
+ this.emit("tfcard status", message.channel, tfCardStatus);
2996
+ }
2997
+ catch (err) {
2998
+ const error = (0, error_1.ensureError)(err);
2999
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GET_TFCARD_STATUS - Error`, {
3000
+ error: (0, utils_3.getError)(error),
3001
+ stationSN: this.rawStation.station_sn,
3002
+ message: {
3003
+ seqNo: message.seqNo,
3004
+ channel: message.channel,
3005
+ commandType: types_1.CommandType[message.commandId],
3006
+ signCode: message.signCode,
3007
+ type: message.type,
3008
+ dataType: types_1.P2PDataType[message.dataType],
3009
+ data: message.data.toString("hex"),
3010
+ },
3011
+ });
3012
+ }
3013
+ break;
3014
+ case types_1.CommandType.CMD_DATABASE:
3015
+ try {
3016
+ const str = (0, utils_1.getNullTerminatedString)(data, "utf8");
3017
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DATABASE :`, {
3018
+ stationSN: this.rawStation.station_sn,
3019
+ payload: str,
3020
+ });
3021
+ const databaseResponse = (0, utils_3.parseJSON)(str, logging_1.rootP2PLogger);
3022
+ switch (databaseResponse.cmd) {
3023
+ case types_1.CommandType.CMD_DATABASE_QUERY_LATEST_INFO: {
3024
+ let data = [];
3025
+ if (databaseResponse.data !== undefined && databaseResponse.data !== "[]")
3026
+ data = databaseResponse.data;
3027
+ const result = [];
3028
+ for (const record of data) {
3029
+ if (record.payload.crop_hb3_path !== "") {
3030
+ result.push({
3031
+ device_sn: record.device_sn,
3032
+ event_count: record.payload.event_count,
3033
+ crop_local_path: record.payload.crop_hb3_path,
3034
+ });
3035
+ }
3036
+ else {
3037
+ result.push({
3038
+ device_sn: record.device_sn,
3039
+ event_count: record.payload.event_count,
3040
+ crop_cloud_path: record.payload.crop_cloud_path,
3041
+ });
3042
+ }
3043
+ }
3044
+ this.emit("database query latest", databaseResponse.mIntRet, result);
3045
+ break;
3046
+ }
3047
+ case types_1.CommandType.CMD_DATABASE_COUNT_BY_DATE: {
3048
+ let data = [];
3049
+ if (databaseResponse.data !== undefined && databaseResponse.data !== "[]")
3050
+ data = databaseResponse.data;
3051
+ const result = [];
3052
+ for (const record of data) {
3053
+ result.push({
3054
+ day: parse(record.days, "YYYYMMDD"),
3055
+ count: record.count,
3056
+ });
3057
+ }
3058
+ this.emit("database count by date", databaseResponse.mIntRet, result);
3059
+ break;
3060
+ }
3061
+ case types_1.CommandType.CMD_DATABASE_QUERY_LOCAL: {
3062
+ let data = [];
3063
+ if (databaseResponse.data !== undefined && databaseResponse.data !== "[]")
3064
+ data = databaseResponse.data;
3065
+ const result = new sweet_collections_1.SortedMap((a, b) => a - b);
3066
+ for (const record of data) {
3067
+ for (const tableRecord of record.payload) {
3068
+ let tmpRecord = result.get(tableRecord.record_id);
3069
+ if (tmpRecord === undefined) {
3070
+ tmpRecord = {
3071
+ record_id: tableRecord.record_id,
3072
+ device_sn: tableRecord.device_sn,
3073
+ station_sn: tableRecord.station_sn,
3074
+ };
3075
+ }
3076
+ if (record.table_name === "history_record_info") {
3077
+ tmpRecord.history = {
3078
+ device_type: tableRecord.device_type,
3079
+ account: tableRecord.account,
3080
+ start_time: parse(tableRecord.start_time, "YYYY-MM-DD HH:mm:ss"),
3081
+ end_time: parse(tableRecord.end_time, "YYYY-MM-DD HH:mm:ss"),
3082
+ frame_num: tableRecord.frame_num,
3083
+ storage_type: tableRecord.storage_type,
3084
+ storage_cloud: tableRecord.storage_cloud,
3085
+ cipher_id: tableRecord.cipher_id,
3086
+ vision: tableRecord.vision,
3087
+ video_type: tableRecord.video_type,
3088
+ has_lock: tableRecord.has_lock,
3089
+ automation_id: tableRecord.automation_id,
3090
+ trigger_type: tableRecord.trigger_type,
3091
+ push_mode: tableRecord.push_mode,
3092
+ mic_status: tableRecord.mic_status,
3093
+ res_change: tableRecord.res_change,
3094
+ res_best_width: tableRecord.res_best_width,
3095
+ res_best_height: tableRecord.res_best_height,
3096
+ self_learning: tableRecord.self_learning,
3097
+ storage_path: tableRecord.storage_path,
3098
+ thumb_path: tableRecord.thumb_path,
3099
+ write_status: tableRecord.write_status,
3100
+ cloud_path: tableRecord.cloud_path,
3101
+ folder_size: tableRecord.folder_size,
3102
+ storage_status: tableRecord.storage_status,
3103
+ storage_label: tableRecord.storage_label,
3104
+ time_zone: tableRecord.time_zone,
3105
+ mp4_cloud: tableRecord.mp4_cloud,
3106
+ snapshot_cloud: tableRecord.snapshot_cloud,
3107
+ table_version: tableRecord.table_version,
3108
+ };
3109
+ }
3110
+ else if (record.table_name === "record_crop_picture_info") {
3111
+ if (tmpRecord.picture === undefined) {
3112
+ tmpRecord.picture = [];
3113
+ }
3114
+ tmpRecord.picture.push({
3115
+ picture_id: tableRecord.picture_id,
3116
+ detection_type: tableRecord.detection_type,
3117
+ person_id: tableRecord.person_id,
3118
+ crop_path: tableRecord.crop_path,
3119
+ event_time: parse(tableRecord.event_time, "YYYY-MM-DD HH:mm:ss"),
3120
+ person_recog_flag: tableRecord
3121
+ .person_recog_flag,
3122
+ crop_pic_quality: tableRecord.crop_pic_quality,
3123
+ pic_marking_flag: tableRecord.pic_marking_flag,
3124
+ group_id: tableRecord.group_id,
3125
+ crop_id: tableRecord.crop_id,
3126
+ start_time: parse(tableRecord.start_time, "YYYY-MM-DD HH:mm:ss"),
3127
+ storage_type: tableRecord.storage_type,
3128
+ storage_status: tableRecord.storage_status,
3129
+ storage_label: tableRecord.storage_label,
3130
+ table_version: tableRecord.table_version,
3131
+ update_time: tableRecord.update_time,
3132
+ });
3133
+ }
3134
+ else {
3135
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented - CMD_DATABASE_QUERY_LOCAL - table_name: ${record.table_name}`, { stationSN: this.rawStation.station_sn });
3136
+ }
3137
+ result.set(tableRecord.record_id, tmpRecord);
3138
+ }
3139
+ }
3140
+ this.emit("database query local", databaseResponse.mIntRet, Array.from(result.values()));
3141
+ break;
3142
+ }
3143
+ case types_1.CommandType.CMD_DATABASE_QUERY_BY_DATE: {
3144
+ let data = [];
3145
+ if (databaseResponse.data !== undefined && databaseResponse.data !== "[]")
3146
+ data = databaseResponse.data;
3147
+ const result = [];
3148
+ for (const record of data) {
3149
+ result.push({
3150
+ device_sn: record.device_sn,
3151
+ device_type: record.device_type,
3152
+ start_time: parse(record.start_time, "YYYY-MM-DD HH:mm:ss"),
3153
+ end_time: parse(record.end_time, "YYYY-MM-DD HH:mm:ss"),
3154
+ storage_path: record.storage_path,
3155
+ thumb_path: record.thumb_path,
3156
+ cipher_id: record.cipher_id,
3157
+ folder_size: record.folder_size,
3158
+ frame_num: record.frame_num,
3159
+ trigger_type: record.trigger_type,
3160
+ video_type: record.video_type,
3161
+ record_id: record.record_id,
3162
+ station_sn: record.station_sn,
3163
+ storage_type: record.storage_type,
3164
+ storage_cloud: record.storage_cloud,
3165
+ });
3166
+ }
3167
+ this.emit("database query by date", databaseResponse.mIntRet, result);
3168
+ break;
3169
+ }
3170
+ case types_1.CommandType.CMD_DATABASE_DELETE: {
3171
+ const data = databaseResponse.data;
3172
+ let failed_delete = [];
3173
+ if (databaseResponse.data !== undefined && data.failed_delete !== "[]")
3174
+ failed_delete = data.failed_delete;
3175
+ this.emit("database delete", databaseResponse.mIntRet, failed_delete);
3176
+ break;
3177
+ }
3178
+ default:
3179
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented - CMD_DATABASE message`, {
3180
+ stationSN: this.rawStation.station_sn,
3181
+ commandIdName: types_1.CommandType[message.commandId],
3182
+ commandId: message.commandId,
3183
+ channel: message.channel,
3184
+ databaseResponse: databaseResponse,
3185
+ });
3186
+ break;
3187
+ }
3188
+ }
3189
+ catch (err) {
3190
+ const error = (0, error_1.ensureError)(err);
3191
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_DATABASE - Error`, {
3192
+ error: (0, utils_3.getError)(error),
3193
+ stationSN: this.rawStation.station_sn,
3194
+ message: {
3195
+ seqNo: message.seqNo,
3196
+ channel: message.channel,
3197
+ commandType: types_1.CommandType[message.commandId],
3198
+ signCode: message.signCode,
3199
+ type: message.type,
3200
+ dataType: types_1.P2PDataType[message.dataType],
3201
+ data: message.data.toString(),
3202
+ },
3203
+ });
3204
+ }
3205
+ break;
3206
+ case types_1.CommandType.CMD_GATEWAYINFO:
3207
+ const cipherID = data.subarray(0, 2).readUInt16LE();
3208
+ //const unknownNumber = data.subarray(2, 2).readUInt16LE();
3209
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - cipherID`, {
3210
+ stationSN: this.rawStation.station_sn,
3211
+ channel: message.channel,
3212
+ data: data.toString("hex"),
3213
+ cipherID: cipherID,
3214
+ });
3215
+ const encryptedKey = (0, utils_1.readNullTerminatedBuffer)(data.subarray(4));
3216
+ this.api
3217
+ .getCipher(/*this.rawStation.station_sn, */ cipherID, this.rawStation.member.admin_user_id)
3218
+ .then((cipher) => {
3219
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - get cipher with cipherID`, {
3220
+ stationSN: this.rawStation.station_sn,
3221
+ channel: message.channel,
3222
+ data: data.toString("hex"),
3223
+ cipherID: cipherID,
3224
+ cipher: JSON.stringify(cipher),
3225
+ });
3226
+ if (cipher !== undefined) {
3227
+ this.encryption = types_1.EncryptionType.LEVEL_2;
3228
+ const rsa = (0, utils_1.getRSAPrivateKey)(cipher.private_key, this.enableEmbeddedPKCS1Support);
3229
+ this.p2pKey = rsa.decrypt(encryptedKey);
3230
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - set encryption level 2`, { stationSN: this.rawStation.station_sn, key: this.p2pKey.toString("hex") });
3231
+ }
3232
+ else {
3233
+ this.encryption = types_1.EncryptionType.LEVEL_1;
3234
+ this.p2pKey = Buffer.from((0, utils_1.getP2PCommandEncryptionKey)(this.rawStation.station_sn, this.rawStation.p2p_did));
3235
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - set encryption level 1`, { stationSN: this.rawStation.station_sn, key: this.p2pKey.toString("hex") });
3236
+ }
3237
+ this._clearTimeout(this.messageStates.get(message.seqNo)?.timeout);
3238
+ this.p2pSeqMapping.forEach((value, key, map) => {
3239
+ if (value === message.seqNo) {
3240
+ map.delete(key);
3241
+ }
3242
+ });
3243
+ this.p2pDataSeqNumber--;
3244
+ this.messageStates.delete(message.seqNo);
3245
+ this.sendQueuedMessage();
3246
+ })
3247
+ .catch((err) => {
3248
+ const error = (0, error_1.ensureError)(err);
3249
+ this.encryption = types_1.EncryptionType.LEVEL_1;
3250
+ this.p2pKey = Buffer.from((0, utils_1.getP2PCommandEncryptionKey)(this.rawStation.station_sn, this.rawStation.p2p_did));
3251
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - set encryption level 1 (fallback)`, {
3252
+ error: (0, utils_3.getError)(error),
3253
+ stationSN: this.rawStation.station_sn,
3254
+ message: {
3255
+ seqNo: message.seqNo,
3256
+ channel: message.channel,
3257
+ commandType: types_1.CommandType[message.commandId],
3258
+ signCode: message.signCode,
3259
+ type: message.type,
3260
+ dataType: types_1.P2PDataType[message.dataType],
3261
+ data: message.data.toString("hex"),
3262
+ },
3263
+ key: this.p2pKey.toString("hex"),
3264
+ });
3265
+ this._clearTimeout(this.messageStates.get(message.seqNo)?.timeout);
3266
+ this.p2pSeqMapping.forEach((value, key, map) => {
3267
+ if (value === message.seqNo) {
3268
+ map.delete(key);
3269
+ }
3270
+ });
3271
+ this.p2pDataSeqNumber--;
3272
+ this.messageStates.delete(message.seqNo);
3273
+ this.sendQueuedMessage();
3274
+ });
3275
+ break;
3276
+ default:
3277
+ logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented`, {
3278
+ stationSN: this.rawStation.station_sn,
3279
+ commandIdName: types_1.CommandType[message.commandId],
3280
+ commandId: message.commandId,
3281
+ channel: message.channel,
3282
+ data: data.toString("hex"),
3283
+ });
3284
+ break;
3285
+ }
3286
+ }
3287
+ catch (err) {
3288
+ const error = (0, error_1.ensureError)(err);
3289
+ logging_1.rootP2PLogger.error(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Error`, {
3290
+ error: (0, utils_3.getError)(error),
3291
+ stationSN: this.rawStation.station_sn,
3292
+ message: {
3293
+ seqNo: message.seqNo,
3294
+ channel: message.channel,
3295
+ commandType: types_1.CommandType[message.commandId],
3296
+ signCode: message.signCode,
3297
+ type: message.type,
3298
+ dataType: types_1.P2PDataType[message.dataType],
3299
+ data: message.data.toString("hex"),
3300
+ },
3301
+ });
3302
+ }
3303
+ }
3304
+ async sendAck(address, dataType, seqNo) {
3305
+ const num_pending_acks = 1; // Max possible: 17 in one ack packet
3306
+ const pendingAcksBuffer = Buffer.allocUnsafe(2);
3307
+ pendingAcksBuffer.writeUInt16BE(num_pending_acks, 0);
3308
+ const seqBuffer = Buffer.allocUnsafe(2);
3309
+ seqBuffer.writeUInt16BE(seqNo, 0);
3310
+ const payload = Buffer.concat([dataType, pendingAcksBuffer, seqBuffer]);
3311
+ await this.sendMessage(`Send ack`, address, types_1.RequestMessageType.ACK, payload);
3312
+ }
3313
+ getDataType(input) {
3314
+ if (input.compare(types_1.P2PDataTypeHeader.DATA) === 0) {
3315
+ return types_1.P2PDataType.DATA;
3316
+ }
3317
+ else if (input.compare(types_1.P2PDataTypeHeader.VIDEO) === 0) {
3318
+ return types_1.P2PDataType.VIDEO;
3319
+ }
3320
+ else if (input.compare(types_1.P2PDataTypeHeader.CONTROL) === 0) {
3321
+ return types_1.P2PDataType.CONTROL;
3322
+ }
3323
+ else if (input.compare(types_1.P2PDataTypeHeader.BINARY) === 0) {
3324
+ return types_1.P2PDataType.BINARY;
3325
+ }
3326
+ return types_1.P2PDataType.UNKNOWN;
3327
+ }
3328
+ async close() {
3329
+ this.terminating = true;
3330
+ this._clearLookupTimeout();
3331
+ this._clearLocalLookupRetryTimeout();
3332
+ this._clearLookupRetryTimeout();
3333
+ this._clearConnectTimeout();
3334
+ this._clearHeartbeatTimeout();
3335
+ this._clearMessageStateTimeouts();
3336
+ this._clearMessageVideoStateTimeouts();
3337
+ this._clearSecondaryCommandTimeout();
3338
+ this.sendQueue = [];
3339
+ if (this.socket) {
3340
+ if (this.connected) {
3341
+ await this.sendMessage(`Send end connection`, this.connectAddress, types_1.RequestMessageType.END);
3342
+ this._disconnected();
3343
+ }
3344
+ else {
3345
+ this._initialize();
3346
+ }
3347
+ }
3348
+ }
3349
+ getHeartbeatInterval() {
3350
+ return this.HEARTBEAT_INTERVAL;
3351
+ }
3352
+ onClose() {
3353
+ this.socket.removeAllListeners();
3354
+ this.socket = (0, dgram_1.createSocket)("udp4");
3355
+ this.socket.on("message", (msg, rinfo) => this.handleMsg(msg, rinfo));
3356
+ this.socket.on("error", (error) => this.onError(error));
3357
+ this.socket.on("close", () => this.onClose());
3358
+ this.binded = false;
3359
+ this._disconnected();
3360
+ }
3361
+ onError(err) {
3362
+ const error = (0, error_1.ensureError)(err);
3363
+ logging_1.rootP2PLogger.debug(`Socket Error:`, { error: (0, utils_3.getError)(error), stationSN: this.rawStation.station_sn });
3364
+ }
3365
+ scheduleHeartbeat() {
3366
+ if (this.isConnected()) {
3367
+ this.sendPing(this.connectAddress);
3368
+ this.heartbeatTimeout = setTimeout(() => {
3369
+ this.scheduleHeartbeat();
3370
+ }, this.getHeartbeatInterval());
3371
+ }
3372
+ else {
3373
+ logging_1.rootP2PLogger.debug(`Heartbeat not activated because no connection is present!`, {
3374
+ stationSN: this.rawStation.station_sn,
3375
+ });
3376
+ }
3377
+ }
3378
+ scheduleP2PKeepalive() {
3379
+ if (this.isConnected()) {
3380
+ this.sendCommandPing(http_1.Station.CHANNEL);
3381
+ this.keepaliveTimeout = setTimeout(() => {
3382
+ this.scheduleP2PKeepalive();
3383
+ }, this.KEEPALIVE_INTERVAL);
3384
+ this.closeEnergySavingDevice();
3385
+ }
3386
+ else {
3387
+ logging_1.rootP2PLogger.debug(`P2P keepalive not activated because no connection is present`, {
3388
+ stationSN: this.rawStation.station_sn,
3389
+ });
3390
+ }
3391
+ }
3392
+ getDownloadRSAPrivateKey() {
3393
+ if (this.currentMessageState[types_1.P2PDataType.BINARY].rsaKey === null) {
3394
+ this.currentMessageState[types_1.P2PDataType.BINARY].rsaKey = (0, utils_1.getNewRSAPrivateKey)(this.enableEmbeddedPKCS1Support);
3395
+ }
3396
+ return this.currentMessageState[types_1.P2PDataType.BINARY].rsaKey;
3397
+ }
3398
+ setDownloadRSAPrivateKeyPem(pem) {
3399
+ this.currentMessageState[types_1.P2PDataType.BINARY].rsaKey = (0, utils_1.getRSAPrivateKey)(pem, this.enableEmbeddedPKCS1Support);
3400
+ }
3401
+ getRSAPrivateKey() {
3402
+ return this.currentMessageState[types_1.P2PDataType.VIDEO].rsaKey;
3403
+ }
3404
+ initializeStream(datatype) {
3405
+ this.currentMessageState[datatype].videoStream?.destroy();
3406
+ this.currentMessageState[datatype].audioStream?.destroy();
3407
+ this.currentMessageState[datatype].videoStream = null;
3408
+ this.currentMessageState[datatype].audioStream = null;
3409
+ this.currentMessageState[datatype].videoStream = new stream_1.Readable({
3410
+ autoDestroy: true,
3411
+ read() { } /*,
3412
+
3413
+ destroy(this, error, _callback) {
3414
+ if (error) {
3415
+ this.emit("error", error);
3416
+ }
3417
+ this.emit("end");
3418
+ this.emit("close");
3419
+ }*/,
3420
+ });
3421
+ this.currentMessageState[datatype].audioStream = new stream_1.Readable({
3422
+ autoDestroy: true,
3423
+ read() { } /*,
3424
+
3425
+ destroy(this, error, _callback) {
3426
+ if (error) {
3427
+ this.emit("error", error);
3428
+ }
3429
+ this.emit("end");
3430
+ this.emit("close");
3431
+ }*/,
3432
+ });
3433
+ this.currentMessageState[datatype].p2pStreaming = false;
3434
+ if (this.currentMessageState[datatype].waitForSeqNoTimeout !== undefined) {
3435
+ clearTimeout(this.currentMessageState[datatype].waitForSeqNoTimeout);
3436
+ this.currentMessageState[datatype].waitForSeqNoTimeout = undefined;
3437
+ }
3438
+ if (this.currentMessageState[datatype].waitForAudioData !== undefined) {
3439
+ clearTimeout(this.currentMessageState[datatype].waitForAudioData);
3440
+ this.currentMessageState[datatype].waitForAudioData = undefined;
3441
+ }
3442
+ }
3443
+ endStream(datatype, sendStopCommand = false) {
3444
+ if (this.currentMessageState[datatype].p2pStreaming) {
3445
+ if (sendStopCommand) {
3446
+ switch (datatype) {
3447
+ case types_1.P2PDataType.VIDEO:
3448
+ this.sendCommandWithInt({
3449
+ commandType: types_1.CommandType.CMD_STOP_REALTIME_MEDIA,
3450
+ value: this.currentMessageState[datatype].p2pStreamChannel,
3451
+ channel: this.currentMessageState[datatype].p2pStreamChannel,
3452
+ }, {
3453
+ command: {
3454
+ name: http_1.CommandName.DeviceStopLivestream,
3455
+ },
3456
+ });
3457
+ break;
3458
+ case types_1.P2PDataType.BINARY:
3459
+ this.sendCommandWithInt({
3460
+ commandType: types_1.CommandType.CMD_DOWNLOAD_CANCEL,
3461
+ value: this.currentMessageState[datatype].p2pStreamChannel,
3462
+ strValueSub: this.rawStation.member.admin_user_id,
3463
+ channel: this.currentMessageState[datatype].p2pStreamChannel,
3464
+ }, {
3465
+ command: {
3466
+ name: http_1.CommandName.DeviceCancelDownload,
3467
+ },
3468
+ });
3469
+ break;
3470
+ }
3471
+ }
3472
+ this.currentMessageState[datatype].p2pStreaming = false;
3473
+ this.currentMessageState[datatype].videoStream?.push(null);
3474
+ this.currentMessageState[datatype].audioStream?.push(null);
3475
+ if (this.currentMessageState[datatype].p2pStreamingTimeout) {
3476
+ clearTimeout(this.currentMessageState[datatype].p2pStreamingTimeout);
3477
+ this.currentMessageState[datatype].p2pStreamingTimeout = undefined;
3478
+ }
3479
+ if (!this.currentMessageState[datatype].invalidStream && !this.currentMessageState[datatype].p2pStreamNotStarted)
3480
+ this.emitStreamStopEvent(datatype);
3481
+ if (this.currentMessageState[datatype].queuedData.size > 0) {
3482
+ this.expectedSeqNo[datatype] = this._incrementSequence([...this.currentMessageState[datatype].queuedData.keys()][this.currentMessageState[datatype].queuedData.size - 1]);
3483
+ }
3484
+ this.initializeMessageBuilder(datatype);
3485
+ this.initializeMessageState(datatype, this.currentMessageState[datatype].rsaKey);
3486
+ this.initializeStream(datatype);
3487
+ this.closeEnergySavingDevice();
3488
+ }
3489
+ }
3490
+ endRTSPStream(channel) {
3491
+ if (this.currentMessageState[types_1.P2PDataType.DATA].rtspStreaming[channel]) {
3492
+ this.currentMessageState[types_1.P2PDataType.DATA].rtspStream[channel] = false;
3493
+ this.currentMessageState[types_1.P2PDataType.DATA].rtspStreaming[channel] = false;
3494
+ this.emit("rtsp livestream stopped", channel);
3495
+ }
3496
+ }
3497
+ emitStreamStartEvent(datatype) {
3498
+ this.currentMessageState[datatype].p2pStreamNotStarted = false;
3499
+ if (datatype === types_1.P2PDataType.VIDEO) {
3500
+ this.emit("livestream started", this.currentMessageState[datatype].p2pStreamChannel, this.currentMessageState[datatype].p2pStreamMetadata, this.currentMessageState[datatype].videoStream, this.currentMessageState[datatype].audioStream);
3501
+ }
3502
+ else if (datatype === types_1.P2PDataType.BINARY) {
3503
+ this.emit("download started", this.currentMessageState[datatype].p2pStreamChannel, this.currentMessageState[datatype].p2pStreamMetadata, this.currentMessageState[datatype].videoStream, this.currentMessageState[datatype].audioStream);
3504
+ }
3505
+ }
3506
+ emitStreamStopEvent(datatype) {
3507
+ if (datatype === types_1.P2PDataType.VIDEO) {
3508
+ this.emit("livestream stopped", this.currentMessageState[datatype].p2pStreamChannel);
3509
+ }
3510
+ else if (datatype === types_1.P2PDataType.BINARY) {
3511
+ this.emit("download finished", this.currentMessageState[datatype].p2pStreamChannel);
3512
+ }
3513
+ }
3514
+ isStreaming(channel, datatype) {
3515
+ if (this.currentMessageState[datatype].p2pStreamChannel === channel)
3516
+ return this.currentMessageState[datatype].p2pStreaming;
3517
+ return false;
3518
+ }
3519
+ isLiveStreaming(channel) {
3520
+ return this.isStreaming(channel, types_1.P2PDataType.VIDEO);
3521
+ }
3522
+ isCurrentlyStreaming() {
3523
+ for (const element of Object.values(this.currentMessageState)) {
3524
+ if (element.p2pStreaming || element.p2pTalkback)
3525
+ return true;
3526
+ }
3527
+ return false;
3528
+ }
3529
+ isRTSPLiveStreaming(channel) {
3530
+ return this.currentMessageState[types_1.P2PDataType.DATA].rtspStreaming[channel]
3531
+ ? this.currentMessageState[types_1.P2PDataType.DATA].rtspStreaming[channel]
3532
+ : false;
3533
+ }
3534
+ isDownloading(channel) {
3535
+ return this.isStreaming(channel, types_1.P2PDataType.BINARY);
3536
+ }
3537
+ getLockSequenceNumber() {
3538
+ if (this.lockSeqNumber === -1) {
3539
+ let deviceType = undefined;
3540
+ let deviceSN = undefined;
3541
+ if (this.rawStation?.devices?.length > 0) {
3542
+ deviceType = this.rawStation?.devices[0]?.device_type;
3543
+ deviceSN = this.rawStation?.devices[0]?.device_sn;
3544
+ }
3545
+ this.lockSeqNumber = (0, utils_1.generateLockSequence)(deviceType, deviceSN);
3546
+ }
3547
+ return this.lockSeqNumber;
3548
+ }
3549
+ incLockSequenceNumber() {
3550
+ if (this.lockSeqNumber === -1) {
3551
+ this.getLockSequenceNumber();
3552
+ }
3553
+ else
3554
+ this.lockSeqNumber++;
3555
+ return this.lockSeqNumber;
3556
+ }
3557
+ setConnectionType(type) {
3558
+ this.connectionType = type;
3559
+ }
3560
+ getConnectionType() {
3561
+ return this.connectionType;
3562
+ }
3563
+ isEnergySavingDevice() {
3564
+ return this.energySavingDevice;
3565
+ }
3566
+ async getDSKKeys() {
3567
+ if (this.api.isConnected()) {
3568
+ try {
3569
+ const data = {
3570
+ invalid_dsks: {},
3571
+ station_sns: [this.rawStation.station_sn],
3572
+ transaction: `${new Date().getTime()}`,
3573
+ };
3574
+ data.invalid_dsks[this.rawStation.station_sn] = "";
3575
+ const response = await this.api.request({
3576
+ method: "post",
3577
+ endpoint: "v1/app/equipment/get_dsk_keys",
3578
+ data: data,
3579
+ });
3580
+ logging_1.rootP2PLogger.debug(`Get DSK keys - Response:`, { stationSN: this.rawStation.station_sn, data: response.data });
3581
+ if (response.status == 200) {
3582
+ const result = response.data;
3583
+ if (result.code == 0) {
3584
+ const dataresult = result.data;
3585
+ dataresult.dsk_keys.forEach((key) => {
3586
+ if (key.station_sn == this.rawStation.station_sn) {
3587
+ this.dskKey = key.dsk_key;
3588
+ this.dskExpiration = new Date(key.expiration * 1000);
3589
+ logging_1.rootP2PLogger.debug(`Get DSK keys - received key and expiration`, {
3590
+ stationSN: this.rawStation.station_sn,
3591
+ dskKey: this.dskKey,
3592
+ dskExpiration: this.dskExpiration,
3593
+ });
3594
+ }
3595
+ });
3596
+ }
3597
+ else {
3598
+ logging_1.rootP2PLogger.error(`Get DSK keys - Response code not ok`, {
3599
+ stationSN: this.rawStation.station_sn,
3600
+ code: result.code,
3601
+ msg: result.msg,
3602
+ });
3603
+ }
3604
+ }
3605
+ else {
3606
+ logging_1.rootP2PLogger.error(`Get DSK keys - Status return code not 200`, {
3607
+ stationSN: this.rawStation.station_sn,
3608
+ status: response.status,
3609
+ statusText: response.statusText,
3610
+ });
3611
+ }
3612
+ }
3613
+ catch (err) {
3614
+ const error = (0, error_1.ensureError)(err);
3615
+ logging_1.rootP2PLogger.error(`Get DSK keys - Generic Error`, {
3616
+ error: (0, utils_3.getError)(error),
3617
+ stationSN: this.rawStation.station_sn,
3618
+ });
3619
+ }
3620
+ }
3621
+ }
3622
+ updateRawStation(value) {
3623
+ this.rawStation = value;
3624
+ this.channel = http_1.Station.getChannel(value.device_type);
3625
+ if (device_1.Device.hasBattery(this.rawStation.device_type)) {
3626
+ if (!this.energySavingDevice) {
3627
+ this.energySavingDevice = true;
3628
+ }
3629
+ if (this.energySavingDevice) {
3630
+ logging_1.rootP2PLogger.debug(`Identified standalone battery device ${this.rawStation.station_sn} => activate p2p keepalive command`);
3631
+ }
3632
+ }
3633
+ else {
3634
+ this.energySavingDevice = false;
3635
+ }
3636
+ if (this.rawStation.devices)
3637
+ for (const device of this.rawStation.devices) {
3638
+ this.deviceSNs[device.device_channel] = {
3639
+ sn: device.device_sn,
3640
+ adminUserId: this.rawStation.member.admin_user_id,
3641
+ };
3642
+ }
3643
+ }
3644
+ initializeTalkbackStream(channel = 0) {
3645
+ this.talkbackStream = new talkback_1.TalkbackStream();
3646
+ this.talkbackStream.on("data", (audioData) => {
3647
+ this.sendTalkbackAudioFrame(audioData, channel);
3648
+ });
3649
+ this.talkbackStream.on("error", (error) => {
3650
+ this.onTalkbackStreamError(error);
3651
+ });
3652
+ this.talkbackStream.on("close", () => {
3653
+ this.onTalkbackStreamClose();
3654
+ });
3655
+ }
3656
+ sendTalkbackAudioFrame(audioData, channel) {
3657
+ const messageHeader = (0, utils_1.buildCommandHeader)(this.videoSeqNumber, types_1.CommandType.CMD_AUDIO_FRAME, types_1.P2PDataTypeHeader.VIDEO);
3658
+ const messageAudioHeader = (0, utils_1.buildTalkbackAudioFrameHeader)(audioData, channel);
3659
+ const messageData = Buffer.concat([messageHeader, messageAudioHeader, audioData]);
3660
+ const message = {
3661
+ sequence: this.videoSeqNumber,
3662
+ channel: channel,
3663
+ data: messageData,
3664
+ retries: 0,
3665
+ };
3666
+ this.videoSeqNumber = this._incrementSequence(this.videoSeqNumber);
3667
+ this._sendVideoData(message);
3668
+ }
3669
+ onTalkbackStreamClose() {
3670
+ this.talkbackStream?.removeAllListeners();
3671
+ }
3672
+ onTalkbackStreamError(err) {
3673
+ const error = (0, error_1.ensureError)(err);
3674
+ logging_1.rootP2PLogger.debug(`Talkback Stream Error:`, { error: (0, utils_3.getError)(error), stationSN: this.rawStation.station_sn });
3675
+ }
3676
+ async _sendVideoData(message) {
3677
+ if (message.retries < this.MAX_RETRIES) {
3678
+ message.retries++;
3679
+ }
3680
+ else {
3681
+ logging_1.rootP2PLogger.error(`Sending video data - Max send video data retries ${this.messageVideoStates.get(message.sequence)?.retries} reached. Discard data.`, {
3682
+ stationSN: this.rawStation.station_sn,
3683
+ sequence: message.sequence,
3684
+ channel: message.channel,
3685
+ retries: message.retries,
3686
+ });
3687
+ this.messageVideoStates.delete(message.sequence);
3688
+ this.emit("talkback error", message.channel, new error_1.TalkbackError("Max send video data retries reached. Discard data packet.", {
3689
+ context: { station: this.rawStation.station_sn, channel: message.channel, retries: message.retries },
3690
+ }));
3691
+ return;
3692
+ }
3693
+ message = message;
3694
+ message.timeout = setTimeout(() => {
3695
+ this._sendVideoData(message);
3696
+ }, this.MAX_AKNOWLEDGE_TIMEOUT);
3697
+ this.messageVideoStates.set(message.sequence, message);
3698
+ logging_1.rootP2PLogger.trace("Sending p2p video data...", {
3699
+ station: this.rawStation.station_sn,
3700
+ sequence: message.sequence,
3701
+ channel: message.channel,
3702
+ retries: message.retries,
3703
+ messageVideoStatesSize: this.messageVideoStates.size,
3704
+ });
3705
+ await this.sendMessage(`Send video data`, this.connectAddress, types_1.RequestMessageType.DATA, message.data);
3706
+ }
3707
+ isTalkbackOngoing(channel) {
3708
+ if (this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkbackChannel === channel)
3709
+ return this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkback;
3710
+ return false;
3711
+ }
3712
+ startTalkback(channel = 0) {
3713
+ this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkback = true;
3714
+ this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkbackChannel = channel;
3715
+ this.initializeTalkbackStream(channel);
3716
+ this.talkbackStream?.startTalkback();
3717
+ this.emit("talkback started", channel, this.talkbackStream);
3718
+ }
3719
+ stopTalkback(channel = 0) {
3720
+ this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkback = false;
3721
+ this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkbackChannel = -1;
3722
+ this.talkbackStream?.stopTalkback();
3723
+ this.emit("talkback stopped", channel);
3724
+ this.closeEnergySavingDevice();
3725
+ }
3726
+ setLockAESKey(commandCode, aesKey) {
3727
+ this.lockAESKeys.set(commandCode, aesKey);
3728
+ }
3729
+ getLockAESKey(commandCode) {
3730
+ return this.lockAESKeys.get(commandCode);
3731
+ }
3732
+ isConnecting() {
3733
+ return this.connecting;
3734
+ }
3735
+ }
3736
+ exports.P2PClientProtocol = P2PClientProtocol;
3737
+ //# sourceMappingURL=session.js.map