eufy-security-client 4.0.0 → 4.1.0-dev.37
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 +32 -0
- package/build/eufysecurity.d.ts +15 -0
- package/build/eufysecurity.js +58 -6
- package/build/eufysecurity.js.map +1 -1
- package/build/http/api.js +1 -1
- package/build/http/api.js.map +1 -1
- package/build/http/decodeImageV2.d.ts +12 -8
- package/build/http/decodeImageV2.js +17 -12
- package/build/http/decodeImageV2.js.map +1 -1
- package/build/http/index.d.ts +2 -0
- package/build/http/index.js +2 -0
- package/build/http/index.js.map +1 -1
- package/build/http/interfaces.d.ts +2 -0
- package/build/http/megaApi.d.ts +186 -0
- package/build/http/megaApi.js +513 -0
- package/build/http/megaApi.js.map +1 -0
- package/build/http/megaCrypto.d.ts +84 -0
- package/build/http/megaCrypto.js +129 -0
- package/build/http/megaCrypto.js.map +1 -0
- package/build/http/megaInterfaces.d.ts +83 -0
- package/build/http/megaInterfaces.js +3 -0
- package/build/http/megaInterfaces.js.map +1 -0
- package/build/http/megaTransition.d.ts +103 -0
- package/build/http/megaTransition.js +203 -0
- package/build/http/megaTransition.js.map +1 -0
- package/build/http/station.d.ts +1 -0
- package/build/http/station.js +4 -0
- package/build/http/station.js.map +1 -1
- package/build/http/types.d.ts +7 -1
- package/build/http/types.js +6 -0
- package/build/http/types.js.map +1 -1
- package/build/http/utils.d.ts +10 -0
- package/build/http/utils.js +34 -10
- package/build/http/utils.js.map +1 -1
- package/build/interfaces.d.ts +2 -0
- package/build/p2p/interfaces.d.ts +2 -0
- package/build/p2p/session.js +17 -0
- 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/coverage/clover.xml +15420 -0
- package/coverage/coverage-final.json +37 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +176 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov.info +29542 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# eufy-security-client
|
|
2
2
|
|
|
3
|
+
> [!CAUTION]
|
|
4
|
+
> # 🚨🚨🚨 LIBRARY DEPRECATION NOTICE 🚨🚨🚨
|
|
5
|
+
>
|
|
6
|
+
> ### ⚠️ Eufy is shutting down the legacy APIs this library is built on. ⚠️
|
|
7
|
+
>
|
|
8
|
+
> Eufy is in the middle of a large migration of their ecosystem. The newer **Eufy Mega**
|
|
9
|
+
> platform (the "5-in-1" app, covering Security / Clean / Lights / Care) is gradually
|
|
10
|
+
> becoming the only supported backend, and Eufy has **already started removing access to
|
|
11
|
+
> the legacy APIs** this library was originally built on. Until recently both worked in
|
|
12
|
+
> parallel — that is no longer guaranteed.
|
|
13
|
+
>
|
|
14
|
+
> **🔔 What this means for you:**
|
|
15
|
+
>
|
|
16
|
+
> - 🟢 A recent PR restores **push notifications** against the new v6 ("eufy_mega")
|
|
17
|
+
> backend, so push works again **for now**. This is a short-term stopgap.
|
|
18
|
+
> - 🟡 Other functionality that still depends on legacy endpoints may stop working
|
|
19
|
+
> **without warning** as Eufy continues the rollout. The current Eufy app no longer
|
|
20
|
+
> uses the legacy API at all.
|
|
21
|
+
> - 🔴 Once the legacy API is fully shut down, **this library will stop functioning** —
|
|
22
|
+
> no amount of patching here will change that.
|
|
23
|
+
>
|
|
24
|
+
> **🚧 What's next:**
|
|
25
|
+
>
|
|
26
|
+
> A new integration built around **Eufy Mega** is in active development (auto-discovery,
|
|
27
|
+
> less battery drain for P2P), designed from the ground up rather than bolted onto the
|
|
28
|
+
> Security-only structure, and coordinated across the Home Assistant, Homebridge and
|
|
29
|
+
> Homey communities so the new library works for everyone.
|
|
30
|
+
>
|
|
31
|
+
> ### 👉 Treat this release as a **temporary stopgap.** 👈
|
|
32
|
+
>
|
|
33
|
+
> *This notice will be updated as the migration progresses.*
|
|
34
|
+
|
|
3
35
|

|
|
4
36
|
|
|
5
37
|
[](https://www.npmjs.com/package/eufy-security-client)
|
package/build/eufysecurity.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ import { LogLevel } from "typescript-logging";
|
|
|
12
12
|
export declare class EufySecurity extends TypedEmitter<EufySecurityEvents> {
|
|
13
13
|
private config;
|
|
14
14
|
private api;
|
|
15
|
+
/** All v6 ("eufy_mega") behaviour — transport, login, push, connect sequencing — is isolated here. */
|
|
16
|
+
private megaTransition;
|
|
15
17
|
private houses;
|
|
16
18
|
private stations;
|
|
17
19
|
private devices;
|
|
@@ -70,7 +72,20 @@ export declare class EufySecurity extends TypedEmitter<EufySecurityEvents> {
|
|
|
70
72
|
setCameraMaxLivestreamDuration(seconds: number): void;
|
|
71
73
|
getCameraMaxLivestreamDuration(): number;
|
|
72
74
|
registerPushNotifications(credentials?: Credentials, persistentIds?: string[]): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Entry point for the consumer. The whole login sequence (v6-first, legacy best-effort, single
|
|
77
|
+
* app-ready signal at the end, serialisation, 2FA/captcha routing) lives in {@link MegaTransition};
|
|
78
|
+
* here we just delegate. Removing the transition layer makes this equivalent to {@link legacyConnect}.
|
|
79
|
+
*/
|
|
73
80
|
connect(options?: LoginOptions): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* The original (upstream) login: authenticate the legacy backend and trust the device on first
|
|
83
|
+
* 2FA. Kept verbatim and driven by {@link MegaTransition} as the best-effort second step; it no
|
|
84
|
+
* longer signals the app directly (that is now done once, at the end of the sequence).
|
|
85
|
+
*/
|
|
86
|
+
private legacyConnect;
|
|
87
|
+
/** The narrow surface the v6 transition layer uses to talk back to us, as a closure object. */
|
|
88
|
+
private megaTransitionHost;
|
|
74
89
|
getPushPersistentIds(): string[];
|
|
75
90
|
private updateDeviceProperties;
|
|
76
91
|
private onAPIClose;
|
package/build/eufysecurity.js
CHANGED
|
@@ -42,7 +42,7 @@ const fs_1 = require("fs");
|
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
43
|
const util = __importStar(require("util"));
|
|
44
44
|
const events_1 = __importDefault(require("events"));
|
|
45
|
-
const
|
|
45
|
+
const megaTransition_1 = require("./http/megaTransition");
|
|
46
46
|
const station_1 = require("./http/station");
|
|
47
47
|
const types_1 = require("./http/types");
|
|
48
48
|
const service_1 = require("./push/service");
|
|
@@ -61,6 +61,8 @@ const utils_3 = require("./p2p/utils");
|
|
|
61
61
|
class EufySecurity extends tiny_typed_emitter_1.TypedEmitter {
|
|
62
62
|
config;
|
|
63
63
|
api;
|
|
64
|
+
/** All v6 ("eufy_mega") behaviour — transport, login, push, connect sequencing — is isolated here. */
|
|
65
|
+
megaTransition;
|
|
64
66
|
houses = {};
|
|
65
67
|
stations = {};
|
|
66
68
|
devices = {};
|
|
@@ -236,17 +238,31 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
236
238
|
this.persistentData.cloud_token_expiration = 0;
|
|
237
239
|
this.persistentData.httpApi = undefined;
|
|
238
240
|
}
|
|
239
|
-
|
|
241
|
+
// All v6 ("eufy_mega") behaviour is isolated in MegaTransition (login, push, connect
|
|
242
|
+
// sequencing). It talks back to us only through this narrow host surface and builds the live
|
|
243
|
+
// transport (the legacy HTTPApi today). Removing the transition layer reverts everything to the
|
|
244
|
+
// upstream legacy behaviour.
|
|
245
|
+
this.megaTransition = new megaTransition_1.MegaTransition(this.megaTransitionHost());
|
|
246
|
+
this.api = await this.megaTransition.createTransport(this.persistentData.httpApi);
|
|
240
247
|
this.api.setLanguage(this.config.language);
|
|
241
248
|
this.api.setPhoneModel(this.config.trustedDeviceName);
|
|
242
249
|
this.api.on("houses", (houses) => this.handleHouses(houses));
|
|
243
250
|
this.api.on("hubs", (hubs) => this.handleHubs(hubs));
|
|
244
251
|
this.api.on("devices", (devices) => this.handleDevices(devices));
|
|
245
252
|
this.api.on("close", () => this.onAPIClose());
|
|
246
|
-
|
|
247
|
-
|
|
253
|
+
// NOTE: the legacy login emitting "connect" no longer drives the app-ready signal directly —
|
|
254
|
+
// connect() sequences mega + legacy and calls onAPIConnect() once at the very end (see below).
|
|
255
|
+
this.api.on("connect", () => logging_1.rootMainLogger.debug("Legacy API connected"));
|
|
256
|
+
// The legacy login records itself as the pending challenge so the next code/captcha is routed to it.
|
|
257
|
+
this.api.on("captcha request", (id, captcha) => {
|
|
258
|
+
this.megaTransition.recordLegacyChallenge();
|
|
259
|
+
this.onCaptchaRequest(id, captcha);
|
|
260
|
+
});
|
|
248
261
|
this.api.on("auth token invalidated", () => this.onAuthTokenInvalidated());
|
|
249
|
-
this.api.on("tfa request", () =>
|
|
262
|
+
this.api.on("tfa request", () => {
|
|
263
|
+
this.megaTransition.recordLegacyChallenge();
|
|
264
|
+
this.onTfaRequest();
|
|
265
|
+
});
|
|
250
266
|
this.api.on("connection error", (error) => this.onAPIConnectionError(error));
|
|
251
267
|
if (this.persistentData.cloud_token &&
|
|
252
268
|
this.persistentData.cloud_token != "" &&
|
|
@@ -273,8 +289,11 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
273
289
|
this.pushService.on("connect", async (token) => {
|
|
274
290
|
this.pushCloudRegistered = await this.api.registerPushToken(token);
|
|
275
291
|
this.pushCloudChecked = await this.api.checkPushToken();
|
|
292
|
+
const megaRegistered = await this.megaTransition.registerMegaPushToken(token);
|
|
276
293
|
//TODO: Retry if failed with max retry to not lock account
|
|
277
|
-
if
|
|
294
|
+
// Push is "connected" if registration succeeded on EITHER backend: on a migrated account the
|
|
295
|
+
// legacy registration fails (no legacy session) but the v6 one carries the events.
|
|
296
|
+
if ((this.pushCloudRegistered && this.pushCloudChecked) || megaRegistered) {
|
|
278
297
|
logging_1.rootMainLogger.info("Push notification connection successfully established");
|
|
279
298
|
this.emit("push connect");
|
|
280
299
|
}
|
|
@@ -560,6 +579,7 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
560
579
|
station.on("garage door status", (station, channel, doorId, status) => this.onStationGarageDoorStatus(station, channel, doorId, status));
|
|
561
580
|
station.on("storage info hb3", (station, channel, storageInfo) => this.onStorageInfoHb3(station, channel, storageInfo));
|
|
562
581
|
station.on("hub notify update", (station) => this.onHubNotifyUpdate(station));
|
|
582
|
+
station.on("push notification", (_station, message) => this.onPushMessage(message));
|
|
563
583
|
this.addStation(station);
|
|
564
584
|
station.initialize();
|
|
565
585
|
}
|
|
@@ -869,7 +889,20 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
869
889
|
this.pushService.setPersistentIds(persistentIds);
|
|
870
890
|
this.pushService.open();
|
|
871
891
|
}
|
|
892
|
+
/**
|
|
893
|
+
* Entry point for the consumer. The whole login sequence (v6-first, legacy best-effort, single
|
|
894
|
+
* app-ready signal at the end, serialisation, 2FA/captcha routing) lives in {@link MegaTransition};
|
|
895
|
+
* here we just delegate. Removing the transition layer makes this equivalent to {@link legacyConnect}.
|
|
896
|
+
*/
|
|
872
897
|
async connect(options) {
|
|
898
|
+
return this.megaTransition.connect(options);
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* The original (upstream) login: authenticate the legacy backend and trust the device on first
|
|
902
|
+
* 2FA. Kept verbatim and driven by {@link MegaTransition} as the best-effort second step; it no
|
|
903
|
+
* longer signals the app directly (that is now done once, at the end of the sequence).
|
|
904
|
+
*/
|
|
905
|
+
async legacyConnect(options) {
|
|
873
906
|
await this.api
|
|
874
907
|
.login(options)
|
|
875
908
|
.then(async () => {
|
|
@@ -890,6 +923,25 @@ class EufySecurity extends tiny_typed_emitter_1.TypedEmitter {
|
|
|
890
923
|
logging_1.rootMainLogger.error("Connect Error", { error: (0, utils_1.getError)(error), options: options });
|
|
891
924
|
});
|
|
892
925
|
}
|
|
926
|
+
/** The narrow surface the v6 transition layer uses to talk back to us, as a closure object. */
|
|
927
|
+
megaTransitionHost() {
|
|
928
|
+
// `api` is a getter so the transition layer always sees the live transport, which we assign
|
|
929
|
+
// right after building the host (and could swap later); the other members are stable.
|
|
930
|
+
const eufy = this;
|
|
931
|
+
return {
|
|
932
|
+
config: this.config,
|
|
933
|
+
persistentData: this.persistentData,
|
|
934
|
+
get api() {
|
|
935
|
+
return eufy.api;
|
|
936
|
+
},
|
|
937
|
+
writePersistentData: () => this.writePersistentData(),
|
|
938
|
+
emitTfaRequest: () => this.onTfaRequest(),
|
|
939
|
+
emitCaptchaRequest: (id, captcha) => this.onCaptchaRequest(id, captcha),
|
|
940
|
+
legacyConnect: (options) => this.legacyConnect(options),
|
|
941
|
+
onAPIConnect: () => this.onAPIConnect(),
|
|
942
|
+
onConnectionError: (error) => this.onAPIConnectionError(error),
|
|
943
|
+
};
|
|
944
|
+
}
|
|
893
945
|
getPushPersistentIds() {
|
|
894
946
|
return this.pushService.getPersistentIds();
|
|
895
947
|
}
|