@mega-yfue/eufy-sdk 0.5.0-beta.9 → 0.5.0-beta.90
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/device-registry.d.ts +24 -2
- package/dist/client/eufy-mega.d.ts +73 -8
- package/dist/client/types.d.ts +1 -1
- package/dist/core/contracts.d.ts +96 -41
- package/dist/core/store.d.ts +4 -0
- package/dist/core/types.d.ts +2 -0
- package/dist/index.js +4152 -719
- package/dist/index.js.map +4 -4
- package/dist/model/capabilities/arming.d.ts +39 -4
- package/dist/model/capabilities/battery.d.ts +6 -1
- package/dist/model/capabilities/camera.d.ts +61 -18
- package/dist/model/capabilities/dp-catalog.d.ts +4 -3
- package/dist/model/capabilities/index.d.ts +17 -4
- package/dist/model/capabilities/keypad.d.ts +20 -23
- package/dist/model/capabilities/light.d.ts +3 -0
- package/dist/model/capabilities/lock.d.ts +42 -16
- package/dist/model/capabilities/motion.d.ts +17 -2
- package/dist/model/capabilities/mower.d.ts +230 -0
- package/dist/model/capabilities/rtsp.d.ts +16 -3
- package/dist/model/capabilities/smart-drop.d.ts +20 -0
- package/dist/model/capabilities/suction.d.ts +5 -5
- package/dist/model/capabilities/types.d.ts +10 -0
- package/dist/model/capabilities/vacuum-clean.d.ts +19 -13
- package/dist/model/classify.d.ts +1 -2
- package/dist/model/device-family.d.ts +24 -0
- package/dist/model/param-dictionary.d.ts +6 -0
- package/dist/model/param-namespace.d.ts +1 -1
- package/dist/model/types.d.ts +20 -12
- package/dist/transport/abortable.d.ts +8 -0
- package/dist/transport/http/mega-client.d.ts +43 -2
- package/dist/transport/mqtt/broker-discovery.d.ts +2 -1
- package/dist/transport/mqtt/command-router.d.ts +3 -2
- package/dist/transport/p2p/audio-frame.d.ts +14 -0
- package/dist/transport/p2p/codec.d.ts +32 -0
- package/dist/transport/p2p/command-router.d.ts +93 -31
- package/dist/transport/p2p/fmp4.d.ts +12 -2
- package/dist/transport/p2p/keyed-payload.d.ts +112 -0
- package/dist/transport/p2p/live-trace.d.ts +25 -2
- package/dist/transport/p2p/media.d.ts +3 -5
- package/dist/transport/p2p/p2p-session.d.ts +81 -12
- package/dist/transport/p2p/readable-egress.d.ts +3 -0
- package/dist/transport/p2p/recording-download.d.ts +4 -3
- package/dist/transport/p2p/shared-live-source.d.ts +44 -56
- package/dist/transport/p2p/talkback.d.ts +21 -10
- package/dist/transport/p2p/video.d.ts +2 -0
- package/dist/transport/push/push-client.d.ts +5 -0
- package/dist/transport/rtc/call-live.d.ts +41 -0
- package/dist/transport/rtc/command-router.d.ts +145 -0
- package/dist/transport/rtc/live.d.ts +76 -0
- package/dist/transport/rtc/peer.d.ts +122 -0
- package/dist/transport/rtc/portal-keepalive.d.ts +11 -0
- package/dist/transport/rtc/portal-packet.d.ts +72 -0
- package/dist/transport/rtc/ptcs-framer.d.ts +86 -0
- package/dist/transport/rtc/scall-sdp.d.ts +50 -0
- package/dist/transport/rtc/session.d.ts +99 -0
- package/dist/transport/rtc/signaling.d.ts +139 -0
- package/dist/transport/token-echo.d.ts +8 -0
- package/dist/transport/tuya/account.d.ts +8 -5
- package/dist/transport/tuya/client.d.ts +19 -5
- package/dist/transport/tuya/command-router.d.ts +10 -14
- package/dist/transport/tuya/dp-codec.d.ts +2 -23
- package/dist/transport/tuya/et3.d.ts +22 -0
- package/dist/transport/tuya/request.d.ts +18 -8
- package/dist/transport/tuya/sign.d.ts +7 -0
- package/package.json +7 -6
|
@@ -16,7 +16,10 @@ export interface DeviceRecord {
|
|
|
16
16
|
deviceType?: number;
|
|
17
17
|
model?: string;
|
|
18
18
|
category?: string;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* The app-shown device name: `alias_name` first on a vacuum, `device_name` first otherwise;
|
|
21
|
+
* see {@link CloudRecord.name}.
|
|
22
|
+
*/
|
|
20
23
|
name?: string;
|
|
21
24
|
/** Parent HomeBase serial when attached (topology signal; see {@link CloudRecord.parentSn}). */
|
|
22
25
|
parentSn?: string;
|
|
@@ -92,6 +95,8 @@ export declare class DeviceRegistry {
|
|
|
92
95
|
private overlayRefusalReported;
|
|
93
96
|
/** Per-serial capability cache for {@link capabilitiesForDevice}; `null` = negative hit. */
|
|
94
97
|
private readonly deviceCapsCache;
|
|
98
|
+
/** Per-serial cloud data points of an AIoT robot, joined beneath the params in {@link record} — see {@link loadCloudDps}. */
|
|
99
|
+
private readonly cloudDps;
|
|
95
100
|
/** Per-serial realtime state, keyed by param id in the device's own namespace (see {@link DeviceRecord.dpParams}). */
|
|
96
101
|
private readonly dpParams;
|
|
97
102
|
/** Callers blocked in {@link awaitRealtimeState}, released by the device's first report. */
|
|
@@ -181,9 +186,26 @@ export declare class DeviceRegistry {
|
|
|
181
186
|
* transport stacks own the claim predicate over the record.
|
|
182
187
|
*/
|
|
183
188
|
require(sn: string): EufyDevice;
|
|
189
|
+
/**
|
|
190
|
+
* Hold an AIoT robot's cloud data points: the cloud's last-known value of each one it has reported,
|
|
191
|
+
* from `getDeviceRelationList`'s per-device `dps`.
|
|
192
|
+
*
|
|
193
|
+
* The device list carries only some of a robot's data points, so a point the robot publishes on change
|
|
194
|
+
* alone (its scene list, its schedules, its consumables) has no value until it next reports. The
|
|
195
|
+
* relation list holds every one, for an account the robot is shared with as well as its owner.
|
|
196
|
+
*
|
|
197
|
+
* Kept apart from the device list's params rather than merged into them: those are what a poll diffs,
|
|
198
|
+
* and a diff retires the realtime report for that id, so a cloud value lagging the robot's last report
|
|
199
|
+
* would replace it. {@link record} joins this beneath both halves. A failure keeps what was held.
|
|
200
|
+
*
|
|
201
|
+
* Limited to {@link CLOUD_DPS_MODELS}, the models whose relation-list `dps` are checked to carry the
|
|
202
|
+
* DP numbers their realtime reports use.
|
|
203
|
+
*/
|
|
204
|
+
private loadCloudDps;
|
|
184
205
|
/**
|
|
185
206
|
* Resolve a serial to a {@link DeviceRecord} with current params: starts from the device-list params, then
|
|
186
|
-
* overlays a fresh `get_device_param_list` when that call is available to this account.
|
|
207
|
+
* overlays a fresh `get_device_param_list` when that call is available to this account. An AIoT robot's
|
|
208
|
+
* cloud data points ({@link loadCloudDps}) sit beneath both, filling only ids neither carries.
|
|
187
209
|
*
|
|
188
210
|
* The overlay is **owner-gated** — a shared or member account is refused it for every device, permanently,
|
|
189
211
|
* which {@link OWNER_ONLY_CODE} identifies — so when it is unavailable the device list is the source
|
|
@@ -98,6 +98,8 @@ export declare class EufyMega extends EventEmitter {
|
|
|
98
98
|
private readonly prewarmTiers;
|
|
99
99
|
/** Transport-side owner of the P2P sessions + all wire senders. */
|
|
100
100
|
private readonly p2p;
|
|
101
|
+
/** Transport-side owner of the T9000 station sessions (sibling of {@link p2p}). */
|
|
102
|
+
private readonly rtc;
|
|
101
103
|
/** Transport-side owner of the secure-MQTT ff09 lock/garage command path (sibling of {@link p2p}). */
|
|
102
104
|
private readonly mqtt;
|
|
103
105
|
/** Transport-side owner of the legacy Tuya REST command path for non-AIoT vacuums (G-series). */
|
|
@@ -119,6 +121,8 @@ export declare class EufyMega extends EventEmitter {
|
|
|
119
121
|
private readonly mapStores;
|
|
120
122
|
/** Re-armed after each cloud-param poll; cancelled by {@link disconnect}. */
|
|
121
123
|
private readonly pollTimer;
|
|
124
|
+
/** Armed while a failed push bring-up waits to be retried; cancelled by {@link disconnect}. */
|
|
125
|
+
private readonly pushRetryTimer;
|
|
122
126
|
constructor(opts: EufyMegaOptions);
|
|
123
127
|
/**
|
|
124
128
|
* Builds the inbound listener that converts ThingClips DP maps to realtime capability state.
|
|
@@ -181,7 +185,8 @@ export declare class EufyMega extends EventEmitter {
|
|
|
181
185
|
*
|
|
182
186
|
* Skipped entirely for a device with a pollable cloud record, for an already-reporting device, and
|
|
183
187
|
* when realtime is off — so the common path adds nothing. Resolving on timeout leaves a silent device
|
|
184
|
-
* read-less rather than failing the lookup.
|
|
188
|
+
* read-less rather than failing the lookup. A Tuya clean-line device waits on the Tuya cloud read the
|
|
189
|
+
* bring-up started, so no broker is brought up for it.
|
|
185
190
|
*/
|
|
186
191
|
private awaitFirstRealtimeState;
|
|
187
192
|
/**
|
|
@@ -241,8 +246,17 @@ export declare class EufyMega extends EventEmitter {
|
|
|
241
246
|
* to land it under the property's name.
|
|
242
247
|
*/
|
|
243
248
|
private rebindReads;
|
|
244
|
-
/**
|
|
249
|
+
/**
|
|
250
|
+
* The distinct credential scopes the current MQTT roster needs — no devices on a scope, no connection.
|
|
251
|
+
* A Tuya clean-line device (`eufy_home_tuya`) needs none: the broker refuses its topics, and its state
|
|
252
|
+
* is read from the Tuya cloud.
|
|
253
|
+
*/
|
|
245
254
|
private mqttScopesInUse;
|
|
255
|
+
/**
|
|
256
|
+
* Read every Tuya clean-line device's DPs from the Tuya cloud and deliver them as realtime state,
|
|
257
|
+
* concurrently. A failed read is reported, not fatal, and leaves that device's state as it was.
|
|
258
|
+
*/
|
|
259
|
+
private readTuyaDevices;
|
|
246
260
|
/**
|
|
247
261
|
* Emit a semantic capability event whose name is only known at runtime (`decodeCapabilityEvent`
|
|
248
262
|
* returns a plain-string `event` + `payload`). The typed `emit` overload requires a literal event
|
|
@@ -266,6 +280,8 @@ export declare class EufyMega extends EventEmitter {
|
|
|
266
280
|
* only "differs from what was read before", which state already on hand can satisfy spuriously — and the
|
|
267
281
|
* caller that has no expectation is the push path, where the signal itself is the news that a re-read is owed.
|
|
268
282
|
*
|
|
283
|
+
* Only a write's observation throws at the deadline; a push with nothing moved answers `false`.
|
|
284
|
+
*
|
|
269
285
|
* The cloud half is asked for through {@link DeviceRegistry.refreshedList}, never by fetching the account
|
|
270
286
|
* list outright. The fetch is account-wide — one house list plus one device list per house — so a param that
|
|
271
287
|
* never converges would otherwise spend a whole burst of those every iteration of this loop, and concurrent
|
|
@@ -291,6 +307,8 @@ export declare class EufyMega extends EventEmitter {
|
|
|
291
307
|
* manager's own log, since by then nothing is waiting to receive it.
|
|
292
308
|
*/
|
|
293
309
|
private recycleStandaloneSession;
|
|
310
|
+
/** Reopen a wired station that {@link recycleStandaloneSession} closed, while the generation that warmed it is current. */
|
|
311
|
+
private reopenWiredStation;
|
|
294
312
|
/**
|
|
295
313
|
* Whether the DECODED property now reads what the write asked for.
|
|
296
314
|
*
|
|
@@ -395,6 +413,14 @@ export declare class EufyMega extends EventEmitter {
|
|
|
395
413
|
* other kind is P2P.
|
|
396
414
|
*/
|
|
397
415
|
private commandSinkFor;
|
|
416
|
+
/**
|
|
417
|
+
* The station a device's commands go through: the device itself, or the HomeBase the registry links it
|
|
418
|
+
* to. Answers the target record, the station record and serial, the station's `device_type`, and the
|
|
419
|
+
* `t9000` / `t8n00` / `t8223` family flags.
|
|
420
|
+
*/
|
|
421
|
+
private stationOf;
|
|
422
|
+
/** The vendor DeviceType of the station a device hangs off; `undefined` for a station itself. */
|
|
423
|
+
private stationDeviceTypeOf;
|
|
398
424
|
/**
|
|
399
425
|
* Combine explicit P2P media with the optional passive push-thumbnail provider.
|
|
400
426
|
*
|
|
@@ -409,15 +435,32 @@ export declare class EufyMega extends EventEmitter {
|
|
|
409
435
|
* Choose the transport stack for one command — the routing half of {@link commandSinkFor}.
|
|
410
436
|
*
|
|
411
437
|
* The `ff09-*` kinds share ONE frame that rides either transport, so each stack is asked whether it
|
|
412
|
-
* drives this device (`claimsDevice`) rather than inferring from the kind
|
|
413
|
-
*
|
|
438
|
+
* drives this device (`claimsDevice`) rather than inferring from the kind; `keyed-payload-actuate` (the
|
|
439
|
+
* classic Wi-Fi lock) is routed the same way, a channel-less actuation claimed by the stack that drives
|
|
440
|
+
* the lock. `registry.require` throws on an unloaded serial, so a routing decision never silently falls
|
|
441
|
+
* through to the wrong transport.
|
|
414
442
|
* The `eufy_life` DP writes (smart lights) are secure-MQTT-only. `aiot-dp` routes to either the
|
|
415
443
|
* Anker AIoT MQTT stack or the legacy Tuya REST router depending on the device's category
|
|
416
444
|
* (`eufy_home_tuya` → Tuya, everything else → MQTT). The capability layer emits a single `aiot-dp`
|
|
417
|
-
* kind and stays transport-agnostic; only the facade sees both sides and decides here.
|
|
418
|
-
*
|
|
445
|
+
* kind and stays transport-agnostic; only the facade sees both sides and decides here. A T9000 or T8N00
|
|
446
|
+
* station and the devices attached to it go over RTC, with an attached device's command refused
|
|
447
|
+
* unless its channel resolves back to it on the station. T8N00 uses full SDP and admits direct and
|
|
448
|
+
* relay candidates; T9000 retains compact SDP and relay-only candidates. A T8223 doorbell's route serves
|
|
449
|
+
* media only, and the RTC router refuses a command on it. Everything else is P2P.
|
|
419
450
|
*/
|
|
420
451
|
private routeCommand;
|
|
452
|
+
/**
|
|
453
|
+
* The RTC route for a T9000 or T8N00 station or a device attached to one, or a T8223 doorbell that is its
|
|
454
|
+
* own station, decided by the station's model classification; `undefined` for any other device. A T8N00
|
|
455
|
+
* route carries its native `call` signalling; a T8223 route does too, and streams its video on a media
|
|
456
|
+
* track. The admin id is the station's `member.admin_user_id`, else the logged-in user.
|
|
457
|
+
*/
|
|
458
|
+
private rtcRouteFor;
|
|
459
|
+
/**
|
|
460
|
+
* The RTC media provider for a station on a media-track route, or a camera attached to a T9000 whose
|
|
461
|
+
* `device_channel` resolves back to it on the station; `undefined` otherwise, unless an NVR camera has its own dedicated media call.
|
|
462
|
+
*/
|
|
463
|
+
private rtcMediaFor;
|
|
421
464
|
/**
|
|
422
465
|
* List + classify devices across all houses (mega API). Each device is tagged with its API backend
|
|
423
466
|
* + realtime transport. Camera/HomeBase records still appear here for inventory; driving them is
|
|
@@ -425,8 +468,9 @@ export declare class EufyMega extends EventEmitter {
|
|
|
425
468
|
*
|
|
426
469
|
* Side-effect: registers `eufy_home_tuya` devices with the Tuya command router so
|
|
427
470
|
* the command dispatcher can resolve a eufy SN → Tuya devId without a separate lookup.
|
|
428
|
-
* The Tuya id is
|
|
429
|
-
* `
|
|
471
|
+
* The Tuya id is the first non-empty `tuya_uuid`, `tuya_virtual_id`, `tuya_device_id` or
|
|
472
|
+
* `virtualId` on the raw cloud record, else the device's serial: the app keys its Tuya device by
|
|
473
|
+
* the eufy device id, and a record that carries none of those fields is read under its serial.
|
|
430
474
|
*
|
|
431
475
|
* A partial cloud outage still resolves, with the devices that answered plus the ones already known — but a
|
|
432
476
|
* session the cloud has rejected REJECTS, with {@link SessionExpiredError}. An empty list would be
|
|
@@ -529,6 +573,14 @@ export declare class EufyMega extends EventEmitter {
|
|
|
529
573
|
private ensureRealtimeGeneration;
|
|
530
574
|
/** Start all selected transports concurrently and settle their owning generation once. */
|
|
531
575
|
private startRealtimeGeneration;
|
|
576
|
+
/**
|
|
577
|
+
* Bring push up again after the generation's startup attempt failed, backing off between attempts
|
|
578
|
+
* until one connects, the generation is superseded, or it is aborted by {@link disconnect}.
|
|
579
|
+
*
|
|
580
|
+
* The MCS socket reconnects on its own only once it has connected. The settled
|
|
581
|
+
* {@link RealtimeReadiness} is a startup snapshot and is not revised when a retry connects.
|
|
582
|
+
*/
|
|
583
|
+
private retryPush;
|
|
532
584
|
/** Resolve a generation with an immutable count snapshot; later transport completions are ignored. */
|
|
533
585
|
private settleRealtimeGeneration;
|
|
534
586
|
/**
|
|
@@ -571,6 +623,11 @@ export declare class EufyMega extends EventEmitter {
|
|
|
571
623
|
* an unchanged VALUE, which is no state change to report but is fresh proof the device is alive. A
|
|
572
624
|
* device absent from the previous pass is skipped — first sight is discovery, not a transition;
|
|
573
625
|
* {@link deviceState} answers an initial reading.
|
|
626
|
+
*
|
|
627
|
+
* Then re-reads every Tuya clean-line robot from the Tuya cloud ({@link readTuyaDevices}): its state
|
|
628
|
+
* is not in the cloud record this pass diffs and no live channel carries it, so the poll is where it
|
|
629
|
+
* stays fresh. That read runs after the `try`, not inside it, so a cloud error on the list diff does
|
|
630
|
+
* not skip it; it reports its own failures and never rejects.
|
|
574
631
|
*/
|
|
575
632
|
private pollOnce;
|
|
576
633
|
/**
|
|
@@ -674,6 +731,11 @@ export declare class EufyMega extends EventEmitter {
|
|
|
674
731
|
* — an already-open session resolves immediately.
|
|
675
732
|
*/
|
|
676
733
|
connectStation(deviceSn: string, signal?: AbortSignal): Promise<void>;
|
|
734
|
+
/**
|
|
735
|
+
* Keep a T9000 station's RTC command session open and warm, so a command fires without a cold reconnect.
|
|
736
|
+
* Answers the hold's disposer; throws for any other station, and while logged out.
|
|
737
|
+
*/
|
|
738
|
+
keepStationWarm(stationSn: string): () => void;
|
|
677
739
|
/** Eagerly open P2P sessions for WIRED stations only (persistent — they don't drain). Battery
|
|
678
740
|
* stations stay closed until an on-demand open. Best-effort per station. */
|
|
679
741
|
private warmWiredP2P;
|
|
@@ -748,6 +810,9 @@ export declare class EufyMega extends EventEmitter {
|
|
|
748
810
|
/**
|
|
749
811
|
* Subscribe the devices on one credential scope; a failing subscribe is reported, not fatal (one
|
|
750
812
|
* unreachable device must not stop the rest of the roster from coming up).
|
|
813
|
+
*
|
|
814
|
+
* A Tuya clean-line device (`eufy_home_tuya`) is not subscribed: its state lives on the Tuya cloud,
|
|
815
|
+
* and the broker refuses its `eufy_home` topics on every credential.
|
|
751
816
|
*/
|
|
752
817
|
private subscribeMqttDevices;
|
|
753
818
|
/**
|
package/dist/client/types.d.ts
CHANGED
package/dist/core/contracts.d.ts
CHANGED
|
@@ -171,9 +171,10 @@ export declare class RecordingDownloadError extends Error {
|
|
|
171
171
|
* A recording downloaded from the station, as elementary streams.
|
|
172
172
|
*
|
|
173
173
|
* `video` is Annex-B H.264 in decode order, `audio` AAC-LC 16 kHz mono in ADTS framing (absent when the
|
|
174
|
-
* recording carried none that could be decoded). `frames` counts the distinct video
|
|
175
|
-
* `missingFrames`
|
|
176
|
-
*
|
|
174
|
+
* recording carried none that could be decoded). `frames` counts the distinct video sequences emitted.
|
|
175
|
+
* `missingFrames` counts gaps between received sequences and received sequences that yielded no usable
|
|
176
|
+
* picture, including at either end. `durationMs` spans the first to the last emitted picture by the
|
|
177
|
+
* camera's own timestamps; `fps` is derived from those stamps.
|
|
177
178
|
*/
|
|
178
179
|
export interface RecordingDownload {
|
|
179
180
|
video: Buffer;
|
|
@@ -230,7 +231,7 @@ export interface StateConvergenceFailure {
|
|
|
230
231
|
property: string;
|
|
231
232
|
param: number;
|
|
232
233
|
/** The RAW param value the write asked the device to report. */
|
|
233
|
-
expected
|
|
234
|
+
expected: boolean | number | string;
|
|
234
235
|
/** What the param actually read when the deadline passed, absent where the device reported none at all. */
|
|
235
236
|
observed?: boolean | number | string;
|
|
236
237
|
timeoutMs: number;
|
|
@@ -248,7 +249,7 @@ export declare class StateConvergenceError extends Error {
|
|
|
248
249
|
readonly sn: string;
|
|
249
250
|
readonly property: string;
|
|
250
251
|
readonly param: number;
|
|
251
|
-
readonly expected
|
|
252
|
+
readonly expected: boolean | number | string;
|
|
252
253
|
readonly observed?: boolean | number | string;
|
|
253
254
|
readonly timeoutMs: number;
|
|
254
255
|
constructor(failure: StateConvergenceFailure);
|
|
@@ -263,10 +264,11 @@ export declare class StateConvergenceError extends Error {
|
|
|
263
264
|
*/
|
|
264
265
|
export type ScalarForm = "auto" | "int-string" | "direct-binary";
|
|
265
266
|
/**
|
|
266
|
-
* The identity fields
|
|
267
|
-
* plus the frame's own direction bit and user-attribution fields
|
|
268
|
-
*
|
|
269
|
-
*
|
|
267
|
+
* The identity fields an actuation frame is built from — for `ff09`, the AES key/IV inputs
|
|
268
|
+
* (`adminUserId`/`deviceSn`) plus the frame's own direction bit and user-attribution fields
|
|
269
|
+
* (`A3`/`A4`/`A5`); for the keyed-payload envelope, the same five fields, sealed into its inner JSON.
|
|
270
|
+
* Shared by both actuate intents; the `ff09` settings intents carry a narrower subset (the GET/SET frames
|
|
271
|
+
* omit the user-attribution fields).
|
|
270
272
|
*/
|
|
271
273
|
export interface Ff09Identity {
|
|
272
274
|
/** Actuation direction: `true` = engage (lock / close), `false` = release (unlock / open) — the frame's `A3` byte. */
|
|
@@ -328,6 +330,11 @@ export type Command = {
|
|
|
328
330
|
channel: number;
|
|
329
331
|
mValue3?: number;
|
|
330
332
|
form?: ScalarForm;
|
|
333
|
+
/**
|
|
334
|
+
* `"set-payload"`: a level-2 key that seals only this envelope (an RSA cipher's AES-128-ECB key)
|
|
335
|
+
* may seal it. Absent, the send waits for the key every level-2 sender uses.
|
|
336
|
+
*/
|
|
337
|
+
level2Sender?: "set-payload";
|
|
331
338
|
} | {
|
|
332
339
|
kind: "p2p-privacy-burst";
|
|
333
340
|
enabled: boolean;
|
|
@@ -347,6 +354,14 @@ export type Command = {
|
|
|
347
354
|
channel: number;
|
|
348
355
|
} | ({
|
|
349
356
|
kind: "ff09-actuate";
|
|
357
|
+
} & Ff09Identity)
|
|
358
|
+
/**
|
|
359
|
+
* `keyed-payload-actuate` — a `SET_PAYLOAD` envelope whose inner JSON is sealed with a fresh AES key
|
|
360
|
+
* that the envelope itself carries, wrapped for the device's own public key. P2P-only; the router
|
|
361
|
+
* resolves the device key and the member display name from the device record.
|
|
362
|
+
*/
|
|
363
|
+
| ({
|
|
364
|
+
kind: "keyed-payload-actuate";
|
|
350
365
|
} & Ff09Identity) | {
|
|
351
366
|
kind: "ff09-autolock";
|
|
352
367
|
adminUserId: string;
|
|
@@ -457,7 +472,7 @@ export interface CommandSink {
|
|
|
457
472
|
dispatch(cmd: Command): Promise<void>;
|
|
458
473
|
}
|
|
459
474
|
/**
|
|
460
|
-
* Inbound Tuya DP event contract — the transport parses
|
|
475
|
+
* Inbound Tuya DP event contract — the transport parses the Tuya cloud DP read
|
|
461
476
|
* (string-keyed object → numeric-keyed record) and delivers it through this interface. The
|
|
462
477
|
* transport owns the parse; a capability owns the semantics (which DP id means what). Mirrors
|
|
463
478
|
* {@link DpInboundFrame} for the AIoT MQTT path; the capability layer never names the Tuya framing.
|
|
@@ -487,6 +502,8 @@ export type VideoCodec = "h264" | "h265" | "av1";
|
|
|
487
502
|
* headers promise decodes to no picture at all.
|
|
488
503
|
*/
|
|
489
504
|
export interface LiveVideoFrame {
|
|
505
|
+
/** Source-reported unsigned 32-bit timestamp; unit and clock origin are unspecified. */
|
|
506
|
+
sourceTimestamp?: number;
|
|
490
507
|
/** True on an IDR — a unit a consumer may begin decoding at, never a continuation of an earlier one. */
|
|
491
508
|
keyframe: boolean;
|
|
492
509
|
/**
|
|
@@ -541,6 +558,12 @@ export interface LiveVideoConfig {
|
|
|
541
558
|
width: number;
|
|
542
559
|
height: number;
|
|
543
560
|
}
|
|
561
|
+
/** Video frame paired with its original SDK source-arrival time. */
|
|
562
|
+
export interface ReceivedVideoFrame {
|
|
563
|
+
readonly frame: LiveVideoFrame;
|
|
564
|
+
/** Wall-clock milliseconds when the shared source received the frame; preserved on replay. */
|
|
565
|
+
readonly timestampMs: number;
|
|
566
|
+
}
|
|
544
567
|
/**
|
|
545
568
|
* Elementary-stream audio codec of a {@link LiveAudioFrame}. The station declares it per frame as a
|
|
546
569
|
* byte in the `CMD_AUDIO_FRAME` header — unlike video, nothing is sniffed. These are the three values
|
|
@@ -548,19 +571,30 @@ export interface LiveVideoConfig {
|
|
|
548
571
|
* 7 → `mp4a.40.39`); it fails the stream on anything else.
|
|
549
572
|
*/
|
|
550
573
|
export type AudioCodec = "aac-lc" | "aac-eld" | "g711a";
|
|
574
|
+
/** The codec a camera's speaker plays: `aac-eld` on a camera whose speaker plays it, `aac-lc` otherwise. */
|
|
575
|
+
export type TalkbackCodec = Extract<AudioCodec, "aac-lc" | "aac-eld">;
|
|
551
576
|
/**
|
|
552
577
|
* One audio access unit, carrying the codec the station declared for it.
|
|
553
578
|
*
|
|
554
|
-
* Sample rate and channel count are
|
|
555
|
-
* 16 kHz mono for every audio type rather than reading them
|
|
556
|
-
*
|
|
579
|
+
* Sample rate and channel count are absent for `aac-lc` and `g711a`: they are not on the wire, and the v6 app
|
|
580
|
+
* assumes 16 kHz mono for every audio type rather than reading them. An `aac-lc` frame is one ADTS frame and
|
|
581
|
+
* `g711a` is raw A-law. An `aac-eld` frame is one raw access unit with no transport framing, so it carries the
|
|
582
|
+
* AudioSpecificConfig that decodes it.
|
|
557
583
|
*/
|
|
558
|
-
export
|
|
584
|
+
export type LiveAudioFrame = {
|
|
559
585
|
/** Codec declared in the frame header. */
|
|
560
|
-
codec:
|
|
561
|
-
/**
|
|
586
|
+
codec: "aac-lc" | "g711a";
|
|
587
|
+
/** One ADTS frame for `aac-lc`, raw A-law samples for `g711a`. */
|
|
562
588
|
data: Buffer;
|
|
563
|
-
|
|
589
|
+
config?: undefined;
|
|
590
|
+
} | {
|
|
591
|
+
/** Codec declared in the frame header. */
|
|
592
|
+
codec: "aac-eld";
|
|
593
|
+
/** One raw AAC-ELD access unit. */
|
|
594
|
+
data: Buffer;
|
|
595
|
+
/** AudioSpecificConfig (ISO/IEC 14496-3) that decodes `data`. */
|
|
596
|
+
config: Buffer;
|
|
597
|
+
};
|
|
564
598
|
/**
|
|
565
599
|
* One fragmented-MP4 (CMAF) output unit from the native muxer. `init` (the `ftyp`+`moov` init
|
|
566
600
|
* segment) is present exactly once, on the first fragment; every fragment carries a `moof`+`mdat`
|
|
@@ -655,10 +689,9 @@ export interface LiveStreamConsumer extends LiveStreamHandle {
|
|
|
655
689
|
* consumer receives, so a consumer holding media it has not been told the configuration of is not a state
|
|
656
690
|
* it can reach.
|
|
657
691
|
*
|
|
658
|
-
* Per consumer, against what
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
* would leave both holding an encoder built for media they never received.
|
|
692
|
+
* Per consumer, against what this consumer was last given: a mid-session join begins at its next live
|
|
693
|
+
* keyframe, and a consumer crossing its queue bound resynchronises onto a later IDR. Each receives
|
|
694
|
+
* the configuration of the media it is delivered, even when another consumer has already seen it.
|
|
662
695
|
*
|
|
663
696
|
* Only a consumer announces this, never a bare {@link LiveStreamHandle}: it is read from the parameter
|
|
664
697
|
* sets a shared source watches every frame for, which the raw pull underneath it does not do. That is
|
|
@@ -709,6 +742,10 @@ export interface LiveStreamConsumer extends LiveStreamHandle {
|
|
|
709
742
|
export interface AbortableCall {
|
|
710
743
|
signal?: AbortSignal;
|
|
711
744
|
}
|
|
745
|
+
/** Select an optical view; absence selects view 0. */
|
|
746
|
+
export interface MediaSourceSelection {
|
|
747
|
+
view?: number;
|
|
748
|
+
}
|
|
712
749
|
export interface SharedSourceHints {
|
|
713
750
|
/**
|
|
714
751
|
* Power source, a runtime device fact (`"battery"` incl. solar, or `"wired"`) — never a device-family
|
|
@@ -734,6 +771,8 @@ export interface SharedSourceHints {
|
|
|
734
771
|
* because an unbound model has no live client (hence `?.` at the call site).
|
|
735
772
|
*/
|
|
736
773
|
export interface MediaProvider {
|
|
774
|
+
/** Optical view identifiers supported by this bound media route. */
|
|
775
|
+
views?: readonly number[];
|
|
737
776
|
/**
|
|
738
777
|
* Return the latest validated push thumbnail retained in memory. This passive operation performs no
|
|
739
778
|
* network, storage, P2P, live-media, or transcoding work at call time. It rejects with
|
|
@@ -760,7 +799,7 @@ export interface MediaProvider {
|
|
|
760
799
|
timeoutMs?: number;
|
|
761
800
|
collectMs?: number;
|
|
762
801
|
skipKeyframes?: number;
|
|
763
|
-
} & SharedSourceHints & AbortableCall): Promise<{
|
|
802
|
+
} & SharedSourceHints & AbortableCall & MediaSourceSelection): Promise<{
|
|
764
803
|
jpeg: Buffer;
|
|
765
804
|
width: number;
|
|
766
805
|
height: number;
|
|
@@ -792,7 +831,7 @@ export interface MediaProvider {
|
|
|
792
831
|
* stream?.stop(); // detach this consumer
|
|
793
832
|
* ```
|
|
794
833
|
*/
|
|
795
|
-
live(opts?: SharedSourceHints & AbortableCall & Record<string, unknown>): Promise<LiveStreamConsumer>;
|
|
834
|
+
live(opts?: SharedSourceHints & AbortableCall & MediaSourceSelection & Record<string, unknown>): Promise<LiveStreamConsumer>;
|
|
796
835
|
/**
|
|
797
836
|
* Record `seconds` of video → an mp4/h264 buffer.
|
|
798
837
|
*
|
|
@@ -809,13 +848,16 @@ export interface MediaProvider {
|
|
|
809
848
|
}): Promise<Buffer>;
|
|
810
849
|
/**
|
|
811
850
|
* Open a video-only `node:stream` Readable over a shared source consumer — raw Annex-B bytes
|
|
812
|
-
* (default) or `objectMode` {@link LiveVideoFrame}s.
|
|
813
|
-
* {@link
|
|
814
|
-
*
|
|
851
|
+
* (default) or `objectMode` {@link LiveVideoFrame}s. With objectMode and arrivalTime, it yields
|
|
852
|
+
* {@link ReceivedVideoFrame} envelopes whose original source-arrival time survives retained replay.
|
|
853
|
+
* Raw mode ignores arrivalTime. Audio is available separately through {@link live} or muxed through
|
|
854
|
+
* {@link recordFragments}; it is never interleaved into raw video. The caller owns the Readable's
|
|
855
|
+
* lifetime, and destroying it releases the shared pull.
|
|
815
856
|
*/
|
|
816
857
|
openReadable?(opts?: {
|
|
817
858
|
objectMode?: boolean;
|
|
818
|
-
|
|
859
|
+
arrivalTime?: boolean;
|
|
860
|
+
} & SharedSourceHints & AbortableCall & MediaSourceSelection): Promise<import("node:stream").Readable>;
|
|
819
861
|
/**
|
|
820
862
|
* Continuously record the live feed as fragmented-MP4 (CMAF). The caller-owned
|
|
821
863
|
* {@link FragmentRecordingHandle} yields an init segment then keyframe-bounded media fragments,
|
|
@@ -855,6 +897,7 @@ export interface MediaProvider {
|
|
|
855
897
|
*/
|
|
856
898
|
p2pQuery?(subCmd: number, opts?: {
|
|
857
899
|
timeoutMs?: number;
|
|
900
|
+
payload?: Record<string, unknown>;
|
|
858
901
|
}): Promise<Record<string, unknown>>;
|
|
859
902
|
/**
|
|
860
903
|
* Generic control-payload request/reply query: send a `CONTROL_PAYLOAD` (1700) `{commandType,data}`
|
|
@@ -868,10 +911,11 @@ export interface MediaProvider {
|
|
|
868
911
|
}): Promise<Record<string, unknown>>;
|
|
869
912
|
}
|
|
870
913
|
/**
|
|
871
|
-
* An encoder that turns raw PCM into AAC-LC frames, supplied by the CALLER. The SDK ships none:
|
|
872
|
-
*
|
|
914
|
+
* An encoder that turns raw PCM into AAC-LC frames, supplied by the CALLER. The SDK ships none: an
|
|
915
|
+
* `aac-lc` speaker plays AAC-LC 16 kHz mono, and every plausible encoder is either a native
|
|
873
916
|
* dependency or an external process, both of which belong to the host rather than to a protocol SDK.
|
|
874
|
-
* It is unnecessary where the audio is already AAC — see {@link TalkbackHandle}.
|
|
917
|
+
* It is unnecessary where the audio is already AAC — see {@link TalkbackHandle}. A camera whose speaker
|
|
918
|
+
* plays `aac-eld` refuses an encoder: a talkback opened with one is rejected before the speaker path opens.
|
|
875
919
|
*
|
|
876
920
|
* `encode` receives 16-bit little-endian mono PCM at 16 kHz and returns whole ADTS frames, zero or
|
|
877
921
|
* more per call (an encoder buffers until it has a full 1024-sample block). `flush` drains a partial
|
|
@@ -886,17 +930,19 @@ export interface AacEncoder {
|
|
|
886
930
|
* A live talkback session: audio pushed from the host to a camera's speaker, the mirror of
|
|
887
931
|
* {@link LiveStreamHandle}'s inbound feed.
|
|
888
932
|
*
|
|
889
|
-
* Audio must be
|
|
890
|
-
*
|
|
891
|
-
*
|
|
933
|
+
* Audio must be in the codec the camera's speaker plays, {@link TalkbackHandle.codec}:
|
|
934
|
+
*
|
|
935
|
+
* - **`aac-lc`:** AAC-LC, 16 kHz, mono, in ADTS frames, through `write` / `writable()`. Chunk boundaries are
|
|
936
|
+
* irrelevant; frames are recovered from the stream. With an {@link AacEncoder}, `write` takes 16-bit
|
|
937
|
+
* little-endian mono PCM at 16 kHz instead.
|
|
938
|
+
* - **`aac-eld`:** one raw AAC-ELD access unit with LD-SBR (16 kHz, mono, 512 samples) per `write`, or per
|
|
939
|
+
* object-mode `writable()` write. No encoder applies.
|
|
892
940
|
*
|
|
893
|
-
*
|
|
894
|
-
*
|
|
895
|
-
* - **PCM** — only when the handle was opened with an {@link AacEncoder}, which then does the
|
|
896
|
-
* conversion. `write` takes 16-bit little-endian mono PCM at 16 kHz instead.
|
|
941
|
+
* An `aac-lc` stream at another rate or channel count is rejected rather than resampled. An `aac-eld` unit longer
|
|
942
|
+
* than the device accepts is refused with an `error`.
|
|
897
943
|
*
|
|
898
|
-
* Frames are **paced** at their own playback rate (64 ms
|
|
899
|
-
* arrive, so feeding a file plays it at speed instead of overrunning the device. A live source keeps
|
|
944
|
+
* Frames are **paced** at their own playback rate (64 ms for `aac-lc`, 32 ms for `aac-eld`) rather than flushed as
|
|
945
|
+
* fast as they arrive, so feeding a file plays it at speed instead of overrunning the device. A live source keeps
|
|
900
946
|
* the queue near-empty and is unaffected.
|
|
901
947
|
*
|
|
902
948
|
* @example
|
|
@@ -908,7 +954,16 @@ export interface AacEncoder {
|
|
|
908
954
|
* ```
|
|
909
955
|
*/
|
|
910
956
|
export interface TalkbackHandle {
|
|
911
|
-
/**
|
|
957
|
+
/**
|
|
958
|
+
* The codec this camera's speaker plays: `aac-eld` on a camera whose speaker plays it and whose stream on the same
|
|
959
|
+
* media session carries `aac-eld`, `aac-lc` otherwise, including when that stream carries no audio. It decides
|
|
960
|
+
* what {@link write} and {@link writable} take.
|
|
961
|
+
*/
|
|
962
|
+
readonly codec: TalkbackCodec;
|
|
963
|
+
/**
|
|
964
|
+
* Queue audio: ADTS frames or PCM for `aac-lc`, one access unit per call for `aac-eld`. Partial ADTS frames are
|
|
965
|
+
* held.
|
|
966
|
+
*/
|
|
912
967
|
write(chunk: Buffer): void;
|
|
913
968
|
/**
|
|
914
969
|
* A `node:stream` Writable over {@link write}, for piping a file or an encoder's stdout. Applies
|
|
@@ -931,7 +986,7 @@ export interface TalkbackHandle {
|
|
|
931
986
|
*
|
|
932
987
|
* This deliberately does NOT fire on a merely-empty queue. A realtime source keeps the queue near
|
|
933
988
|
* empty by design, so "queue is empty" arrives after the very first frame and stopping on it would
|
|
934
|
-
* cut the clip to
|
|
989
|
+
* cut the clip to one frame. {@link pending} is the instantaneous depth.
|
|
935
990
|
*/
|
|
936
991
|
on(event: "finished", listener: () => void): this;
|
|
937
992
|
/**
|
package/dist/core/store.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export interface PersistedSession {
|
|
|
16
16
|
authToken: string;
|
|
17
17
|
geoKey?: string;
|
|
18
18
|
region: RegionShard;
|
|
19
|
+
/** The `countryCode` the session was logged in with; a different configured one logs in again. */
|
|
20
|
+
countryCode?: string;
|
|
21
|
+
/** The host `estimate_domain` answered; it names shards the region classification cannot (`ie`). */
|
|
22
|
+
estimatedDomain?: string;
|
|
19
23
|
openudid: string;
|
|
20
24
|
/** This install's reported device model + media user-agent, generated once and reused. */
|
|
21
25
|
phoneModel?: string;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -55,6 +55,8 @@ export interface EufyDevice {
|
|
|
55
55
|
* every attached sensor of a T8010 — and serves only as a fallback for a device naming no parent.
|
|
56
56
|
*/
|
|
57
57
|
stationSn?: string;
|
|
58
|
+
/** The device's channel on its station, as its record states it (`device_channel`); absent where it states none. */
|
|
59
|
+
channel?: number;
|
|
58
60
|
/** Present (and non-empty) for P2P devices. */
|
|
59
61
|
p2pDid?: string;
|
|
60
62
|
/**
|