@mega-yfue/eufy-sdk 0.5.0-beta.0 → 0.5.0-beta.10
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/client/eufy-mega.d.ts +18 -6
- package/dist/core/store.d.ts +2 -0
- package/dist/index.js +1379 -39
- package/dist/index.js.map +4 -4
- package/dist/model/device-family.d.ts +2 -0
- package/dist/model/registry.d.ts +3 -2
- package/dist/model/types.d.ts +6 -0
- package/dist/transport/http/mega-client.d.ts +17 -0
- package/dist/transport/p2p/command-router.d.ts +8 -5
- package/dist/transport/p2p/live-stream.d.ts +3 -0
- package/dist/transport/p2p/p2p-session.d.ts +7 -2
- package/dist/transport/p2p/video.d.ts +3 -2
- package/dist/transport/rtc/command-router.d.ts +72 -0
- package/dist/transport/rtc/peer.d.ts +93 -0
- package/dist/transport/rtc/portal-packet.d.ts +66 -0
- package/dist/transport/rtc/ptcs-framer.d.ts +85 -0
- package/dist/transport/rtc/scall-sdp.d.ts +42 -0
- package/dist/transport/rtc/session.d.ts +78 -0
- package/dist/transport/rtc/signaling.d.ts +137 -0
- package/package.json +9 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The T9000's signalling channel: the WebSocket through which a client and a HomeBase S1 Pro agree on a
|
|
3
|
+
* WebRTC session. It is the wire the security.eufy.com web client uses (`/v1/rtc/ws/join`), and the hub
|
|
4
|
+
* accepts it from any client presenting the account's mega token.
|
|
5
|
+
*
|
|
6
|
+
* Sequence:
|
|
7
|
+
*
|
|
8
|
+
* 1. `GET https://<smart host>/v1/smart/nvr/ws/sign` with the mega token → a `sign` blob.
|
|
9
|
+
* 2. WebSocket to `wss://<smart host>/v1/rtc/ws/join?reqtype=nvr`, subprotocols `["v1", <base64url
|
|
10
|
+
* JSON>]` carrying the cluster region, station serial, token, `gtoken` and the sign. HTTP headers on
|
|
11
|
+
* the upgrade alone are refused: the JSON subprotocol is what authenticates.
|
|
12
|
+
* 3. `action 1` auth on open; `action 3` session messages after: `scall` (start), `info` (SDP and
|
|
13
|
+
* trickle ICE), `ack`, `hangup`. Every session message carries an HMAC-SHA256 `account` over
|
|
14
|
+
* `channelId + adminUserId + ts`, keyed by the token.
|
|
15
|
+
*
|
|
16
|
+
* Region is two values: the HTTP sign request names the account's **country** (`Web-Country`), the
|
|
17
|
+
* WebSocket payload names the **cluster**, the shard prefix uppercased (`ie-pr` → `IE`). The smart host
|
|
18
|
+
* is `security-smart` for `us` and `security-smart-<prefix>` for any other shard.
|
|
19
|
+
*
|
|
20
|
+
* `fetch` and the `WebSocket` constructor are injectable.
|
|
21
|
+
*/
|
|
22
|
+
import { EventEmitter } from "node:events";
|
|
23
|
+
import { type Logger } from "../../core/logger.js";
|
|
24
|
+
/** The portal's origin; the sign endpoint and the socket upgrade both check it. */
|
|
25
|
+
export declare const PORTAL_ORIGIN = "https://security.eufy.com";
|
|
26
|
+
/** The hub drops an idle signalling socket after ~83 s; re-sending auth inside that keeps it. */
|
|
27
|
+
export declare const SIGNALING_KEEPALIVE_MS = 25000;
|
|
28
|
+
/** How long the socket has to open after the sign. */
|
|
29
|
+
export declare const SIGNALING_CONNECT_TIMEOUT_MS = 15000;
|
|
30
|
+
/** Outer wire envelope. */
|
|
31
|
+
export interface RtcWsEnvelope {
|
|
32
|
+
msgid: string;
|
|
33
|
+
data: string;
|
|
34
|
+
}
|
|
35
|
+
/** Inner message (the envelope's `data`, JSON-parsed). */
|
|
36
|
+
export interface RtcInnerMessage {
|
|
37
|
+
code?: number;
|
|
38
|
+
action?: number;
|
|
39
|
+
sessionId?: string;
|
|
40
|
+
sn?: string;
|
|
41
|
+
subSn?: string;
|
|
42
|
+
channelId?: number;
|
|
43
|
+
isResponse?: number;
|
|
44
|
+
dataType?: string;
|
|
45
|
+
source?: string;
|
|
46
|
+
ts?: number;
|
|
47
|
+
data?: string;
|
|
48
|
+
msgid?: string;
|
|
49
|
+
}
|
|
50
|
+
/** What a socket event carries; only the fields each event type fills are read. */
|
|
51
|
+
export interface SignalingSocketEvent {
|
|
52
|
+
data?: unknown;
|
|
53
|
+
code?: number;
|
|
54
|
+
reason?: string;
|
|
55
|
+
}
|
|
56
|
+
/** The subset of a `WebSocket` the client uses. */
|
|
57
|
+
export interface SignalingSocket {
|
|
58
|
+
readonly readyState: number;
|
|
59
|
+
send(data: string): void;
|
|
60
|
+
close(code?: number, reason?: string): void;
|
|
61
|
+
addEventListener(type: "open" | "message" | "close" | "error", listener: (ev: SignalingSocketEvent) => void): void;
|
|
62
|
+
}
|
|
63
|
+
export interface SignalingSocketInit {
|
|
64
|
+
/** The subprotocols; slot 2 carries the base64url auth JSON. */
|
|
65
|
+
protocols: string[];
|
|
66
|
+
/** The upgrade Origin, which the server checks like the sign call. */
|
|
67
|
+
origin: string;
|
|
68
|
+
}
|
|
69
|
+
export type SignalingSocketFactory = (url: string, init: SignalingSocketInit) => SignalingSocket;
|
|
70
|
+
export interface RtcSignalingOptions {
|
|
71
|
+
/** The mega session's auth token. */
|
|
72
|
+
authToken: string;
|
|
73
|
+
/** The `gtoken` header value the mega session's authed HTTP calls carry. */
|
|
74
|
+
gtoken: string;
|
|
75
|
+
stationSn: string;
|
|
76
|
+
/** The station's `member.admin_user_id`, the account the session HMAC names. */
|
|
77
|
+
adminUserId: string;
|
|
78
|
+
/** The mega shard the account lives on; picks the host and the cluster name. */
|
|
79
|
+
shard: string;
|
|
80
|
+
/** The account's ISO country, sent on the sign request (`Web-Country`). */
|
|
81
|
+
country: string;
|
|
82
|
+
fetch?: typeof fetch;
|
|
83
|
+
createSocket?: SignalingSocketFactory;
|
|
84
|
+
logger?: Logger;
|
|
85
|
+
now?: () => number;
|
|
86
|
+
makeMsgId?: () => string;
|
|
87
|
+
}
|
|
88
|
+
export interface RtcSignalingEvents {
|
|
89
|
+
message: [inner: RtcInnerMessage, envelope: RtcWsEnvelope];
|
|
90
|
+
open: [];
|
|
91
|
+
close: [code: number, reason: string];
|
|
92
|
+
error: [err: Error];
|
|
93
|
+
}
|
|
94
|
+
/** The portal's `account` field: HMAC-SHA256 of `channelId + adminUserId + ts`, keyed by the token. */
|
|
95
|
+
export declare function sessionAccount(channelId: number, adminUserId: string, ts: number, authToken: string): string;
|
|
96
|
+
export declare class RtcSignalingClient extends EventEmitter<RtcSignalingEvents> {
|
|
97
|
+
private readonly opts;
|
|
98
|
+
private ws?;
|
|
99
|
+
private sign?;
|
|
100
|
+
private keepalive?;
|
|
101
|
+
private readonly wsUrl;
|
|
102
|
+
private readonly signUrl;
|
|
103
|
+
private readonly wsRegion;
|
|
104
|
+
private readonly fetchImpl;
|
|
105
|
+
private readonly createSocket;
|
|
106
|
+
private readonly logger;
|
|
107
|
+
private readonly now;
|
|
108
|
+
private readonly makeMsgId;
|
|
109
|
+
constructor(opts: RtcSignalingOptions);
|
|
110
|
+
get isOpen(): boolean;
|
|
111
|
+
/** Step 1: the sign blob the socket and every auth message carry. A non-JSON body is reported by status. */
|
|
112
|
+
fetchSign(): Promise<string>;
|
|
113
|
+
/**
|
|
114
|
+
* Step 2: open the socket and send the first auth; resolves on `open`. A socket that closes or errors
|
|
115
|
+
* before it ever opened rejects, so the call always settles.
|
|
116
|
+
*/
|
|
117
|
+
connect(): Promise<void>;
|
|
118
|
+
/** `action 1`, also what keeps the socket alive when re-sent. */
|
|
119
|
+
sendAuth(sign?: string): void;
|
|
120
|
+
/**
|
|
121
|
+
* `action 3`, a session message on channel 0, the hub's own session; `scall` opens the negotiation.
|
|
122
|
+
* `subSn` is empty for the hub's session.
|
|
123
|
+
*/
|
|
124
|
+
sendSession(dataType: string, payload?: Record<string, unknown>): void;
|
|
125
|
+
sendCall(): void;
|
|
126
|
+
sendAck(): void;
|
|
127
|
+
/** The SDP answer, as scall JSON text, in an `info`. */
|
|
128
|
+
sendInfoSdp(scallJson: string): void;
|
|
129
|
+
/** Trickle a candidate on the session's channel, like its SDP answer. An empty candidate ends them. */
|
|
130
|
+
sendInfoCandidate(candidate: string): void;
|
|
131
|
+
sendHangup(): void;
|
|
132
|
+
close(): void;
|
|
133
|
+
private startKeepalive;
|
|
134
|
+
private stopKeepalive;
|
|
135
|
+
private sendEnvelope;
|
|
136
|
+
private handleWireMessage;
|
|
137
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mega-yfue/eufy-sdk",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.10",
|
|
4
4
|
"description": "One typed TypeScript client for the Anker eufy v6 cloud — capability-driven devices, realtime events over P2P/MQTT/push, and live media",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "mega-yfue",
|
|
@@ -76,6 +76,14 @@
|
|
|
76
76
|
"mqtt": "^5.16.0",
|
|
77
77
|
"protobufjs": "^8.8.0"
|
|
78
78
|
},
|
|
79
|
+
"peerDependencies": {
|
|
80
|
+
"node-datachannel": "^0.33.4"
|
|
81
|
+
},
|
|
82
|
+
"peerDependenciesMeta": {
|
|
83
|
+
"node-datachannel": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
79
87
|
"devDependencies": {
|
|
80
88
|
"@types/node": "^26.6.3",
|
|
81
89
|
"esbuild": "^0.28.2",
|