@mega-yfue/eufy-sdk 0.5.0-beta.84 → 0.5.0-beta.86
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/dist/index.js
CHANGED
|
@@ -5347,6 +5347,8 @@ var CAMERA_CMD = {
|
|
|
5347
5347
|
* (1013). (Some models omit full colour; enum in {@link NightVision}.)
|
|
5348
5348
|
*/
|
|
5349
5349
|
NIGHT_VISION_TYPE: 1277,
|
|
5350
|
+
/** T8442 infrared night vision on/off: direct L1 int-string, hardware verified (0/1). */
|
|
5351
|
+
IC_NIGHT_VISION_TYPE: 1013,
|
|
5350
5352
|
/**
|
|
5351
5353
|
* Push-notification STYLE — how much a detection push carries: text alone, a thumbnail, or text
|
|
5352
5354
|
* followed by a thumbnail. App `CMD_INDOOR_PUSH_NOTIFY_TYPE`, and the "indoor" is a misnomer: the
|
|
@@ -5537,6 +5539,9 @@ function powerValue(on, ctx) {
|
|
|
5537
5539
|
function isT8410HomeBase3(ctx) {
|
|
5538
5540
|
return ctx.model === "T8410" && ctx.stationSerial?.startsWith("T8030") === true;
|
|
5539
5541
|
}
|
|
5542
|
+
function isT8442(ctx) {
|
|
5543
|
+
return ctx.model === "T8442";
|
|
5544
|
+
}
|
|
5540
5545
|
function usesHomeBasePowerPayload(ctx) {
|
|
5541
5546
|
if (!isT8410HomeBase3(ctx) || !ctx.firmwareVersion || !/^\d+(?:\.\d+){2,3}$/.test(ctx.firmwareVersion))
|
|
5542
5547
|
return false;
|
|
@@ -5758,12 +5763,22 @@ var CAMERA_MEMBERS = {
|
|
|
5758
5763
|
type: "enum",
|
|
5759
5764
|
kind: "enum",
|
|
5760
5765
|
enumValues: { 0: "Off", 1: "Infrared", 2: "Full Color" },
|
|
5766
|
+
enumValuesFor: (ctx) => isT8442(ctx) ? { 0: "Off", 1: "Infrared" } : void 0,
|
|
5761
5767
|
provenance: "verified",
|
|
5762
5768
|
available: isCameraCodec,
|
|
5763
|
-
|
|
5769
|
+
readAvailable: (ctx) => !isT8442(ctx),
|
|
5770
|
+
readAliases: [{ paramType: 2002, available: isT8442 }],
|
|
5771
|
+
description: "Night-vision mode: 0 = off, 1 = infrared (B&W), 2 = full colour where supported. T8425 uses NIGHT_VISION_TYPE 1277 (1350 SET_PAYLOAD, night_sion); T8442 reports 2002 and writes direct IC_NIGHT_VISION_TYPE 1013 (int-string, 0/1). Both wires verified live.",
|
|
5764
5772
|
write: (v, ctx) => {
|
|
5765
5773
|
const nv = coerceEnumValue(NightVision, v);
|
|
5766
|
-
|
|
5774
|
+
if (nv == null)
|
|
5775
|
+
return void 0;
|
|
5776
|
+
if (isT8442(ctx)) {
|
|
5777
|
+
if (nv === NightVision.FullColor)
|
|
5778
|
+
throw new Error("nightVision: Full Color is not supported on T8442 (only Off/Infrared)");
|
|
5779
|
+
return setScalar(CAMERA_CMD.IC_NIGHT_VISION_TYPE, nv, ctx, "int-string");
|
|
5780
|
+
}
|
|
5781
|
+
return setPayload(CAMERA_CMD.NIGHT_VISION_TYPE, { channel: ctx.channel, night_sion: nv }, ctx, 0, 0, "auto");
|
|
5767
5782
|
}
|
|
5768
5783
|
},
|
|
5769
5784
|
/**
|
|
@@ -16227,6 +16242,10 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16227
16242
|
* change (the level-2 key arrived after an initial level-1 start), otherwise sends the 1139 nudge.
|
|
16228
16243
|
*/
|
|
16229
16244
|
liveStartedChannels = /* @__PURE__ */ new Map();
|
|
16245
|
+
/** The `cipher_id` this connection's `CMD_GATEWAYINFO` reply named, once its negotiation has read it. */
|
|
16246
|
+
gatewayCipherId;
|
|
16247
|
+
/** The `n` of the last `[0x01, n]` header this connection sent in the clear — see {@link nextClearCommandBytes}. */
|
|
16248
|
+
clearCommandCount = 0;
|
|
16230
16249
|
unackedLiveStarts = /* @__PURE__ */ new Map();
|
|
16231
16250
|
liveStartRetransmitTimer;
|
|
16232
16251
|
logger;
|
|
@@ -16424,6 +16443,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16424
16443
|
this.level2Negotiating = true;
|
|
16425
16444
|
const generation = this.connectionGeneration;
|
|
16426
16445
|
const cipherId = gatewayInfoCipherId(gwPayload);
|
|
16446
|
+
this.gatewayCipherId = cipherId;
|
|
16427
16447
|
this.trace({ phase: "level2-negotiating", cipherId });
|
|
16428
16448
|
void (async () => {
|
|
16429
16449
|
try {
|
|
@@ -16530,6 +16550,8 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16530
16550
|
this.level2Pending = this.cfg.resolveCipherKey !== void 0;
|
|
16531
16551
|
this.level2Negotiating = false;
|
|
16532
16552
|
this.level2Reprompted = false;
|
|
16553
|
+
this.gatewayCipherId = void 0;
|
|
16554
|
+
this.clearCommandCount = 0;
|
|
16533
16555
|
}
|
|
16534
16556
|
const socket = dgram.createSocket("udp4");
|
|
16535
16557
|
this.socket = socket;
|
|
@@ -16820,7 +16842,7 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16820
16842
|
this.sendMediaPayloadLevel2(CMD_START_REALTIME_MEDIA, channel, accountId, {});
|
|
16821
16843
|
return;
|
|
16822
16844
|
}
|
|
16823
|
-
const want = this.level2Key ? "l2" : "l1";
|
|
16845
|
+
const want = this.level2Key ? "l2" : this.sendsInTheClear() ? "clear" : "l1";
|
|
16824
16846
|
if (opts?.force) {
|
|
16825
16847
|
for (const pending of this.unackedLiveStarts.values())
|
|
16826
16848
|
if (pending.channel === channel)
|
|
@@ -16850,8 +16872,17 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16850
16872
|
* `video_type` is 12, and `transaction` is the millisecond timestamp as a string. The device answers a
|
|
16851
16873
|
* start carrying these values with a keyframe; `encryptkey` is the modulus it RSA-wraps each keyframe's
|
|
16852
16874
|
* AES media key with (unwrapped by {@link decodeVideoFrame}).
|
|
16875
|
+
*
|
|
16876
|
+
* With `clearMsgId`, the body of a start sent in the clear: `{accountId, encryptkey, msg_id, streamtype: 1}`
|
|
16877
|
+
* only, `msg_id` being `clearMsgId`, which changes from one start to the next.
|
|
16853
16878
|
*/
|
|
16854
|
-
startLiveJson(channel, accountId) {
|
|
16879
|
+
startLiveJson(channel, accountId, clearMsgId) {
|
|
16880
|
+
if (clearMsgId !== void 0) {
|
|
16881
|
+
return Buffer.from(JSON.stringify({
|
|
16882
|
+
commandType: CMD_START_LIVE,
|
|
16883
|
+
data: { accountId, encryptkey: this.rsaModulus(), msg_id: clearMsgId, streamtype: 1 }
|
|
16884
|
+
}), "utf-8");
|
|
16885
|
+
}
|
|
16855
16886
|
const now2 = Date.now();
|
|
16856
16887
|
return Buffer.from(JSON.stringify({
|
|
16857
16888
|
commandType: CMD_START_LIVE,
|
|
@@ -16882,25 +16913,31 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16882
16913
|
* (GCM, signCode 8, frame type 10) when the session has a negotiated key, else level-1 (AES-128-ECB,
|
|
16883
16914
|
* signCode 1, frame type 11) — chosen by the session key, not the device family. The camera RSA-wraps
|
|
16884
16915
|
* each keyframe's AES media key with the `encryptkey` modulus (unwrapped by {@link decodeVideoFrame}).
|
|
16916
|
+
* A session that {@link sendsInTheClear} sends it unencrypted instead: signCode 0, frame type 0, header
|
|
16917
|
+
* bytes `[0x01, n]` from {@link nextClearCommandBytes}, and `n` as the body's `msg_id`.
|
|
16885
16918
|
*/
|
|
16886
16919
|
sendStartLiveOwnSession(channel, accountId) {
|
|
16887
16920
|
if (!this.connectAddress)
|
|
16888
16921
|
return;
|
|
16889
|
-
const plain = this.startLiveJson(channel, accountId);
|
|
16890
16922
|
let payload;
|
|
16891
16923
|
let signCode;
|
|
16892
16924
|
let magic;
|
|
16893
16925
|
let frameType;
|
|
16894
16926
|
if (this.level2Key) {
|
|
16895
|
-
const enc2 = this.encryptLevel2(
|
|
16927
|
+
const enc2 = this.encryptLevel2(this.startLiveJson(channel, accountId));
|
|
16896
16928
|
if (!enc2)
|
|
16897
16929
|
return;
|
|
16898
16930
|
payload = enc2;
|
|
16899
16931
|
signCode = 8;
|
|
16900
16932
|
magic = [8, 0];
|
|
16901
16933
|
frameType = START_LIVE_FRAME_TYPE_L2;
|
|
16934
|
+
} else if (this.sendsInTheClear()) {
|
|
16935
|
+
magic = this.nextClearCommandBytes();
|
|
16936
|
+
payload = this.startLiveJson(channel, accountId, magic[1]);
|
|
16937
|
+
signCode = 0;
|
|
16938
|
+
frameType = 0;
|
|
16902
16939
|
} else {
|
|
16903
|
-
payload = encryptP2PData(paddingP2PData(
|
|
16940
|
+
payload = encryptP2PData(paddingP2PData(this.startLiveJson(channel, accountId)), this.level1Key);
|
|
16904
16941
|
signCode = 1;
|
|
16905
16942
|
magic = [1, 0];
|
|
16906
16943
|
frameType = START_LIVE_FRAME_TYPE_L1;
|
|
@@ -16920,6 +16957,21 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
16920
16957
|
this.armLiveStartRetransmit();
|
|
16921
16958
|
this.send(this.connectAddress, RequestMessageType.DATA, data);
|
|
16922
16959
|
}
|
|
16960
|
+
/**
|
|
16961
|
+
* Whether this session's own-session start and stop go in the clear: its `CMD_GATEWAYINFO` reply named
|
|
16962
|
+
* `cipher_id` 0.
|
|
16963
|
+
*/
|
|
16964
|
+
sendsInTheClear() {
|
|
16965
|
+
return this.gatewayCipherId === 0;
|
|
16966
|
+
}
|
|
16967
|
+
/**
|
|
16968
|
+
* Header bytes 10-11 of this connection's next command sent in the clear: `[0x01, n]`, with `n` counting
|
|
16969
|
+
* those commands from 1 and never 0. A retransmit repeats its `n`, and the device's reply echoes it.
|
|
16970
|
+
*/
|
|
16971
|
+
nextClearCommandBytes() {
|
|
16972
|
+
this.clearCommandCount = this.clearCommandCount % 255 + 1;
|
|
16973
|
+
return [1, this.clearCommandCount];
|
|
16974
|
+
}
|
|
16923
16975
|
/**
|
|
16924
16976
|
* Repeat unacknowledged own-session live starts until the device takes one. Runs only while a start is
|
|
16925
16977
|
* outstanding.
|
|
@@ -17082,6 +17134,8 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
17082
17134
|
* `uint32`, the same 4-byte body as the attached talkback frames. The app sends this frame on closing
|
|
17083
17135
|
* an own-session camera's live view; the camera stops streaming on it within a second, and keeps
|
|
17084
17136
|
* streaming through the 1350-wrapped form.
|
|
17137
|
+
* - own-session on a session that {@link sendsInTheClear}: the same 4-byte body unencrypted, signCode 0,
|
|
17138
|
+
* header bytes from {@link nextClearCommandBytes}.
|
|
17085
17139
|
* - without a level-2 key: the bare 1004 command.
|
|
17086
17140
|
*/
|
|
17087
17141
|
stopLiveMedia(channel = STATION_CHANNEL3, accountId = "", homeBaseAttached = false) {
|
|
@@ -17094,12 +17148,19 @@ var P2PSession = class _P2PSession extends EventEmitter2 {
|
|
|
17094
17148
|
clearInterval(this.liveStartRetransmitTimer);
|
|
17095
17149
|
this.liveStartRetransmitTimer = void 0;
|
|
17096
17150
|
}
|
|
17151
|
+
const body = Buffer.allocUnsafe(4);
|
|
17152
|
+
body.writeUInt32LE(channel >>> 0, 0);
|
|
17097
17153
|
if (this.level2Key && !homeBaseAttached) {
|
|
17098
|
-
const body = Buffer.allocUnsafe(4);
|
|
17099
|
-
body.writeUInt32LE(channel >>> 0, 0);
|
|
17100
17154
|
this.sendRawLevel2Bytes(body, channel, CMD_STOP_REALTIME_MEDIA, 8);
|
|
17101
17155
|
} else if (this.level2Key) {
|
|
17102
17156
|
this.sendMediaPayloadLevel2(CMD_STOP_REALTIME_MEDIA, channel, accountId, {});
|
|
17157
|
+
} else if (!homeBaseAttached && this.sendsInTheClear() && this.connectAddress) {
|
|
17158
|
+
const data = Buffer.concat([
|
|
17159
|
+
buildCommandHeader(this.seqNumber, CMD_STOP_REALTIME_MEDIA),
|
|
17160
|
+
buildRawCommandPayload(body, channel, 0, this.nextClearCommandBytes(), 0)
|
|
17161
|
+
]);
|
|
17162
|
+
this.seqNumber = this.seqNumber + 1 & 65535;
|
|
17163
|
+
this.send(this.connectAddress, RequestMessageType.DATA, data);
|
|
17103
17164
|
} else {
|
|
17104
17165
|
this.sendCommand(CMD_STOP_REALTIME_MEDIA, channel);
|
|
17105
17166
|
}
|