@mega-yfue/eufy-sdk 0.5.0-beta.66 → 0.5.0-beta.68
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 +10 -7
- package/dist/index.js +273 -41
- package/dist/index.js.map +4 -4
- package/dist/model/device-family.d.ts +2 -0
- package/dist/model/types.d.ts +5 -4
- package/dist/transport/rtc/call-live.d.ts +41 -0
- package/dist/transport/rtc/command-router.d.ts +13 -0
- package/dist/transport/rtc/peer.d.ts +19 -1
- package/dist/transport/rtc/scall-sdp.d.ts +8 -0
- package/dist/transport/rtc/session.d.ts +5 -0
- package/package.json +1 -1
|
@@ -47,6 +47,8 @@ export declare const isHomeBase: (ctx: FamilyContext) => boolean;
|
|
|
47
47
|
export declare const isStation9000: (ctx: FamilyContext) => boolean;
|
|
48
48
|
/** T8N00 NVR, identified by its reported model. */
|
|
49
49
|
export declare const isNvrT8N00: (ctx: FamilyContext) => boolean;
|
|
50
|
+
/** Video Doorbell C31 (T8223), identified by its reported model. */
|
|
51
|
+
export declare const isDoorbellT8223: (ctx: FamilyContext) => boolean;
|
|
50
52
|
/** Wired doorbell (DeviceType.DOORBELL). */
|
|
51
53
|
export declare const isWiredDoorbell: (ctx: FamilyContext) => boolean;
|
|
52
54
|
/**
|
package/dist/model/types.d.ts
CHANGED
|
@@ -177,10 +177,11 @@ export interface PropertySpec {
|
|
|
177
177
|
* consumable counters are `"number"` over one base64 string, and reading the value as a number is
|
|
178
178
|
* exactly what must NOT happen at ingest.
|
|
179
179
|
*
|
|
180
|
-
* Storage
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
180
|
+
* Storage keeps such a value intact: a non-numeric string passes a `number` coercion unchanged, and
|
|
181
|
+
* on a `bool` property a string that is not a boolean wire form (`"0"`, `"1"`, `"true"`, `"false"`)
|
|
182
|
+
* is kept rather than read as `false`. The pass-through is not reported as a mistake: a robot
|
|
183
|
+
* reporting ten Raw DPs on every push logged ten warnings a time saying its properties were
|
|
184
|
+
* misdeclared, which is how a real warning goes unread.
|
|
184
185
|
*/
|
|
185
186
|
raw?: true;
|
|
186
187
|
/**
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live view of a T8223 doorbell (Video Doorbell C31) over a portal `call` {@link RtcSession}.
|
|
3
|
+
*
|
|
4
|
+
* The doorbell is its own station and streams no PTCS framing: its video arrives as plain RTP on the
|
|
5
|
+
* offer's H.264 video m-line, which a {@link RtcPeerOptions.mediaTrack} peer hands over as `rtp`. This
|
|
6
|
+
* stream depacketizes it into whole Annex-B access units, one per frame. Each stream owns its session:
|
|
7
|
+
* starting it places the call, stopping it hangs up.
|
|
8
|
+
*/
|
|
9
|
+
import { EventEmitter } from "node:events";
|
|
10
|
+
import type { LiveStreamHandle } from "../../core/contracts.js";
|
|
11
|
+
import { type Logger } from "../../core/logger.js";
|
|
12
|
+
import type { RtcSession } from "./session.js";
|
|
13
|
+
/**
|
|
14
|
+
* H.264 RTP depacketizer (RFC 6184 single NAL / STAP-A / FU-A). Feed whole RTP packets; it calls `onUnit`
|
|
15
|
+
* with each complete Annex-B access unit when the marker bit closes it. A unit with a sequence gap or an
|
|
16
|
+
* unterminated fragment is dropped, never delivered short. RTCP multiplexed on the same transport (PT
|
|
17
|
+
* 200-207, which reads as 72-79 in the RTP payload-type bits) is skipped.
|
|
18
|
+
*/
|
|
19
|
+
export declare class H264Depacketizer {
|
|
20
|
+
private readonly onUnit;
|
|
21
|
+
private nals;
|
|
22
|
+
private fu;
|
|
23
|
+
private lastSeq;
|
|
24
|
+
private broken;
|
|
25
|
+
constructor(onUnit: (unit: Buffer, timestamp: number) => void);
|
|
26
|
+
push(packet: Buffer): void;
|
|
27
|
+
}
|
|
28
|
+
/** A `LiveStreamHandle` over one portal `call` to a T8223 doorbell. */
|
|
29
|
+
export declare class RtcCallLiveStream extends EventEmitter implements LiveStreamHandle {
|
|
30
|
+
private readonly session;
|
|
31
|
+
private readonly logger;
|
|
32
|
+
private running;
|
|
33
|
+
private readonly depacketizer;
|
|
34
|
+
/** `session` is a session on a {@link RtcPeerOptions.mediaTrack} peer, not yet connected. */
|
|
35
|
+
constructor(session: RtcSession, logger?: Logger);
|
|
36
|
+
start(): this;
|
|
37
|
+
stop(): void;
|
|
38
|
+
private onUnit;
|
|
39
|
+
private fail;
|
|
40
|
+
private teardown;
|
|
41
|
+
}
|
|
@@ -33,6 +33,11 @@ export interface RtcRoute {
|
|
|
33
33
|
signalingMode?: "call" | "scall";
|
|
34
34
|
/** Candidate policy used by the station's exchange; defaults to relay-only. */
|
|
35
35
|
iceTransportPolicy?: "relay" | "all";
|
|
36
|
+
/**
|
|
37
|
+
* The station streams video as RTP on its offer's video track and serves no data channel, so it takes
|
|
38
|
+
* no portal command; see `RtcPeerOptions.mediaTrack`.
|
|
39
|
+
*/
|
|
40
|
+
mediaTrack?: boolean;
|
|
36
41
|
stationSn: string;
|
|
37
42
|
/**
|
|
38
43
|
* The station's `member.admin_user_id`, the account the session and the payload name; the logged-in
|
|
@@ -66,6 +71,7 @@ export declare class RtcCommandRouter {
|
|
|
66
71
|
private readonly sessions;
|
|
67
72
|
private readonly lives;
|
|
68
73
|
private readonly warm;
|
|
74
|
+
private readonly callSources;
|
|
69
75
|
constructor(deps: RtcCommandRouterDeps);
|
|
70
76
|
dispatchCommand(route: RtcRoute, cmd: Command): Promise<void>;
|
|
71
77
|
/**
|
|
@@ -73,6 +79,12 @@ export declare class RtcCommandRouter {
|
|
|
73
79
|
* session. Recording has no wire here and is refused; the optional members it has no wire for are absent.
|
|
74
80
|
*/
|
|
75
81
|
mediaProviderFor(route: RtcLiveRoute): MediaProvider;
|
|
82
|
+
/**
|
|
83
|
+
* Live view of a station on a {@link RtcRoute.mediaTrack} route: a live feed, a readable feed and a live
|
|
84
|
+
* still, from one shared source per station whose every warm-up places a fresh `call` on its own
|
|
85
|
+
* session. Recording has no wire here and is refused.
|
|
86
|
+
*/
|
|
87
|
+
trackMediaProviderFor(route: RtcRoute): MediaProvider;
|
|
76
88
|
/**
|
|
77
89
|
* Tear down every station session (logout / shutdown). Each live source is disposed before its session
|
|
78
90
|
* closes, so its consumers end cleanly and its stop still rides the session.
|
|
@@ -97,6 +109,7 @@ export declare class RtcCommandRouter {
|
|
|
97
109
|
* not establish physical actuation. The command is sent once, bounded by {@link ACK_TIMEOUT_MS}.
|
|
98
110
|
*/
|
|
99
111
|
private sendAwaitAck;
|
|
112
|
+
private newSession;
|
|
100
113
|
private stationSession;
|
|
101
114
|
/**
|
|
102
115
|
* One bounded bring-up: the session is up when its command channel opens, and it fails, with the
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
* plays the DTLS client (`active`) and opens its channels on the client's even SCTP stream ids, which
|
|
13
13
|
* is what the hub pairs with.
|
|
14
14
|
*
|
|
15
|
+
* With {@link RtcPeerOptions.mediaTrack} the remote is a T8223 doorbell instead: its offer bundles an Opus
|
|
16
|
+
* audio, an H.264 video and an `m=application` section, but it never completes the SCTP association. The
|
|
17
|
+
* application section is rejected before the offer is applied, no data channel is declared, and the
|
|
18
|
+
* video m-line's RTP is emitted as `rtp`.
|
|
19
|
+
*
|
|
15
20
|
* The native peer is injectable.
|
|
16
21
|
*/
|
|
17
22
|
import { EventEmitter } from "node:events";
|
|
@@ -30,6 +35,11 @@ export interface RtcPeerOptions {
|
|
|
30
35
|
createPeer?: NativePeerFactory;
|
|
31
36
|
/** Candidate policy for the station's exchange; the default is relay-only. */
|
|
32
37
|
iceTransportPolicy?: "relay" | "all";
|
|
38
|
+
/**
|
|
39
|
+
* The remote streams its video as RTP on the offer's video m-line and serves no data channel: the
|
|
40
|
+
* offer's application section is rejected and the video track's packets are emitted as `rtp`.
|
|
41
|
+
*/
|
|
42
|
+
mediaTrack?: boolean;
|
|
33
43
|
logger?: Logger;
|
|
34
44
|
}
|
|
35
45
|
export interface RtcPeerEvents {
|
|
@@ -39,6 +49,8 @@ export interface RtcPeerEvents {
|
|
|
39
49
|
data: [frame: Buffer, linkType: number];
|
|
40
50
|
/** The hub echoed the portal keepalive on the command channel. */
|
|
41
51
|
keepaliveEcho: [];
|
|
52
|
+
/** One RTP (or multiplexed RTCP) packet from the offer's video m-line, in a {@link RtcPeerOptions.mediaTrack} peer. */
|
|
53
|
+
rtp: [packet: Buffer];
|
|
42
54
|
iceCandidate: [candidate: string];
|
|
43
55
|
iceGatheringComplete: [];
|
|
44
56
|
connectionState: [state: string];
|
|
@@ -58,6 +70,8 @@ export declare class RtcPeer extends EventEmitter<RtcPeerEvents> {
|
|
|
58
70
|
private remoteMid;
|
|
59
71
|
private handlingOffer;
|
|
60
72
|
private channelsCreated;
|
|
73
|
+
private videoMid?;
|
|
74
|
+
private videoTrack?;
|
|
61
75
|
private gatheringDone;
|
|
62
76
|
/** Set by the framer's wire callback when the native channel refused a packet mid-send. */
|
|
63
77
|
private wireSendFailed;
|
|
@@ -75,7 +89,9 @@ export declare class RtcPeer extends EventEmitter<RtcPeerEvents> {
|
|
|
75
89
|
* only what is announced. The offer goes in before the channels are declared: `createDataChannel` on
|
|
76
90
|
* a peer without a remote description starts its own negotiation and leaves it in `have-local-offer`.
|
|
77
91
|
* The data channels need no m-line of their own; the offer's SCTP m-line carries them. Only an ANSWER
|
|
78
|
-
* is taken as the local description: one read in `have-local-offer` is an offer.
|
|
92
|
+
* is taken as the local description: one read in `have-local-offer` is an offer. A
|
|
93
|
+
* {@link RtcPeerOptions.mediaTrack} peer applies the offer with its application section rejected,
|
|
94
|
+
* declares no channels, and adds remote candidates on the offer's first (bundle-tagged) mid.
|
|
79
95
|
*/
|
|
80
96
|
handleRemoteOffer(offerSdp: string): Promise<string>;
|
|
81
97
|
addRemoteCandidate(candidate: string): void;
|
|
@@ -99,6 +115,8 @@ export declare class RtcPeer extends EventEmitter<RtcPeerEvents> {
|
|
|
99
115
|
* keepalive on the command channel is unframed and surfaces as `keepaliveEcho` instead.
|
|
100
116
|
*/
|
|
101
117
|
private wireChannel;
|
|
118
|
+
/** Take the offer's video m-line as the one track whose RTP is emitted; any other track is left alone. */
|
|
119
|
+
private wireVideoTrack;
|
|
102
120
|
private addNow;
|
|
103
121
|
private flushPending;
|
|
104
122
|
}
|
|
@@ -40,3 +40,11 @@ export declare function forceDtlsRole(sdp: string, role: "active" | "passive"):
|
|
|
40
40
|
* the hub sends and parses. The native peer hands out the SDP attribute line (`a=candidate:...`).
|
|
41
41
|
*/
|
|
42
42
|
export declare function toWireCandidate(candidate: string): string;
|
|
43
|
+
/** The `a=mid` of the first m-section of `kind` in an SDP; `undefined` when it has none. */
|
|
44
|
+
export declare function midOf(sdp: string, kind: "audio" | "video" | "application"): string | undefined;
|
|
45
|
+
/**
|
|
46
|
+
* Reject an offer's `m=application` (data channel) section: port 0 and out of the BUNDLE group. The
|
|
47
|
+
* remote never completes the SCTP association, so a peer waiting on it fails; without it the peer
|
|
48
|
+
* connects on DTLS-SRTP alone.
|
|
49
|
+
*/
|
|
50
|
+
export declare function rejectDataSection(sdp: string): string;
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
* the signalling socket drops or {@link RtcSession.close} is called, even while the peer connection is
|
|
18
18
|
* nominally up.
|
|
19
19
|
*
|
|
20
|
+
* A session whose peer is a {@link RtcPeerOptions.mediaTrack} peer has no command channel: `close` fires
|
|
21
|
+
* when the peer fails or closes, and the video track's packets are emitted as `rtp`.
|
|
22
|
+
*
|
|
20
23
|
* Both the signalling client and the peer are injectable.
|
|
21
24
|
*/
|
|
22
25
|
import { EventEmitter } from "node:events";
|
|
@@ -37,6 +40,8 @@ export interface RtcSessionEvents {
|
|
|
37
40
|
mediaData: [frame: Buffer];
|
|
38
41
|
/** The hub echoed the portal keepalive on the command channel. */
|
|
39
42
|
keepaliveEcho: [];
|
|
43
|
+
/** One packet of the video track, in a {@link RtcPeerOptions.mediaTrack} session. */
|
|
44
|
+
rtp: [packet: Buffer];
|
|
40
45
|
}
|
|
41
46
|
export declare class RtcSession extends EventEmitter<RtcSessionEvents> {
|
|
42
47
|
private readonly opts;
|
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.68",
|
|
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",
|