eufy-security-client 3.8.0 → 4.0.0-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/build/eufysecurity.d.ts +2 -0
- package/build/eufysecurity.js +17 -0
- package/build/eufysecurity.js.map +1 -1
- package/build/http/api.d.ts +2 -0
- package/build/http/api.js +39 -6
- package/build/http/api.js.map +1 -1
- package/build/http/device.d.ts +13 -1
- package/build/http/device.js +70 -15
- package/build/http/device.js.map +1 -1
- package/build/http/interfaces.d.ts +1 -0
- package/build/http/models.d.ts +1 -0
- package/build/http/station.d.ts +9 -1
- package/build/http/station.js +108 -31
- package/build/http/station.js.map +1 -1
- package/build/http/types.d.ts +4 -0
- package/build/http/types.js +218 -7
- package/build/http/types.js.map +1 -1
- package/build/p2p/adts.d.ts +12 -0
- package/build/p2p/adts.js +185 -0
- package/build/p2p/adts.js.map +1 -0
- package/build/p2p/interfaces.d.ts +10 -0
- package/build/p2p/session.d.ts +3 -1
- package/build/p2p/session.js +138 -30
- package/build/p2p/session.js.map +1 -1
- package/build/p2p/types.d.ts +1 -0
- package/build/p2p/types.js +1 -0
- package/build/p2p/types.js.map +1 -1
- package/build/p2p/utils.d.ts +11 -0
- package/build/p2p/utils.js +76 -4
- package/build/p2p/utils.js.map +1 -1
- package/coverage/clover.xml +8665 -8452
- package/coverage/coverage-final.json +32 -31
- package/coverage/lcov-report/index.html +41 -41
- package/coverage/lcov.info +17294 -15644
- package/package.json +7 -7
- package/.idea/eufy-security-client.iml +0 -12
- package/.idea/modules.xml +0 -8
- package/.idea/vcs.xml +0 -7
- package/bin/act +0 -0
- package/coverage/lcov-report/cache.ts.html +0 -184
- package/coverage/lcov-report/error.ts.html +0 -871
- package/coverage/lcov-report/logging.ts.html +0 -598
- package/dont-care.js +0 -101
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalize a buffer of ADTS audio data by scanning for ADTS frames and splitting
|
|
3
|
+
* any multi-RDB frames into individual single-RDB frames.
|
|
4
|
+
*
|
|
5
|
+
* The input buffer may contain one or more concatenated ADTS frames. Non-ADTS
|
|
6
|
+
* data (or data without a valid sync word) is returned unchanged in a
|
|
7
|
+
* single-element array.
|
|
8
|
+
*
|
|
9
|
+
* For the common case of a single-RDB frame, no allocation occurs — the original
|
|
10
|
+
* buffer region is returned via subarray.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeAdtsFrames(data: Buffer): Buffer[];
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeAdtsFrames = normalizeAdtsFrames;
|
|
4
|
+
const logging_1 = require("../logging");
|
|
5
|
+
// ADTS header: 7 bytes (protection_absent=1) or 9 bytes (protection_absent=0).
|
|
6
|
+
// Byte 6 bits 0-1: number_of_raw_data_blocks_in_frame (0 = 1 RDB, 3 = 4 RDBs).
|
|
7
|
+
// Multi-RDB frames pack several AAC raw data blocks into one ADTS frame.
|
|
8
|
+
// Most decoders (including FFmpeg's native aac) only support single-RDB frames,
|
|
9
|
+
// so we split multi-RDB frames into individual single-RDB ADTS frames.
|
|
10
|
+
const ADTS_SYNC_WORD = 0xfff;
|
|
11
|
+
const ADTS_MIN_HEADER_SIZE = 7;
|
|
12
|
+
/**
|
|
13
|
+
* Returns true when the first two bytes contain an ADTS sync word (0xFFF).
|
|
14
|
+
*/
|
|
15
|
+
function hasAdtsSyncWord(buf, offset) {
|
|
16
|
+
if (offset + 1 >= buf.length)
|
|
17
|
+
return false;
|
|
18
|
+
return ((buf[offset] << 4) | (buf[offset + 1] >> 4)) === ADTS_SYNC_WORD;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extract the 13-bit frame_length field from an ADTS header starting at `offset`.
|
|
22
|
+
* frame_length includes the header itself.
|
|
23
|
+
*/
|
|
24
|
+
function getFrameLength(buf, offset) {
|
|
25
|
+
return ((buf[offset + 3] & 0x03) << 11) | (buf[offset + 4] << 3) | ((buf[offset + 5] >> 5) & 0x07);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Extract the 2-bit number_of_raw_data_blocks_in_frame field (0-indexed, so actual
|
|
29
|
+
* count is value + 1).
|
|
30
|
+
*/
|
|
31
|
+
function getNumRawDataBlocks(buf, offset) {
|
|
32
|
+
return buf[offset + 6] & 0x03;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns true when `protection_absent` is 0 (CRC present).
|
|
36
|
+
*/
|
|
37
|
+
function hasCrc(buf, offset) {
|
|
38
|
+
return (buf[offset + 1] & 0x01) === 0;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Build a single-RDB ADTS frame by prepending a rewritten 7-byte header (without
|
|
42
|
+
* CRC) to a raw data block. The template header is cloned from the original
|
|
43
|
+
* multi-RDB frame and patched:
|
|
44
|
+
* - frame_length → 7 + rdbData.length
|
|
45
|
+
* - num_rdb → 0 (meaning 1 block)
|
|
46
|
+
* - protection_absent → 1 (no CRC, since we don't recompute it)
|
|
47
|
+
*/
|
|
48
|
+
function buildSingleRdbFrame(templateHeader, headerOffset, rdbData) {
|
|
49
|
+
const newLen = ADTS_MIN_HEADER_SIZE + rdbData.length;
|
|
50
|
+
const out = Buffer.allocUnsafe(newLen);
|
|
51
|
+
// Copy the 7-byte header template.
|
|
52
|
+
templateHeader.copy(out, 0, headerOffset, headerOffset + ADTS_MIN_HEADER_SIZE);
|
|
53
|
+
// Set protection_absent = 1 (bit 0 of byte 1).
|
|
54
|
+
out[1] |= 0x01;
|
|
55
|
+
// Rewrite frame_length (13 bits spanning bytes 3-5).
|
|
56
|
+
out[3] = (out[3] & 0xfc) | ((newLen >> 11) & 0x03);
|
|
57
|
+
out[4] = (newLen >> 3) & 0xff;
|
|
58
|
+
out[5] = ((newLen & 0x07) << 5) | (out[5] & 0x1f);
|
|
59
|
+
// Set number_of_raw_data_blocks_in_frame = 0 (bits 0-1 of byte 6).
|
|
60
|
+
out[6] &= 0xfc;
|
|
61
|
+
// Append the raw data block.
|
|
62
|
+
rdbData.copy(out, ADTS_MIN_HEADER_SIZE);
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Split a single multi-RDB ADTS frame into individual single-RDB frames.
|
|
67
|
+
*
|
|
68
|
+
* For multi-RDB frames the spec (ISO 14496-3 §1.A.3) places a
|
|
69
|
+
* `raw_data_block_position` table after the header (and CRC when present) that
|
|
70
|
+
* gives the byte offset of each RDB relative to the start of the first RDB.
|
|
71
|
+
*
|
|
72
|
+
* Layout:
|
|
73
|
+
* [7-byte header]
|
|
74
|
+
* [2-byte CRC] ← only if protection_absent=0
|
|
75
|
+
* [2-byte position] × (N) ← N = number_of_raw_data_blocks_in_frame (i.e. num_rdb)
|
|
76
|
+
* [RDB 0 data ...]
|
|
77
|
+
* [RDB 1 data ...]
|
|
78
|
+
* ...
|
|
79
|
+
*
|
|
80
|
+
* The position entries give byte offsets relative to the start of RDB 0.
|
|
81
|
+
* RDB 0 implicitly starts at offset 0. Position[i] gives the start of RDB i+1.
|
|
82
|
+
*/
|
|
83
|
+
function splitMultiRdbFrame(buf, offset, frameLength) {
|
|
84
|
+
const numRdbMinusOne = getNumRawDataBlocks(buf, offset); // 1-3
|
|
85
|
+
const numRdb = numRdbMinusOne + 1;
|
|
86
|
+
const crcPresent = hasCrc(buf, offset);
|
|
87
|
+
// Calculate where the position table starts.
|
|
88
|
+
let posTableStart = offset + ADTS_MIN_HEADER_SIZE;
|
|
89
|
+
if (crcPresent) {
|
|
90
|
+
posTableStart += 2; // skip CRC-16
|
|
91
|
+
}
|
|
92
|
+
// We need (numRdb - 1) position entries, each 2 bytes.
|
|
93
|
+
const posTableSize = (numRdb - 1) * 2;
|
|
94
|
+
const rdbDataStart = posTableStart + posTableSize;
|
|
95
|
+
if (rdbDataStart > offset + frameLength) {
|
|
96
|
+
logging_1.rootP2PLogger.warn("ADTS multi-RDB frame too short for position table", {
|
|
97
|
+
frameLength,
|
|
98
|
+
numRdb,
|
|
99
|
+
rdbDataStart: rdbDataStart - offset,
|
|
100
|
+
});
|
|
101
|
+
return [buf.subarray(offset, offset + frameLength)];
|
|
102
|
+
}
|
|
103
|
+
// Read position table: offsets relative to the start of RDB 0.
|
|
104
|
+
const rdbOffsets = [0]; // RDB 0 always starts at relative offset 0
|
|
105
|
+
for (let i = 0; i < numRdb - 1; i++) {
|
|
106
|
+
rdbOffsets.push(buf.readUInt16BE(posTableStart + i * 2));
|
|
107
|
+
}
|
|
108
|
+
const rdbSectionEnd = offset + frameLength;
|
|
109
|
+
const result = [];
|
|
110
|
+
for (let i = 0; i < numRdb; i++) {
|
|
111
|
+
const start = rdbDataStart + rdbOffsets[i];
|
|
112
|
+
const end = i < numRdb - 1 ? rdbDataStart + rdbOffsets[i + 1] : rdbSectionEnd;
|
|
113
|
+
if (start >= end || end > rdbSectionEnd) {
|
|
114
|
+
logging_1.rootP2PLogger.warn("ADTS multi-RDB frame has invalid RDB boundaries", {
|
|
115
|
+
rdbIndex: i,
|
|
116
|
+
start: start - offset,
|
|
117
|
+
end: end - offset,
|
|
118
|
+
frameLength,
|
|
119
|
+
});
|
|
120
|
+
// Return remaining data as a single frame rather than losing it.
|
|
121
|
+
if (start < rdbSectionEnd) {
|
|
122
|
+
result.push(buildSingleRdbFrame(buf, offset, buf.subarray(start, rdbSectionEnd)));
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
result.push(buildSingleRdbFrame(buf, offset, buf.subarray(start, end)));
|
|
127
|
+
}
|
|
128
|
+
return result;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Normalize a buffer of ADTS audio data by scanning for ADTS frames and splitting
|
|
132
|
+
* any multi-RDB frames into individual single-RDB frames.
|
|
133
|
+
*
|
|
134
|
+
* The input buffer may contain one or more concatenated ADTS frames. Non-ADTS
|
|
135
|
+
* data (or data without a valid sync word) is returned unchanged in a
|
|
136
|
+
* single-element array.
|
|
137
|
+
*
|
|
138
|
+
* For the common case of a single-RDB frame, no allocation occurs — the original
|
|
139
|
+
* buffer region is returned via subarray.
|
|
140
|
+
*/
|
|
141
|
+
function normalizeAdtsFrames(data) {
|
|
142
|
+
if (data.length < ADTS_MIN_HEADER_SIZE || !hasAdtsSyncWord(data, 0)) {
|
|
143
|
+
// Not ADTS data — return as-is.
|
|
144
|
+
return [data];
|
|
145
|
+
}
|
|
146
|
+
const result = [];
|
|
147
|
+
let pos = 0;
|
|
148
|
+
while (pos + ADTS_MIN_HEADER_SIZE <= data.length) {
|
|
149
|
+
if (!hasAdtsSyncWord(data, pos)) {
|
|
150
|
+
// Lost sync — push remaining bytes and stop.
|
|
151
|
+
logging_1.rootP2PLogger.debug("ADTS sync lost, pushing remaining bytes as-is", {
|
|
152
|
+
offset: pos,
|
|
153
|
+
remaining: data.length - pos,
|
|
154
|
+
});
|
|
155
|
+
result.push(data.subarray(pos));
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
const frameLength = getFrameLength(data, pos);
|
|
159
|
+
if (frameLength < ADTS_MIN_HEADER_SIZE || pos + frameLength > data.length) {
|
|
160
|
+
// Incomplete or invalid frame — push remaining bytes.
|
|
161
|
+
logging_1.rootP2PLogger.debug("ADTS frame length invalid or truncated", {
|
|
162
|
+
offset: pos,
|
|
163
|
+
frameLength,
|
|
164
|
+
available: data.length - pos,
|
|
165
|
+
});
|
|
166
|
+
result.push(data.subarray(pos));
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
const numRdbMinusOne = getNumRawDataBlocks(data, pos);
|
|
170
|
+
if (numRdbMinusOne === 0) {
|
|
171
|
+
// Single RDB — zero-copy, return subarray of original buffer.
|
|
172
|
+
result.push(data.subarray(pos, pos + frameLength));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Multi-RDB — split into individual frames.
|
|
176
|
+
const splitFrames = splitMultiRdbFrame(data, pos, frameLength);
|
|
177
|
+
for (const frame of splitFrames) {
|
|
178
|
+
result.push(frame);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
pos += frameLength;
|
|
182
|
+
}
|
|
183
|
+
return result.length > 0 ? result : [data];
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=adts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adts.js","sourceRoot":"","sources":["../../src/p2p/adts.ts"],"names":[],"mappings":"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"}
|
|
@@ -49,6 +49,7 @@ export interface P2PClientProtocolEvents {
|
|
|
49
49
|
"garage door status": (channel: number, doorId: number, status: number) => void;
|
|
50
50
|
"storage info hb3": (channel: number, storageInfo: StorageInfoBodyHB3) => void;
|
|
51
51
|
"sequence error": (channel: number, command: number, sequence: number, serialnumber: string) => void;
|
|
52
|
+
"hub notify update": () => void;
|
|
52
53
|
}
|
|
53
54
|
export interface P2PQueueMessage {
|
|
54
55
|
p2pCommandType: InternalP2PCommandType;
|
|
@@ -148,6 +149,15 @@ export interface StreamMetadata {
|
|
|
148
149
|
videoHeight: number;
|
|
149
150
|
audioCodec: AudioCodec;
|
|
150
151
|
}
|
|
152
|
+
/** Configurable timeout values for P2P streaming. All values in milliseconds. */
|
|
153
|
+
export interface StreamTimeoutOptions {
|
|
154
|
+
/** Max time to wait for stream data before auto-stopping. Default: 5000ms. */
|
|
155
|
+
streamDataWait?: number;
|
|
156
|
+
/** Time to wait for first audio frame before declaring no audio. Default: 650ms. */
|
|
157
|
+
audioCodecAnalyze?: number;
|
|
158
|
+
/** Max time to wait for an expected sequence number. Default: 20000ms. */
|
|
159
|
+
expectedSeqNoWait?: number;
|
|
160
|
+
}
|
|
151
161
|
export interface DeviceSerial {
|
|
152
162
|
[index: number]: {
|
|
153
163
|
sn: string;
|
package/build/p2p/session.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { TypedEmitter } from "tiny-typed-emitter";
|
|
|
2
2
|
import * as NodeRSA from "node-rsa";
|
|
3
3
|
import { Address, CustomData } from "./models";
|
|
4
4
|
import { CommandType, P2PDataType, P2PConnectionType } from "./types";
|
|
5
|
-
import { P2PClientProtocolEvents, P2PCommand } from "./interfaces";
|
|
5
|
+
import { P2PClientProtocolEvents, P2PCommand, StreamTimeoutOptions } from "./interfaces";
|
|
6
6
|
import { StationListResponse } from "../http/models";
|
|
7
7
|
import { HTTPApi } from "../http/api";
|
|
8
8
|
export declare class P2PClientProtocol extends TypedEmitter<P2PClientProtocolEvents> {
|
|
@@ -24,6 +24,7 @@ export declare class P2PClientProtocol extends TypedEmitter<P2PClientProtocolEve
|
|
|
24
24
|
private readonly ESD_DISCONNECT_TIMEOUT;
|
|
25
25
|
private readonly MAX_STREAM_DATA_WAIT;
|
|
26
26
|
private readonly RESEND_NOT_ACKNOWLEDGED_COMMAND;
|
|
27
|
+
private streamTimeouts;
|
|
27
28
|
private readonly UDP_RECVBUFFERSIZE_BYTES;
|
|
28
29
|
private readonly MAX_PAYLOAD_BYTES;
|
|
29
30
|
private readonly MAX_PACKET_BYTES;
|
|
@@ -162,6 +163,7 @@ export declare class P2PClientProtocol extends TypedEmitter<P2PClientProtocolEve
|
|
|
162
163
|
private emitStreamStopEvent;
|
|
163
164
|
isStreaming(channel: number, datatype: P2PDataType): boolean;
|
|
164
165
|
isLiveStreaming(channel: number): boolean;
|
|
166
|
+
setStreamTimeouts(options: StreamTimeoutOptions): void;
|
|
165
167
|
private isCurrentlyStreaming;
|
|
166
168
|
isRTSPLiveStreaming(channel: number): boolean;
|
|
167
169
|
isDownloading(channel: number): boolean;
|
package/build/p2p/session.js
CHANGED
|
@@ -18,6 +18,7 @@ const ble_1 = require("./ble");
|
|
|
18
18
|
const http_1 = require("../http");
|
|
19
19
|
const utils_3 = require("../utils");
|
|
20
20
|
const logging_1 = require("../logging");
|
|
21
|
+
const adts_1 = require("./adts");
|
|
21
22
|
class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
22
23
|
MAX_RETRIES = 10;
|
|
23
24
|
MAX_COMMAND_RESULT_WAIT = 30 * 1000;
|
|
@@ -37,6 +38,11 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
37
38
|
ESD_DISCONNECT_TIMEOUT = 30 * 1000;
|
|
38
39
|
MAX_STREAM_DATA_WAIT = 5 * 1000;
|
|
39
40
|
RESEND_NOT_ACKNOWLEDGED_COMMAND = 100;
|
|
41
|
+
streamTimeouts = {
|
|
42
|
+
streamDataWait: this.MAX_STREAM_DATA_WAIT,
|
|
43
|
+
audioCodecAnalyze: this.AUDIO_CODEC_ANALYZE_TIMEOUT,
|
|
44
|
+
expectedSeqNoWait: this.MAX_EXPECTED_SEQNO_WAIT,
|
|
45
|
+
};
|
|
40
46
|
UDP_RECVBUFFERSIZE_BYTES = 1048576;
|
|
41
47
|
MAX_PAYLOAD_BYTES = 1028;
|
|
42
48
|
MAX_PACKET_BYTES = 1024;
|
|
@@ -1240,7 +1246,7 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
1240
1246
|
this.currentMessageState[dataType].waitForSeqNoTimeout = setTimeout(() => {
|
|
1241
1247
|
this.endStream(dataType, true);
|
|
1242
1248
|
this.currentMessageState[dataType].waitForSeqNoTimeout = undefined;
|
|
1243
|
-
}, this.
|
|
1249
|
+
}, this.streamTimeouts.expectedSeqNoWait);
|
|
1244
1250
|
if (!this.currentMessageState[dataType].queuedData.get(message.seqNo)) {
|
|
1245
1251
|
this.currentMessageState[dataType].queuedData.set(message.seqNo, message);
|
|
1246
1252
|
logging_1.rootP2PLogger.trace(`Received message - DATA ${types_1.P2PDataType[message.type]} - Received not expected sequence, added to the queue for future processing`, {
|
|
@@ -1514,24 +1520,36 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
1514
1520
|
let return_code = 0;
|
|
1515
1521
|
let resultData;
|
|
1516
1522
|
if (message.bytesToRead > 0) {
|
|
1517
|
-
if (message.signCode > 0) {
|
|
1518
|
-
|
|
1519
|
-
|
|
1523
|
+
if (message.signCode > 0 && message.data.length > 0) {
|
|
1524
|
+
if (message.data.length % 16 === 0) {
|
|
1525
|
+
try {
|
|
1526
|
+
message.data = (0, utils_1.decryptP2PData)(message.data, this.p2pKey);
|
|
1527
|
+
}
|
|
1528
|
+
catch (err) {
|
|
1529
|
+
const error = (0, error_1.ensureError)(err);
|
|
1530
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Decrypt Error`, {
|
|
1531
|
+
error: (0, utils_3.getError)(error),
|
|
1532
|
+
stationSN: this.rawStation.station_sn,
|
|
1533
|
+
message: {
|
|
1534
|
+
seqNo: message.seqNo,
|
|
1535
|
+
channel: message.channel,
|
|
1536
|
+
commandType: types_1.CommandType[message.commandId],
|
|
1537
|
+
signCode: message.signCode,
|
|
1538
|
+
type: message.type,
|
|
1539
|
+
dataType: types_1.P2PDataType[message.dataType],
|
|
1540
|
+
data: message.data.toString("hex"),
|
|
1541
|
+
},
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1520
1544
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Decrypt Error`, {
|
|
1524
|
-
error: (0, utils_3.getError)(error),
|
|
1545
|
+
else {
|
|
1546
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Skipping decryption, data not block-aligned`, {
|
|
1525
1547
|
stationSN: this.rawStation.station_sn,
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
type: message.type,
|
|
1532
|
-
dataType: types_1.P2PDataType[message.dataType],
|
|
1533
|
-
data: message.data.toString("hex"),
|
|
1534
|
-
},
|
|
1548
|
+
seqNo: message.seqNo,
|
|
1549
|
+
commandType: types_1.CommandType[message.commandId],
|
|
1550
|
+
signCode: message.signCode,
|
|
1551
|
+
dataLength: message.data.length,
|
|
1552
|
+
mod16: message.data.length % 16,
|
|
1535
1553
|
});
|
|
1536
1554
|
}
|
|
1537
1555
|
}
|
|
@@ -1770,6 +1788,7 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
1770
1788
|
}
|
|
1771
1789
|
isIFrame(data, isKeyFrame) {
|
|
1772
1790
|
if (this.rawStation.station_sn.startsWith("T8410") ||
|
|
1791
|
+
this.rawStation.station_sn.startsWith("T8417") ||
|
|
1773
1792
|
this.rawStation.station_sn.startsWith("T8400") ||
|
|
1774
1793
|
this.rawStation.station_sn.startsWith("T8401") ||
|
|
1775
1794
|
this.rawStation.station_sn.startsWith("T8411") ||
|
|
@@ -1801,9 +1820,9 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
1801
1820
|
clearTimeout(this.currentMessageState[dataType].p2pStreamingTimeout);
|
|
1802
1821
|
}
|
|
1803
1822
|
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.
|
|
1823
|
+
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.streamTimeouts.streamDataWait / 1000} seconds`);
|
|
1805
1824
|
this.endStream(dataType, sendStopCommand);
|
|
1806
|
-
}, this.
|
|
1825
|
+
}, this.streamTimeouts.streamDataWait);
|
|
1807
1826
|
}
|
|
1808
1827
|
handleDataBinaryAndVideo(message) {
|
|
1809
1828
|
if (!this.currentMessageState[message.dataType].invalidStream) {
|
|
@@ -1883,6 +1902,7 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
1883
1902
|
this.currentMessageState[message.dataType].p2pStreamMetadata.videoWidth = videoMetaData.videoWidth;
|
|
1884
1903
|
if (!this.currentMessageState[message.dataType].p2pStreamFirstVideoDataReceived) {
|
|
1885
1904
|
if (this.rawStation.station_sn.startsWith("T8410") ||
|
|
1905
|
+
this.rawStation.station_sn.startsWith("T8417") ||
|
|
1886
1906
|
this.rawStation.station_sn.startsWith("T8400") ||
|
|
1887
1907
|
this.rawStation.station_sn.startsWith("T8401") ||
|
|
1888
1908
|
this.rawStation.station_sn.startsWith("T8411") ||
|
|
@@ -1972,7 +1992,7 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
1972
1992
|
this.currentMessageState[message.dataType].p2pStreamNotStarted) {
|
|
1973
1993
|
this.emitStreamStartEvent(message.dataType);
|
|
1974
1994
|
}
|
|
1975
|
-
}, this.
|
|
1995
|
+
}, this.streamTimeouts.audioCodecAnalyze);
|
|
1976
1996
|
}
|
|
1977
1997
|
}
|
|
1978
1998
|
if (this.currentMessageState[message.dataType].p2pStreamNotStarted) {
|
|
@@ -2059,7 +2079,18 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
2059
2079
|
this.emitStreamStartEvent(message.dataType);
|
|
2060
2080
|
}
|
|
2061
2081
|
}
|
|
2062
|
-
|
|
2082
|
+
{
|
|
2083
|
+
const codec = this.currentMessageState[message.dataType].p2pStreamMetadata.audioCodec;
|
|
2084
|
+
const stream = this.currentMessageState[message.dataType].audioStream;
|
|
2085
|
+
if (stream && (codec === types_1.AudioCodec.AAC || codec === types_1.AudioCodec.AAC_LC)) {
|
|
2086
|
+
for (const frame of (0, adts_1.normalizeAdtsFrames)(audio_data)) {
|
|
2087
|
+
stream.push(frame);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
else {
|
|
2091
|
+
stream?.push(audio_data);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2063
2094
|
break;
|
|
2064
2095
|
default:
|
|
2065
2096
|
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - Not implemented message`, {
|
|
@@ -2376,8 +2407,17 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
2376
2407
|
this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_BATTERY_LEVEL, payload.slBattery);
|
|
2377
2408
|
this.emit("parameter", message.channel, types_1.CommandType.CMD_SMARTLOCK_QUERY_STATUS, payload.slState);
|
|
2378
2409
|
}
|
|
2410
|
+
else if (json.cmd === types_1.CommandType.CMD_HUB_NOTIFY_UPDATE) {
|
|
2411
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Homebase notify update`, {
|
|
2412
|
+
stationSN: this.rawStation.station_sn,
|
|
2413
|
+
commandIdName: types_1.CommandType[json.cmd],
|
|
2414
|
+
commandId: json.cmd,
|
|
2415
|
+
message: data.toString(),
|
|
2416
|
+
});
|
|
2417
|
+
this.emit("hub notify update");
|
|
2418
|
+
}
|
|
2379
2419
|
else {
|
|
2380
|
-
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Not implemented`, {
|
|
2420
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Not implemented 1`, {
|
|
2381
2421
|
stationSN: this.rawStation.station_sn,
|
|
2382
2422
|
commandIdName: types_1.CommandType[json.cmd],
|
|
2383
2423
|
commandId: json.cmd,
|
|
@@ -2424,9 +2464,10 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
2424
2464
|
device_1.Device.isLockWifiT8502(this.rawStation.devices[0]?.device_type) ||
|
|
2425
2465
|
device_1.Device.isLockWifiT8510P(this.rawStation.devices[0]?.device_type, this.rawStation.devices[0]?.device_sn) ||
|
|
2426
2466
|
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
|
|
2467
|
+
device_1.Device.isLockWifiT85V0(this.rawStation.devices[0]?.device_type) ||
|
|
2428
2468
|
device_1.Device.isLockWifiT8531(this.rawStation.devices[0]?.device_type) ||
|
|
2429
|
-
device_1.Device.isLockWifiT85L0(this.rawStation.devices[0]?.device_type)
|
|
2469
|
+
device_1.Device.isLockWifiT85L0(this.rawStation.devices[0]?.device_type) ||
|
|
2470
|
+
device_1.Device.isLockWifiT85P0(this.rawStation.devices[0]?.device_type)) {
|
|
2430
2471
|
this.emit("sequence error", message.channel, types_1.SmartLockCommand[payload.bus_type == types_1.SmartLockFunctionType.TYPE_2
|
|
2431
2472
|
? types_1.SmartLockBleCommandFunctionType2[payload.lock_cmd]
|
|
2432
2473
|
: types_1.SmartLockBleCommandFunctionType1[payload.lock_cmd]], payload.seq_num, payload.dev_sn);
|
|
@@ -2842,8 +2883,29 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
2842
2883
|
this.emit("storage info hb3", message.channel, payload.body);
|
|
2843
2884
|
}
|
|
2844
2885
|
}
|
|
2886
|
+
else if (json.cmd === 6246) {
|
|
2887
|
+
const payload = json.payload;
|
|
2888
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD Livestream status`, { stationSN: this.rawStation.station_sn, payload: payload });
|
|
2889
|
+
if (payload?.num !== undefined) {
|
|
2890
|
+
if (payload.num > 0) {
|
|
2891
|
+
this.emit("rtsp livestream started", message.channel);
|
|
2892
|
+
}
|
|
2893
|
+
else {
|
|
2894
|
+
this.emit("rtsp livestream stopped", message.channel);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
else if (json.cmd === types_1.CommandType.CMD_HUB_NOTIFY_UPDATE) {
|
|
2899
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Homebase notify update`, {
|
|
2900
|
+
stationSN: this.rawStation.station_sn,
|
|
2901
|
+
commandIdName: types_1.CommandType[json.cmd],
|
|
2902
|
+
commandId: json.cmd,
|
|
2903
|
+
message: data.toString(),
|
|
2904
|
+
});
|
|
2905
|
+
this.emit("hub notify update");
|
|
2906
|
+
}
|
|
2845
2907
|
else {
|
|
2846
|
-
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Not implemented`, {
|
|
2908
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_NOTIFY_PAYLOAD - Not implemented 2`, {
|
|
2847
2909
|
stationSN: this.rawStation.station_sn,
|
|
2848
2910
|
commandIdName: types_1.CommandType[json.cmd],
|
|
2849
2911
|
commandId: json.cmd,
|
|
@@ -3212,7 +3274,10 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
3212
3274
|
data: data.toString("hex"),
|
|
3213
3275
|
cipherID: cipherID,
|
|
3214
3276
|
});
|
|
3215
|
-
|
|
3277
|
+
// Keep full raw buffer for ECDH — readNullTerminatedBuffer truncates binary ECIES envelopes at 0x00 bytes
|
|
3278
|
+
const rawEncryptedKey = data.subarray(4);
|
|
3279
|
+
const encryptedKey = (0, utils_1.readNullTerminatedBuffer)(rawEncryptedKey);
|
|
3280
|
+
const isECDHDevice = this.rawStation.station_sn.startsWith("T8214") || this.rawStation.station_sn.startsWith("T8425");
|
|
3216
3281
|
this.api
|
|
3217
3282
|
.getCipher(/*this.rawStation.station_sn, */ cipherID, this.rawStation.member.admin_user_id)
|
|
3218
3283
|
.then((cipher) => {
|
|
@@ -3224,10 +3289,49 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
3224
3289
|
cipher: JSON.stringify(cipher),
|
|
3225
3290
|
});
|
|
3226
3291
|
if (cipher !== undefined) {
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3292
|
+
// Try RSA first
|
|
3293
|
+
try {
|
|
3294
|
+
this.encryption = types_1.EncryptionType.LEVEL_2;
|
|
3295
|
+
const rsa = (0, utils_1.getRSAPrivateKey)(cipher.private_key, this.enableEmbeddedPKCS1Support);
|
|
3296
|
+
this.p2pKey = rsa.decrypt(encryptedKey);
|
|
3297
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - RSA success - set encryption level 2`, { stationSN: this.rawStation.station_sn, key: this.p2pKey.toString("hex") });
|
|
3298
|
+
}
|
|
3299
|
+
catch (rsaErr) {
|
|
3300
|
+
const rsaError = (0, error_1.ensureError)(rsaErr);
|
|
3301
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - RSA decrypt failed`, {
|
|
3302
|
+
error: (0, utils_3.getError)(rsaError),
|
|
3303
|
+
stationSN: this.rawStation.station_sn,
|
|
3304
|
+
isECDHDevice: isECDHDevice,
|
|
3305
|
+
hasEccKey: !!cipher.ecc_private_key,
|
|
3306
|
+
});
|
|
3307
|
+
// Try ECDH only for known ECDH devices (T8214/T8425)
|
|
3308
|
+
if (isECDHDevice && cipher.ecc_private_key) {
|
|
3309
|
+
try {
|
|
3310
|
+
this.encryption = types_1.EncryptionType.LEVEL_2;
|
|
3311
|
+
this.p2pKey = (0, utils_1.decryptP2PKeyECDH)(rawEncryptedKey, cipher.ecc_private_key);
|
|
3312
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - ECDH success - set encryption level 2`, {
|
|
3313
|
+
stationSN: this.rawStation.station_sn,
|
|
3314
|
+
key: this.p2pKey.toString("hex"),
|
|
3315
|
+
keyLength: this.p2pKey.length,
|
|
3316
|
+
});
|
|
3317
|
+
}
|
|
3318
|
+
catch (ecdhErr) {
|
|
3319
|
+
const ecdhError = (0, error_1.ensureError)(ecdhErr);
|
|
3320
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - ECDH also failed, falling back to Level 1`, {
|
|
3321
|
+
error: (0, utils_3.getError)(ecdhError),
|
|
3322
|
+
stationSN: this.rawStation.station_sn,
|
|
3323
|
+
});
|
|
3324
|
+
this.encryption = types_1.EncryptionType.LEVEL_1;
|
|
3325
|
+
this.p2pKey = Buffer.from((0, utils_1.getP2PCommandEncryptionKey)(this.rawStation.station_sn, this.rawStation.p2p_did));
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
else {
|
|
3329
|
+
// Non-ECDH device or no ECC key — fall back to Level 1
|
|
3330
|
+
this.encryption = types_1.EncryptionType.LEVEL_1;
|
|
3331
|
+
this.p2pKey = Buffer.from((0, utils_1.getP2PCommandEncryptionKey)(this.rawStation.station_sn, this.rawStation.p2p_did));
|
|
3332
|
+
logging_1.rootP2PLogger.debug(`Handle DATA ${types_1.P2PDataType[message.dataType]} - CMD_GATEWAYINFO - RSA failed, set encryption level 1`, { stationSN: this.rawStation.station_sn, key: this.p2pKey.toString("hex") });
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3231
3335
|
}
|
|
3232
3336
|
else {
|
|
3233
3337
|
this.encryption = types_1.EncryptionType.LEVEL_1;
|
|
@@ -3519,6 +3623,9 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
3519
3623
|
isLiveStreaming(channel) {
|
|
3520
3624
|
return this.isStreaming(channel, types_1.P2PDataType.VIDEO);
|
|
3521
3625
|
}
|
|
3626
|
+
setStreamTimeouts(options) {
|
|
3627
|
+
this.streamTimeouts = { ...this.streamTimeouts, ...options };
|
|
3628
|
+
}
|
|
3522
3629
|
isCurrentlyStreaming() {
|
|
3523
3630
|
for (const element of Object.values(this.currentMessageState)) {
|
|
3524
3631
|
if (element.p2pStreaming || element.p2pTalkback)
|
|
@@ -3710,6 +3817,7 @@ class P2PClientProtocol extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
3710
3817
|
return false;
|
|
3711
3818
|
}
|
|
3712
3819
|
startTalkback(channel = 0) {
|
|
3820
|
+
(0, utils_1.resetTalkbackCounters)();
|
|
3713
3821
|
this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkback = true;
|
|
3714
3822
|
this.currentMessageState[types_1.P2PDataType.VIDEO].p2pTalkbackChannel = channel;
|
|
3715
3823
|
this.initializeTalkbackStream(channel);
|