@mega-yfue/eufy-sdk 0.5.0-beta.66 → 0.5.0-beta.67

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.
@@ -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
  /**
@@ -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.66",
3
+ "version": "0.5.0-beta.67",
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",