@mega-yfue/eufy-sdk 0.5.0-beta.70 → 0.5.0-beta.71
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/core/contracts.d.ts +1 -0
- package/dist/index.js +83 -12
- package/dist/index.js.map +2 -2
- package/dist/model/capabilities/lock.d.ts +21 -7
- package/dist/model/capabilities/types.d.ts +4 -0
- package/dist/model/device-family.d.ts +2 -0
- package/dist/transport/p2p/command-router.d.ts +1 -0
- package/package.json +1 -1
package/dist/core/contracts.d.ts
CHANGED
|
@@ -883,6 +883,7 @@ export interface MediaProvider {
|
|
|
883
883
|
*/
|
|
884
884
|
p2pQuery?(subCmd: number, opts?: {
|
|
885
885
|
timeoutMs?: number;
|
|
886
|
+
payload?: Record<string, unknown>;
|
|
886
887
|
}): Promise<Record<string, unknown>>;
|
|
887
888
|
/**
|
|
888
889
|
* Generic control-payload request/reply query: send a `CONTROL_PAYLOAD` (1700) `{commandType,data}`
|
package/dist/index.js
CHANGED
|
@@ -3658,6 +3658,7 @@ var isNvrT8N00 = (ctx) => ctx.model === "T8N00";
|
|
|
3658
3658
|
var isDoorbellT8223 = (ctx) => ctx.model === "T8223";
|
|
3659
3659
|
var isAiotVacuum = (ctx) => ctx.category !== "eufy_home_tuya";
|
|
3660
3660
|
var isTuyaVacuum = (ctx) => ctx.category === "eufy_home_tuya";
|
|
3661
|
+
var isVideoSmartLock = (ctx) => ctx.deviceType === DeviceType.LOCK_8530;
|
|
3661
3662
|
|
|
3662
3663
|
// dist/model/capabilities/rtsp.js
|
|
3663
3664
|
var RTSP_PARAM = {
|
|
@@ -7434,9 +7435,24 @@ var LOCK_SETTING_ID = {
|
|
|
7434
7435
|
/** One-touch rear-lock toggle. Structurally confirmed via the app's own JS (2026-07-18) — see the module doc. */
|
|
7435
7436
|
ONE_TOUCH_REAR_LOCK: 11
|
|
7436
7437
|
};
|
|
7438
|
+
var VIDEO_LOCK_CMD = { ACTUATE: 1961, STATUS: 1955, NOTIFY: 1351 };
|
|
7437
7439
|
function actuate(engage, ctx, sink) {
|
|
7438
|
-
if (!ctx.adminUserId || !ctx.shortUserId) {
|
|
7439
|
-
return Promise.reject(new Error(`lock: missing member identity (admin_user_id/short_user_id) [${describeDevice(ctx)}]`));
|
|
7440
|
+
if (!ctx.adminUserId || !ctx.shortUserId || isVideoSmartLock(ctx) && !ctx.accountName) {
|
|
7441
|
+
return Promise.reject(new Error(`lock: missing member identity (admin_user_id/short_user_id, and user name on video locks) [${describeDevice(ctx)}]`));
|
|
7442
|
+
}
|
|
7443
|
+
if (isVideoSmartLock(ctx)) {
|
|
7444
|
+
return sink.dispatch({
|
|
7445
|
+
kind: "set-payload",
|
|
7446
|
+
cmd: VIDEO_LOCK_CMD.ACTUATE,
|
|
7447
|
+
channel: ctx.channel,
|
|
7448
|
+
mValue3: 0,
|
|
7449
|
+
payload: {
|
|
7450
|
+
shortUserId: ctx.shortUserId,
|
|
7451
|
+
slOperation: engage ? 1 : 0,
|
|
7452
|
+
userId: ctx.adminUserId,
|
|
7453
|
+
userName: ctx.accountName
|
|
7454
|
+
}
|
|
7455
|
+
});
|
|
7440
7456
|
}
|
|
7441
7457
|
return sink.dispatch({
|
|
7442
7458
|
kind: "ff09-actuate",
|
|
@@ -7459,7 +7475,31 @@ function settingToggle(settingId, name, enabled, ctx, sink) {
|
|
|
7459
7475
|
value: enabled
|
|
7460
7476
|
});
|
|
7461
7477
|
}
|
|
7462
|
-
var overP2p = (ctx) => ctx.hasP2p === true;
|
|
7478
|
+
var overP2p = (ctx) => ctx.hasP2p === true && !isVideoSmartLock(ctx);
|
|
7479
|
+
function actuationAvailable(ctx) {
|
|
7480
|
+
return !isVideoSmartLock(ctx) || ctx.stationSerial?.startsWith("T8030") === true;
|
|
7481
|
+
}
|
|
7482
|
+
async function refreshStatus(query, timezone) {
|
|
7483
|
+
const payload = await query(VIDEO_LOCK_CMD.STATUS, {
|
|
7484
|
+
payload: { timezone }
|
|
7485
|
+
});
|
|
7486
|
+
if (payload.code !== 0)
|
|
7487
|
+
throw new Error(`lock.refreshStatus: device result ${String(payload.code)}`);
|
|
7488
|
+
return nativeState(payload.slState);
|
|
7489
|
+
}
|
|
7490
|
+
function nativeState(raw) {
|
|
7491
|
+
if (raw === "3")
|
|
7492
|
+
return false;
|
|
7493
|
+
if (raw === "4")
|
|
7494
|
+
return true;
|
|
7495
|
+
return void 0;
|
|
7496
|
+
}
|
|
7497
|
+
function nativeReport(signal) {
|
|
7498
|
+
if (signal.source !== "p2p-frame" || !isVideoSmartLock(signal) || signal.commandId !== VIDEO_LOCK_CMD.NOTIFY || signal.json?.cmd !== VIDEO_LOCK_CMD.STATUS)
|
|
7499
|
+
return void 0;
|
|
7500
|
+
const report = signal.json.payload;
|
|
7501
|
+
return report?.code === 0 ? report : void 0;
|
|
7502
|
+
}
|
|
7463
7503
|
var LOCK_MEMBERS = {
|
|
7464
7504
|
/**
|
|
7465
7505
|
* The lock's own state, verified on param 6000: "4" = locked, "3" = unlocked.
|
|
@@ -7501,10 +7541,10 @@ var LOCK_MEMBERS = {
|
|
|
7501
7541
|
provenance: "verified",
|
|
7502
7542
|
description: "Lock signal strength (verified: param 1141 = RSSI)."
|
|
7503
7543
|
},
|
|
7504
|
-
/** Lock the deadbolt/door.
|
|
7505
|
-
lock: method(({ ctx, sink }) => () => actuate(true, ctx, sink), "Lock the deadbolt/door."),
|
|
7506
|
-
/** Unlock the deadbolt/door.
|
|
7507
|
-
unlock: method(({ ctx, sink }) => () => actuate(false, ctx, sink), "Unlock the deadbolt/door."),
|
|
7544
|
+
/** Lock the deadbolt/door. Resolves after sending; does not confirm physical state. */
|
|
7545
|
+
lock: method(({ ctx, sink }) => () => actuate(true, ctx, sink), "Lock the deadbolt/door.", actuationAvailable),
|
|
7546
|
+
/** Unlock the deadbolt/door. Resolves after sending; does not confirm physical state. */
|
|
7547
|
+
unlock: method(({ ctx, sink }) => () => actuate(false, ctx, sink), "Unlock the deadbolt/door.", actuationAvailable),
|
|
7508
7548
|
/**
|
|
7509
7549
|
* Read-modify-write the auto-lock setting: the transport GETs the device's current settings, changes
|
|
7510
7550
|
* only `enabled` (+ `delaySeconds` if given — otherwise the current delay is preserved), and writes the
|
|
@@ -7525,7 +7565,7 @@ var LOCK_MEMBERS = {
|
|
|
7525
7565
|
enabled,
|
|
7526
7566
|
delaySeconds
|
|
7527
7567
|
});
|
|
7528
|
-
}, "Read-modify-write the auto-lock setting, preserving the fields it does not change."),
|
|
7568
|
+
}, "Read-modify-write the auto-lock setting, preserving the fields it does not change.", (ctx) => !isVideoSmartLock(ctx)),
|
|
7529
7569
|
/**
|
|
7530
7570
|
* Toggle Rain Mode — a pure blind write, unlike `setAutoLock`: the compact frame carries only this one
|
|
7531
7571
|
* field, so there is no GET pass and it is fire-and-forget. Confirmed on-device end-to-end in both
|
|
@@ -7623,6 +7663,15 @@ var LOCK_MEMBERS = {
|
|
|
7623
7663
|
description: "One-touch rear locking (setting-id 11). Wire shape confirmed in the app's own JS, NOT captured.",
|
|
7624
7664
|
write: (v, ctx) => settingToggleCommand(LOCK_SETTING_ID.ONE_TOUCH_REAR_LOCK, v, ctx)
|
|
7625
7665
|
},
|
|
7666
|
+
/** Read the current device-reported state; undefined means no settled locked/unlocked state. */
|
|
7667
|
+
refreshStatus: {
|
|
7668
|
+
...provided("media", (media, { ctx }) => {
|
|
7669
|
+
const query = media.p2pQuery?.bind(media);
|
|
7670
|
+
const timezone = ctx.timezone;
|
|
7671
|
+
return isVideoSmartLock(ctx) && timezone && query ? () => refreshStatus(query, timezone) : void 0;
|
|
7672
|
+
}, "Read the current lock state from the device."),
|
|
7673
|
+
answers: true
|
|
7674
|
+
},
|
|
7626
7675
|
/**
|
|
7627
7676
|
* Read the device's current auto-lock settings via a live `GET_SETTINGS` round-trip — the SAME read
|
|
7628
7677
|
* `setAutoLock` does internally, exposed standalone with no write attached. Works over BOTH transports.
|
|
@@ -7632,7 +7681,7 @@ var LOCK_MEMBERS = {
|
|
|
7632
7681
|
* though it takes no arguments — offering it as one would run a round-trip and discard the answer.
|
|
7633
7682
|
*/
|
|
7634
7683
|
getAutoLockState: {
|
|
7635
|
-
...provided("ff09Settings", (f) => () => f.getAutoLockState(), "Read the current auto-lock settings via a live round-trip."),
|
|
7684
|
+
...provided("ff09Settings", (f, { ctx }) => isVideoSmartLock(ctx) ? void 0 : () => f.getAutoLockState(), "Read the current auto-lock settings via a live round-trip."),
|
|
7636
7685
|
answers: true
|
|
7637
7686
|
}
|
|
7638
7687
|
};
|
|
@@ -7659,6 +7708,17 @@ var LOCK = {
|
|
|
7659
7708
|
description: "Smart-lock locked/unlocked state and battery.",
|
|
7660
7709
|
members: LOCK_MEMBERS,
|
|
7661
7710
|
properties: propertiesOf(LOCK_MEMBERS),
|
|
7711
|
+
decodeState(signal) {
|
|
7712
|
+
const report = nativeReport(signal);
|
|
7713
|
+
if (!report)
|
|
7714
|
+
return null;
|
|
7715
|
+
const params = {};
|
|
7716
|
+
if (nativeState(report.slState) !== void 0)
|
|
7717
|
+
params[6e3] = String(report.slState);
|
|
7718
|
+
if (report.slBattery !== void 0)
|
|
7719
|
+
params[6001] = String(report.slBattery);
|
|
7720
|
+
return { params };
|
|
7721
|
+
},
|
|
7662
7722
|
// Lock state is reported via param 6000 (verified: 4=locked, 3=unlocked); the model name (lock/safe) is a
|
|
7663
7723
|
// signal, and every lock-codec device has the lock capability as its baseline.
|
|
7664
7724
|
detection: { evidenceParams: [6e3], modelHints: [/lock/i, /safe/i], codecs: ["lock"] },
|
|
@@ -22616,7 +22676,7 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
22616
22676
|
}, timeoutMs);
|
|
22617
22677
|
const onData = (f) => {
|
|
22618
22678
|
const j = f.json;
|
|
22619
|
-
if (j?.cmd === subCmd && j.payload && typeof j.payload === "object") {
|
|
22679
|
+
if (f.channel === channel && f.commandId === CommandType.CMD_NOTIFY_PAYLOAD && j?.cmd === subCmd && j.payload && typeof j.payload === "object") {
|
|
22620
22680
|
cleanup();
|
|
22621
22681
|
resolve(j.payload);
|
|
22622
22682
|
}
|
|
@@ -22627,7 +22687,11 @@ var P2PCommandRouter = class _P2PCommandRouter {
|
|
|
22627
22687
|
};
|
|
22628
22688
|
session.on("data", onData);
|
|
22629
22689
|
try {
|
|
22630
|
-
|
|
22690
|
+
if (opts.payload) {
|
|
22691
|
+
if (!session.sendControlLevel2(subCmd, channel, accountId, opts.payload, 0))
|
|
22692
|
+
throw new Error("p2p query send failed");
|
|
22693
|
+
} else
|
|
22694
|
+
session.sendSetPayload(subCmd, {}, { accountId, channel });
|
|
22631
22695
|
} catch (e) {
|
|
22632
22696
|
cleanup();
|
|
22633
22697
|
reject(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -29021,7 +29085,11 @@ var EufyMega = class extends EventEmitter14 {
|
|
|
29021
29085
|
this.emit("p2p", f);
|
|
29022
29086
|
const caps = this.registry.capabilitiesForFrame(stationSn, f.channel);
|
|
29023
29087
|
const deviceSn = this.registry.serialForFrame(stationSn, f.channel);
|
|
29024
|
-
const signal = {
|
|
29088
|
+
const signal = {
|
|
29089
|
+
source: "p2p-frame",
|
|
29090
|
+
...f,
|
|
29091
|
+
deviceType: deviceSn ? this.registry.require(deviceSn).raw?.device_type : void 0
|
|
29092
|
+
};
|
|
29025
29093
|
if (f.params)
|
|
29026
29094
|
this.applyRealtimeState(deviceSn, f.params);
|
|
29027
29095
|
this.applyRealtimeReport(deviceSn, decodeState(signal, caps));
|
|
@@ -30289,10 +30357,13 @@ var EufyMega = class extends EventEmitter14 {
|
|
|
30289
30357
|
const rawChannel = raw.device_channel;
|
|
30290
30358
|
const channel = typeof rawChannel === "number" ? rawChannel : 0;
|
|
30291
30359
|
const member = raw.member ?? {};
|
|
30360
|
+
const parent = this.registry.list().find((d) => d.sn === rec.parentSn);
|
|
30361
|
+
const parentRaw = parent?.raw;
|
|
30292
30362
|
const resolved = resolveDevice(rec);
|
|
30293
30363
|
const dpCatalog = (resolved.codec === "vacuum" || resolved.codec === "mower") && rec.model ? await this.fetchDpCatalog(rec.model) : void 0;
|
|
30294
30364
|
return {
|
|
30295
30365
|
channel,
|
|
30366
|
+
timezone: recordString(parentRaw ?? {}, "time_zone"),
|
|
30296
30367
|
codec: resolved.codec,
|
|
30297
30368
|
deviceType: rec.deviceType,
|
|
30298
30369
|
model: rec.model,
|