@mega-yfue/eufy-sdk 0.5.0-beta.52 → 0.5.0-beta.54
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.
|
@@ -792,9 +792,9 @@ export declare class EufyMega extends EventEmitter {
|
|
|
792
792
|
* Subscribe the devices on one credential scope; a failing subscribe is reported, not fatal (one
|
|
793
793
|
* unreachable device must not stop the rest of the roster from coming up).
|
|
794
794
|
*
|
|
795
|
-
* A Tuya clean-line device
|
|
796
|
-
*
|
|
797
|
-
*
|
|
795
|
+
* A Tuya clean-line device (`eufy_home_tuya`) is not subscribed: its state lives on the Tuya cloud,
|
|
796
|
+
* and the broker refuses its `eufy_home` topics on every credential. Its DPs are read from the Tuya
|
|
797
|
+
* cloud instead.
|
|
798
798
|
*/
|
|
799
799
|
private subscribeMqttDevices;
|
|
800
800
|
/**
|
package/dist/core/contracts.d.ts
CHANGED
|
@@ -458,7 +458,7 @@ export interface CommandSink {
|
|
|
458
458
|
dispatch(cmd: Command): Promise<void>;
|
|
459
459
|
}
|
|
460
460
|
/**
|
|
461
|
-
* Inbound Tuya DP event contract — the transport parses
|
|
461
|
+
* Inbound Tuya DP event contract — the transport parses the Tuya cloud DP read
|
|
462
462
|
* (string-keyed object → numeric-keyed record) and delivers it through this interface. The
|
|
463
463
|
* transport owns the parse; a capability owns the semantics (which DP id means what). Mirrors
|
|
464
464
|
* {@link DpInboundFrame} for the AIoT MQTT path; the capability layer never names the Tuya framing.
|
package/dist/index.js
CHANGED
|
@@ -7769,6 +7769,12 @@ var ARMING_CMD = {
|
|
|
7769
7769
|
SET_ALL_ACTION: 1255
|
|
7770
7770
|
};
|
|
7771
7771
|
var DEVICE_NOTIFY_FLAG = 8;
|
|
7772
|
+
var STANDALONE_ACTION_MODELS = /* @__PURE__ */ new Set(["T8170", "T8171"]);
|
|
7773
|
+
var STANDALONE_READ_MODELS = /* @__PURE__ */ new Set([...STANDALONE_ACTION_MODELS, "T8400", "T8442", "T8410"]);
|
|
7774
|
+
function ownAction(read, mode) {
|
|
7775
|
+
const value = Number(read(`${mode}Action`)?.value);
|
|
7776
|
+
return Number.isInteger(value) && value >= 0 ? value : void 0;
|
|
7777
|
+
}
|
|
7772
7778
|
function actionTable(read, mode) {
|
|
7773
7779
|
const value = read(`${mode}ActionTable`)?.value;
|
|
7774
7780
|
return value && typeof value === "object" && Array.isArray(value.devices) ? value : void 0;
|
|
@@ -7811,6 +7817,13 @@ function alarmDelayCommand(mode, config, ctx) {
|
|
|
7811
7817
|
};
|
|
7812
7818
|
return setJsonRaw(ARMING_CMD.SET_ALL_ACTION, data, ctx, STATION_CHANNEL2);
|
|
7813
7819
|
}
|
|
7820
|
+
function ownTable(read, mode, channel) {
|
|
7821
|
+
const flags = ownAction(read, mode);
|
|
7822
|
+
if (flags === void 0)
|
|
7823
|
+
return void 0;
|
|
7824
|
+
const wire = ARMING_MODE_WIRE[mode];
|
|
7825
|
+
return { mode_id: wire, devices: [{ device_channel: channel, action: wire << 24 | flags }] };
|
|
7826
|
+
}
|
|
7814
7827
|
var ACTION_TABLE_LAG_MS = 15e3;
|
|
7815
7828
|
function withDeviceNotification(table, channel, on) {
|
|
7816
7829
|
if (!table?.devices.some((d) => d.device_channel === channel))
|
|
@@ -7879,25 +7892,29 @@ var ARMING_MEMBERS = {
|
|
|
7879
7892
|
/**
|
|
7880
7893
|
* Whether the device on `channel` sends a push notification in `mode`: its {@link DEVICE_NOTIFY_FLAG}
|
|
7881
7894
|
* in the action table the station reports for that mode. `undefined` where the station reports no table
|
|
7882
|
-
* or the table does not list `channel`. A
|
|
7895
|
+
* or the table does not list `channel`. A standalone camera answers from its own 1225/1239/1148 flags,
|
|
7896
|
+
* for its own channel only. A device's channel is `EufyDevice.channel`.
|
|
7883
7897
|
*/
|
|
7884
|
-
deviceNotification: method(({ read }) => (mode, channel) => {
|
|
7885
|
-
const action = actionTable(read, mode)?.devices.find((d) => d.device_channel === channel)?.action;
|
|
7898
|
+
deviceNotification: method(({ ctx, read }) => (mode, channel) => {
|
|
7899
|
+
const action = ctx.model === "T8010" ? actionTable(read, mode)?.devices.find((d) => d.device_channel === channel)?.action : channel === ctx.channel ? ownAction(read, mode) : void 0;
|
|
7886
7900
|
return typeof action === "number" ? (action & DEVICE_NOTIFY_FLAG) !== 0 : void 0;
|
|
7887
|
-
}, "Whether the device on a channel sends a push notification in a guard mode (home/away).", (ctx) => ctx.model === "T8010"),
|
|
7901
|
+
}, "Whether the device on a channel sends a push notification in a guard mode (home/away/custom1).", (ctx) => ctx.model === "T8010" || STANDALONE_READ_MODELS.has(ctx.model ?? "")),
|
|
7888
7902
|
/**
|
|
7889
7903
|
* Turn the push notification of the device on `channel` on or off in `mode` (cmd
|
|
7890
7904
|
* {@link ARMING_CMD.SET_ALL_ACTION}): the station's reported table written back with that one flag
|
|
7891
|
-
* changed.
|
|
7892
|
-
*
|
|
7893
|
-
*
|
|
7905
|
+
* changed. A standalone camera is sent a one-entry `{mode_id, devices}` table built from its own flags,
|
|
7906
|
+
* for its own channel only. Rejects, sending nothing, where the table does not list `channel`. A write
|
|
7907
|
+
* builds on the last one sent for that mode until the device reports a table (`${mode}Action` on a
|
|
7908
|
+
* standalone camera) observed 15 s after it, so a second write does not revert it; a write whose dispatch
|
|
7909
|
+
* rejects is not built on.
|
|
7894
7910
|
*/
|
|
7895
7911
|
setDeviceNotification: method(({ ctx, sink, read }) => {
|
|
7896
7912
|
const sent2 = /* @__PURE__ */ new Map();
|
|
7897
7913
|
return (mode, channel, on) => {
|
|
7898
7914
|
const last = sent2.get(mode);
|
|
7899
|
-
const
|
|
7900
|
-
const
|
|
7915
|
+
const reported = ctx.model === "T8010" ? `${mode}ActionTable` : `${mode}Action`;
|
|
7916
|
+
const observed = read(reported)?.ts ?? 0;
|
|
7917
|
+
const base = last && observed < last.at + ACTION_TABLE_LAG_MS ? last.table : ctx.model === "T8010" ? actionTable(read, mode) : ownTable(read, mode, ctx.channel);
|
|
7901
7918
|
const table = withDeviceNotification(base, channel, on);
|
|
7902
7919
|
if (!table) {
|
|
7903
7920
|
return Promise.reject(new Error(`arming: no ${mode} action table lists channel ${channel} [${describeDevice(ctx)}]`));
|
|
@@ -7913,7 +7930,7 @@ var ARMING_MEMBERS = {
|
|
|
7913
7930
|
throw err;
|
|
7914
7931
|
});
|
|
7915
7932
|
};
|
|
7916
|
-
}, "Turn the push notification of the device on a channel on or off in a guard mode (home/away).", (ctx) => ctx.model === "T8010")
|
|
7933
|
+
}, "Turn the push notification of the device on a channel on or off in a guard mode (home/away/custom1).", (ctx) => ctx.model === "T8010" || STANDALONE_ACTION_MODELS.has(ctx.model ?? ""))
|
|
7917
7934
|
};
|
|
7918
7935
|
function decodeAlarmPhase(signal) {
|
|
7919
7936
|
const type = signal.source === "push" ? signal.payload?.["type"] : void 0;
|
|
@@ -11544,7 +11561,7 @@ var SECURITY_PARAMS = {
|
|
|
11544
11561
|
1148: {
|
|
11545
11562
|
name: "custom1Action",
|
|
11546
11563
|
type: "number",
|
|
11547
|
-
provenance: "
|
|
11564
|
+
provenance: "verified"
|
|
11548
11565
|
},
|
|
11549
11566
|
1149: {
|
|
11550
11567
|
name: "custom2Action",
|
|
@@ -11759,7 +11776,7 @@ var SECURITY_PARAMS = {
|
|
|
11759
11776
|
1225: {
|
|
11760
11777
|
name: "homeAction",
|
|
11761
11778
|
type: "number",
|
|
11762
|
-
provenance: "
|
|
11779
|
+
provenance: "verified"
|
|
11763
11780
|
},
|
|
11764
11781
|
1229: {
|
|
11765
11782
|
name: "param1229",
|
|
@@ -25726,33 +25743,6 @@ function parseTuyaDpEvent(payload) {
|
|
|
25726
25743
|
}
|
|
25727
25744
|
return Object.keys(result).length > 0 ? result : null;
|
|
25728
25745
|
}
|
|
25729
|
-
function parseTuyaDpReport(raw) {
|
|
25730
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
25731
|
-
return void 0;
|
|
25732
|
-
const envelope = raw;
|
|
25733
|
-
const payloadObj = jsonObject(envelope.payload);
|
|
25734
|
-
if (payloadObj) {
|
|
25735
|
-
const data2 = payloadObj.data;
|
|
25736
|
-
if (data2 && typeof data2 === "object" && !Array.isArray(data2)) {
|
|
25737
|
-
const dataObj = data2;
|
|
25738
|
-
const dps = dataObj.dps;
|
|
25739
|
-
if (dps && typeof dps === "object" && !Array.isArray(dps))
|
|
25740
|
-
return dps;
|
|
25741
|
-
const filtered = Object.fromEntries(Object.entries(dataObj).filter(([k]) => /^[1-9]\d*$/.test(k)));
|
|
25742
|
-
return Object.keys(filtered).length > 0 ? filtered : void 0;
|
|
25743
|
-
}
|
|
25744
|
-
}
|
|
25745
|
-
const topDps = envelope.dps;
|
|
25746
|
-
if (topDps && typeof topDps === "object" && !Array.isArray(topDps))
|
|
25747
|
-
return topDps;
|
|
25748
|
-
const data = envelope.data;
|
|
25749
|
-
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
25750
|
-
const dataDps = data.dps;
|
|
25751
|
-
if (dataDps && typeof dataDps === "object" && !Array.isArray(dataDps))
|
|
25752
|
-
return dataDps;
|
|
25753
|
-
}
|
|
25754
|
-
return void 0;
|
|
25755
|
-
}
|
|
25756
25746
|
var TuyaDpRouter = class {
|
|
25757
25747
|
listener;
|
|
25758
25748
|
/** Register the inbound listener. Replaces any previously registered one. */
|
|
@@ -29312,16 +29302,6 @@ var EufyMega = class extends EventEmitter13 {
|
|
|
29312
29302
|
transport.on("disconnect", (r) => this.emit("disconnect", r));
|
|
29313
29303
|
transport.on("message", (m) => {
|
|
29314
29304
|
this.emit("message", m);
|
|
29315
|
-
if (m.deviceSn) {
|
|
29316
|
-
const dev = this.registry.list().find((d) => d.sn === m.deviceSn);
|
|
29317
|
-
if (dev?.category === "eufy_home_tuya") {
|
|
29318
|
-
const dps = parseTuyaDpReport(m.raw);
|
|
29319
|
-
if (dps) {
|
|
29320
|
-
this.tuyaDpRouter.deliver(m.deviceSn, dps);
|
|
29321
|
-
return;
|
|
29322
|
-
}
|
|
29323
|
-
}
|
|
29324
|
-
}
|
|
29325
29305
|
if (m.deviceSn) {
|
|
29326
29306
|
const frame = parseBizMapFrame(m.raw);
|
|
29327
29307
|
if (frame) {
|
|
@@ -29355,23 +29335,24 @@ var EufyMega = class extends EventEmitter13 {
|
|
|
29355
29335
|
* Subscribe the devices on one credential scope; a failing subscribe is reported, not fatal (one
|
|
29356
29336
|
* unreachable device must not stop the rest of the roster from coming up).
|
|
29357
29337
|
*
|
|
29358
|
-
* A Tuya clean-line device
|
|
29359
|
-
*
|
|
29360
|
-
*
|
|
29338
|
+
* A Tuya clean-line device (`eufy_home_tuya`) is not subscribed: its state lives on the Tuya cloud,
|
|
29339
|
+
* and the broker refuses its `eufy_home` topics on every credential. Its DPs are read from the Tuya
|
|
29340
|
+
* cloud instead.
|
|
29361
29341
|
*/
|
|
29362
29342
|
async subscribeMqttDevices(scope, transport) {
|
|
29363
29343
|
for (const d of this.getMqttDevices().filter((dev) => mqttScopeFor(dev) === scope)) {
|
|
29364
|
-
try {
|
|
29365
|
-
await transport.subscribeDevice(d);
|
|
29366
|
-
await this.sendRealtimeInit(d.sn);
|
|
29367
|
-
} catch (e) {
|
|
29368
|
-
this.reportError(e);
|
|
29369
|
-
}
|
|
29370
29344
|
if (d.category === "eufy_home_tuya") {
|
|
29371
29345
|
this.tuya.fetchDps(d.sn).then((dps) => {
|
|
29372
29346
|
if (dps)
|
|
29373
29347
|
this.tuyaDpRouter.deliver(d.sn, dps);
|
|
29374
29348
|
}).catch((e) => this.reportError(e));
|
|
29349
|
+
continue;
|
|
29350
|
+
}
|
|
29351
|
+
try {
|
|
29352
|
+
await transport.subscribeDevice(d);
|
|
29353
|
+
await this.sendRealtimeInit(d.sn);
|
|
29354
|
+
} catch (e) {
|
|
29355
|
+
this.reportError(e);
|
|
29375
29356
|
}
|
|
29376
29357
|
}
|
|
29377
29358
|
}
|