@mega-yfue/eufy-sdk 0.0.4 → 0.1.0-beta.0
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 +31 -12
- package/dist/client/device-registry.d.ts +343 -0
- package/dist/client/eufy-mega.d.ts +872 -0
- package/dist/client/index.d.ts +1 -6
- package/dist/client/map-channels.d.ts +22 -0
- package/dist/client/types.d.ts +383 -0
- package/dist/core/contracts.d.ts +898 -0
- package/dist/core/crypto.d.ts +98 -0
- package/dist/core/index.d.ts +9 -7
- package/dist/core/logger.d.ts +53 -0
- package/dist/core/lz4-block.d.ts +35 -0
- package/dist/core/raw-dp-hex.d.ts +32 -0
- package/dist/core/raw-dp-writer.d.ts +83 -0
- package/dist/core/store.d.ts +43 -0
- package/dist/core/types.d.ts +169 -0
- package/dist/core/util.d.ts +78 -0
- package/dist/index.d.ts +4 -7
- package/dist/index.js +25526 -14
- package/dist/index.js.map +7 -1
- package/dist/model/capabilities/access.d.ts +127 -0
- package/dist/model/capabilities/arming.d.ts +201 -0
- package/dist/model/capabilities/audio.d.ts +154 -0
- package/dist/model/capabilities/battery.d.ts +363 -0
- package/dist/model/capabilities/camera.d.ts +564 -0
- package/dist/model/capabilities/co.d.ts +40 -0
- package/dist/model/capabilities/contact.d.ts +133 -0
- package/dist/model/capabilities/doorbell.d.ts +345 -0
- package/dist/model/capabilities/dp-catalog.d.ts +38 -0
- package/dist/model/capabilities/index.d.ts +561 -0
- package/dist/model/capabilities/info.d.ts +28 -0
- package/dist/model/capabilities/keypad.d.ts +61 -0
- package/dist/model/capabilities/leak.d.ts +43 -0
- package/dist/model/capabilities/light.d.ts +174 -0
- package/dist/model/capabilities/locate.d.ts +63 -0
- package/dist/model/capabilities/lock.d.ts +242 -0
- package/dist/model/capabilities/manifest.d.ts +107 -0
- package/dist/model/capabilities/members.d.ts +647 -0
- package/dist/model/capabilities/motion.d.ts +377 -0
- package/dist/model/capabilities/person-detection.d.ts +8 -0
- package/dist/model/capabilities/ptz.d.ts +289 -0
- package/dist/model/capabilities/rtsp.d.ts +221 -0
- package/dist/model/capabilities/siren.d.ts +218 -0
- package/dist/model/capabilities/smart-light.d.ts +172 -0
- package/dist/model/capabilities/smoke.d.ts +40 -0
- package/dist/model/capabilities/snapshot.d.ts +6 -0
- package/dist/model/capabilities/storage.d.ts +11 -0
- package/dist/model/capabilities/suction.d.ts +104 -0
- package/dist/model/capabilities/types.d.ts +484 -0
- package/dist/model/capabilities/vacuum-clean.d.ts +1946 -0
- package/dist/model/capabilities/vacuum-dock.d.ts +208 -0
- package/dist/model/capabilities/video.d.ts +6 -0
- package/dist/model/classify.d.ts +78 -0
- package/dist/model/clean-record-detail.d.ts +65 -0
- package/dist/model/clean-records.d.ts +69 -0
- package/dist/model/device-family.d.ts +73 -0
- package/dist/model/device-types.d.ts +123 -0
- package/dist/model/device.d.ts +265 -0
- package/dist/model/index.d.ts +29 -4
- package/dist/model/infer.d.ts +23 -0
- package/dist/model/inspect.d.ts +61 -0
- package/dist/model/life-params.d.ts +21 -0
- package/dist/model/map-pixels.d.ts +70 -0
- package/dist/model/param-dictionary.d.ts +28 -0
- package/dist/model/param-namespace.d.ts +21 -0
- package/dist/model/proto-read.d.ts +53 -0
- package/dist/model/push-events.d.ts +147 -0
- package/dist/model/registry.d.ts +54 -0
- package/dist/model/types.d.ts +301 -0
- package/dist/model/vacuum-map-store.d.ts +92 -0
- package/dist/model/vacuum-map.d.ts +286 -0
- package/dist/model/vacuum-scenes.d.ts +76 -0
- package/dist/model/vacuum-schedules.d.ts +85 -0
- package/dist/transport/dp-preset.d.ts +102 -0
- package/dist/transport/ff09.d.ts +444 -0
- package/dist/transport/ffmpeg.d.ts +86 -0
- package/dist/transport/http/decodeImageV1.d.ts +20 -0
- package/dist/transport/http/decodeImageV2.d.ts +19 -0
- package/dist/transport/http/index.d.ts +5 -0
- package/dist/transport/http/light-catalog.d.ts +62 -0
- package/dist/transport/http/media-download.d.ts +14 -0
- package/dist/transport/http/mega-client.d.ts +514 -0
- package/dist/transport/http/phone-model.d.ts +21 -0
- package/dist/transport/index.d.ts +10 -7
- package/dist/transport/mqtt/app-client-id.d.ts +16 -0
- package/dist/transport/mqtt/availability.d.ts +14 -0
- package/dist/transport/mqtt/bare-ip-tls.d.ts +46 -0
- package/dist/transport/mqtt/biz-stream.d.ts +98 -0
- package/dist/transport/mqtt/broker-discovery.d.ts +55 -0
- package/dist/transport/mqtt/clean-codec.d.ts +14 -0
- package/dist/transport/mqtt/command-router.d.ts +285 -0
- package/dist/transport/mqtt/dp-codec.d.ts +58 -0
- package/dist/transport/mqtt/dp-color.d.ts +14 -0
- package/dist/transport/mqtt/engine.d.ts +16 -0
- package/dist/transport/mqtt/index.d.ts +5 -0
- package/dist/transport/mqtt/secure-mqtt.d.ts +107 -0
- package/dist/transport/mqtt/topics.d.ts +80 -0
- package/dist/transport/p2p/adts.d.ts +91 -0
- package/dist/transport/p2p/annexb.d.ts +124 -0
- package/dist/transport/p2p/codec.d.ts +166 -0
- package/dist/transport/p2p/command-router.d.ts +655 -0
- package/dist/transport/p2p/commands.d.ts +550 -0
- package/dist/transport/p2p/envelope.d.ts +46 -0
- package/dist/transport/p2p/fmp4.d.ts +89 -0
- package/dist/transport/p2p/fragment-recording.d.ts +33 -0
- package/dist/transport/p2p/index.d.ts +13 -0
- package/dist/transport/p2p/lan-ip.d.ts +25 -0
- package/dist/transport/p2p/live-stream.d.ts +223 -0
- package/dist/transport/p2p/live-trace.d.ts +123 -0
- package/dist/transport/p2p/media.d.ts +105 -0
- package/dist/transport/p2p/p2p-session.d.ts +620 -0
- package/dist/transport/p2p/readable-egress.d.ts +27 -0
- package/dist/transport/p2p/session-manager.d.ts +154 -0
- package/dist/transport/p2p/shared-live-source.d.ts +431 -0
- package/dist/transport/p2p/talkback.d.ts +187 -0
- package/dist/transport/p2p/video.d.ts +150 -0
- package/dist/transport/p2p/write-commands.d.ts +21 -0
- package/dist/transport/protobuf.d.ts +5 -0
- package/dist/transport/push/fcm.d.ts +23 -0
- package/dist/transport/push/index.d.ts +6 -0
- package/dist/transport/push/message-tags.d.ts +26 -0
- package/dist/transport/push/parser.d.ts +27 -0
- package/dist/transport/push/proto.d.ts +11 -0
- package/dist/transport/push/push-client.d.ts +64 -0
- package/dist/transport/push/store.d.ts +23 -0
- package/dist/transport/push/types.d.ts +180 -0
- package/dist/transport/raw-dp.d.ts +6 -0
- package/dist/transport/stored-image-cache.d.ts +23 -0
- package/dist/transport/tuya/account.d.ts +44 -0
- package/dist/transport/tuya/client.d.ts +89 -0
- package/dist/transport/tuya/command-router.d.ts +79 -0
- package/dist/transport/tuya/dp-codec.d.ts +56 -0
- package/dist/transport/tuya/index.d.ts +28 -0
- package/dist/transport/tuya/request.d.ts +157 -0
- package/dist/transport/tuya/sign.d.ts +64 -0
- package/package.json +12 -13
- package/dist/client/index.js +0 -2
- package/dist/client/index.js.map +0 -1
- package/dist/core/index.js +0 -2
- package/dist/core/index.js.map +0 -1
- package/dist/model/index.js +0 -2
- package/dist/model/index.js.map +0 -1
- package/dist/transport/index.js +0 -2
- package/dist/transport/index.js.map +0 -1
package/dist/client/index.d.ts
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BizChannel, BizMapFrame } from "../transport/mqtt/biz-stream.js";
|
|
2
|
+
import type { RawDpCodec } from "../core/contracts.js";
|
|
3
|
+
import type { VacuumMapPiece } from "../model/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Which channels are read, named as {@link BIZ_CHANNEL} names them — the key set of
|
|
6
|
+
* {@link READER_BY_CHANNEL}. The `satisfies` on that table constrains every key to a `BizChannel`.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DECODED_MAP_CHANNELS: readonly BizChannel[];
|
|
9
|
+
/**
|
|
10
|
+
* Decode one frame off the map stream into the map piece it carries, or `undefined`.
|
|
11
|
+
*
|
|
12
|
+
* `undefined` is the ordinary answer for a channel nothing reads yet, and for a frame that is not a
|
|
13
|
+
* whole message.
|
|
14
|
+
*
|
|
15
|
+
* **A frame with a non-zero `offset` is skipped without being decoded.** A large map is split across
|
|
16
|
+
* frames, and `offset` locates this one within its channel — so whatever it counts, a non-zero value
|
|
17
|
+
* means this is not the start of a message. Handing a fragment to a protobuf reader is how a decoder
|
|
18
|
+
* produces a confident wrong answer. The reader would in fact reject it anyway, because the frame's
|
|
19
|
+
* own length prefix disagrees with a partial body, but relying on that would make correctness an
|
|
20
|
+
* accident of the framing rather than a decision.
|
|
21
|
+
*/
|
|
22
|
+
export declare function decodeMapFrame(frame: BizMapFrame, codec: RawDpCodec): VacuumMapPiece | undefined;
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public type surface of the {@link EufyMega} facade — options + the typed event map.
|
|
3
|
+
*
|
|
4
|
+
* Kept separate from the class so the event contract reads on its own. The declaration-merged
|
|
5
|
+
* `interface EufyMega` (the typed on/once/off/emit overloads) stays in `eufy-mega.ts` next to the
|
|
6
|
+
* class — TS declaration merging requires both in the same module.
|
|
7
|
+
*/
|
|
8
|
+
import type { MegaClientConfig } from "../transport/http/mega-client.js";
|
|
9
|
+
import type { FcmStore } from "../transport/push/store.js";
|
|
10
|
+
import type { FfmpegLevel } from "../transport/ffmpeg.js";
|
|
11
|
+
import type { DeviceEventMap } from "../model/capabilities/index.js";
|
|
12
|
+
import type { Capability } from "../model/index.js";
|
|
13
|
+
import type { P2PFrame } from "../transport/p2p/p2p-session.js";
|
|
14
|
+
import type { PowerTier } from "../transport/p2p/session-manager.js";
|
|
15
|
+
import type { BizMapFrame } from "../transport/mqtt/biz-stream.js";
|
|
16
|
+
import type { VacuumMapSnapshot } from "../model/index.js";
|
|
17
|
+
import type { PushEvent, RawPushMessage } from "../transport/push/types.js";
|
|
18
|
+
import type { AvailabilityObservation, EufyDevice, RealtimeMessage } from "../core/types.js";
|
|
19
|
+
/** Count-only startup status for one auto-managed realtime transport plane. */
|
|
20
|
+
export interface RealtimePlaneReadiness {
|
|
21
|
+
/** Number of transport starts selected for the plane. */
|
|
22
|
+
readonly required: number;
|
|
23
|
+
/** Number of selected starts that completed successfully. */
|
|
24
|
+
readonly ready: number;
|
|
25
|
+
/** Number of selected starts that failed. */
|
|
26
|
+
readonly failed: number;
|
|
27
|
+
/** Number of selected starts that have not settled. */
|
|
28
|
+
readonly pending: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Count-only status of the current auto-managed realtime generation.
|
|
32
|
+
*
|
|
33
|
+
* The summary intentionally carries no credentials, identifiers, or underlying errors. Transport
|
|
34
|
+
* failures continue to surface through the `error` event.
|
|
35
|
+
*/
|
|
36
|
+
export interface RealtimeReadiness {
|
|
37
|
+
/** Outcome of the generation or of this caller's bounded wait. */
|
|
38
|
+
readonly state: "ready" | "partial" | "disabled" | "superseded" | "timed-out";
|
|
39
|
+
/** Account-wide FCM push startup status. */
|
|
40
|
+
readonly push: RealtimePlaneReadiness;
|
|
41
|
+
/** Secure-MQTT credential-scope startup status. */
|
|
42
|
+
readonly mqtt: RealtimePlaneReadiness;
|
|
43
|
+
/** Persistent station-control P2P startup status for wired stations. */
|
|
44
|
+
readonly wiredP2p: RealtimePlaneReadiness;
|
|
45
|
+
}
|
|
46
|
+
/** Options for {@link EufyMega.waitForRealtime}. */
|
|
47
|
+
export interface WaitForRealtimeOptions {
|
|
48
|
+
/**
|
|
49
|
+
* Maximum time in milliseconds for this caller to wait. Expiry does not cancel background startup;
|
|
50
|
+
* a later call can observe the generation's final result.
|
|
51
|
+
*/
|
|
52
|
+
timeoutMs?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface EufyMegaOptions extends MegaClientConfig {
|
|
55
|
+
/** Persist FCM push credentials + seen ids across runs (default: in-memory). */
|
|
56
|
+
pushStore?: FcmStore;
|
|
57
|
+
/** Eagerly retain validated push thumbnails in memory for `camera.snapshotStored()` (default `true`). */
|
|
58
|
+
storedSnapshotCache?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* LAN address overrides for direct P2P, keyed by **parent-station serial** → `host` or `host:port`.
|
|
61
|
+
* The SDK normally derives a station's LAN address from its device record; an entry here overrides it
|
|
62
|
+
* where the record's IP is wrong/blocked (AP isolation, a stale `ip_addr`).
|
|
63
|
+
*/
|
|
64
|
+
localAddresses?: Record<string, string>;
|
|
65
|
+
/**
|
|
66
|
+
* Suppress the `255.255.255.255` local-lookup broadcast (default `false` — broadcast is sent).
|
|
67
|
+
*
|
|
68
|
+
* An unconnected P2P session broadcasts a local lookup **once a second for the whole connect
|
|
69
|
+
* timeout**, which is how a station on the same LAN is found without knowing its address. That is
|
|
70
|
+
* cheap when it works and not free when it does not: the datagram goes to every host on the segment,
|
|
71
|
+
* every associated client of a WLAN has to receive it, and a host that sets `SO_BROADCAST` on many
|
|
72
|
+
* sockets in quick succession is doing something unusual to its own network stack.
|
|
73
|
+
*
|
|
74
|
+
* Turning it off costs the LAN-discovery path only. A station whose record carries a usable
|
|
75
|
+
* `ip_addr` is still found directly, and the PPCS cloud lookup — which is what actually connects a
|
|
76
|
+
* station in most topologies — is unaffected. Set it when the caller knows its stations' addresses,
|
|
77
|
+
* or when the broadcast is suspected of disturbing the host's own networking.
|
|
78
|
+
*/
|
|
79
|
+
noBroadcast?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Auto-manage connectivity (default `true`). When on, a successful {@link EufyMega.login} brings up
|
|
82
|
+
* the always-on event channels itself — FCM push + secure MQTT (if the account has appliances) — and
|
|
83
|
+
* eagerly warms P2P only for **wired** stations (HomeBases / mains cameras). Battery cameras stay
|
|
84
|
+
* detached until a command / stream — or a pre-warm the caller opted into via {@link
|
|
85
|
+
* EufyMegaOptions.prewarmEvents} — needs them, and idle-detach afterwards. The host calls no
|
|
86
|
+
* `connect*` — connectivity is transport-agnostic. Set `false` to manage nothing automatically
|
|
87
|
+
* (advanced/testing).
|
|
88
|
+
*/
|
|
89
|
+
autoRealtime?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Read-through cache freshness window in ms (default 15000). A `getProperty`/`getProperties` read of
|
|
92
|
+
* a value older than this schedules ONE coalesced background refresh and returns the last-known value
|
|
93
|
+
* immediately; realtime (push/P2P) updates keep values fresh so a live device rarely refetches.
|
|
94
|
+
*/
|
|
95
|
+
cacheTtlMs?: number;
|
|
96
|
+
/**
|
|
97
|
+
* How long {@link EufyMega.getDevice} waits (ms, default `4000`) for a device whose state exists ONLY
|
|
98
|
+
* on its realtime wire to make its first report, before resolving it.
|
|
99
|
+
*
|
|
100
|
+
* Such a device has no pollable cloud state, and the typed read getters are gated on what it has
|
|
101
|
+
* actually reported — so one resolved before its first report has no readable state, and no later
|
|
102
|
+
* report can add the getters to it. A short wait buys a populated read surface. `0` disables the wait
|
|
103
|
+
* and accepts that reads appear only on a `Device` fetched after the first report. Devices with a
|
|
104
|
+
* cloud record never wait.
|
|
105
|
+
*/
|
|
106
|
+
stateSnapshotMs?: number;
|
|
107
|
+
/**
|
|
108
|
+
* Idle window in ms before an on-demand P2P session to a **battery** station is closed so the device
|
|
109
|
+
* can sleep (default 300000 = 5 min). Wired stations stay persistent.
|
|
110
|
+
*/
|
|
111
|
+
p2pIdleMs?: number;
|
|
112
|
+
/**
|
|
113
|
+
* How long a speculative pre-warm holds the session it opened, in ms (default 28000). Applies only to
|
|
114
|
+
* the events {@link EufyMegaOptions.prewarmEvents} opts into; pre-warm is off until then.
|
|
115
|
+
*
|
|
116
|
+
* When the window expires with nothing attached, the session does not close — the hold is released and
|
|
117
|
+
* the station's own idle window takes over, which for a battery station is {@link
|
|
118
|
+
* EufyMegaOptions.p2pIdleMs} (5 min by default). Budget an unattended pre-warm at the sum of the two.
|
|
119
|
+
*/
|
|
120
|
+
prewarmMs?: number;
|
|
121
|
+
/**
|
|
122
|
+
* How often to re-read the cloud device list and emit a semantic event for each param that changed
|
|
123
|
+
* (default 600000 = 10 min). Set `0` to disable polling entirely.
|
|
124
|
+
*
|
|
125
|
+
* The default is paced to the data rather than to a host's refresh appetite — see
|
|
126
|
+
* the device's `params` for how slowly the cloud actually refreshes them. Polling faster costs
|
|
127
|
+
* requests without seeing anything sooner.
|
|
128
|
+
*
|
|
129
|
+
* This channel carries the slow-moving state that has no push of its own (a battery level; a sensor
|
|
130
|
+
* that only reports to the cloud). Fast state — motion, doorbell, contact, lock — arrives over
|
|
131
|
+
* push/P2P/MQTT and is unaffected by this setting.
|
|
132
|
+
*/
|
|
133
|
+
pollMs?: number;
|
|
134
|
+
/**
|
|
135
|
+
* Which semantic events speculatively pre-warm a camera's P2P session — **opt-in, default `[]`**, an
|
|
136
|
+
* empty list being what disables it. Naming an event buys a stream or talkback opened right after it
|
|
137
|
+
* starting warm rather than paying a cold open, and costs what the three paragraphs below describe.
|
|
138
|
+
*
|
|
139
|
+
* Any name in {@link DeviceEventMap} is accepted, so the list autocompletes and a typo won't compile.
|
|
140
|
+
* A pre-warm rides the push channel, so only an event push carries can trigger one — a poll-carried
|
|
141
|
+
* event is inert however it is listed, and each capability module declares which source carries its own
|
|
142
|
+
* events. An event from a device that is not a camera pre-warms the station behind it, which for an
|
|
143
|
+
* attached sensor is its HomeBase.
|
|
144
|
+
*
|
|
145
|
+
* **One camera pays for it.** Wired stations are warmed at login and never idle-detach, and an attached
|
|
146
|
+
* camera's session lives on its wired base — so the only station a pre-warm genuinely opens is a
|
|
147
|
+
* standalone battery camera, the device class the on-demand session lifecycle exists to let sleep.
|
|
148
|
+
* {@link EufyMegaOptions.prewarmTiers} is how that class is spared while keeping the opt-in.
|
|
149
|
+
*
|
|
150
|
+
* **An unwatched pre-warm costs more than its window**, per {@link EufyMegaOptions.prewarmMs}: the hold
|
|
151
|
+
* expiring arms the station's idle window instead of closing the session, and a second qualifying event
|
|
152
|
+
* inside that tail restarts it.
|
|
153
|
+
*
|
|
154
|
+
* **Frequency is a property of the installation, not of the event name.** A camera set to report human
|
|
155
|
+
* detection only fires `personDetected` as often as a busier one fires raw `motion`, so the rate is the
|
|
156
|
+
* fleet's and not the event's.
|
|
157
|
+
*/
|
|
158
|
+
prewarmEvents?: (keyof DeviceEventMap)[];
|
|
159
|
+
/**
|
|
160
|
+
* Which station power tiers {@link EufyMegaOptions.prewarmEvents} may pre-warm (default: both). The
|
|
161
|
+
* tier is the one of the **station whose session would open** — a camera attached to a HomeBase is
|
|
162
|
+
* pre-warmed as `wired`, because that base's session is the one being held.
|
|
163
|
+
*
|
|
164
|
+
* `["wired"]` keeps the opt-in and spends no battery: it is close to a no-op, since wired stations are
|
|
165
|
+
* already warmed at login and never idle-detach, so it only bites after a session drops.
|
|
166
|
+
*/
|
|
167
|
+
prewarmTiers?: PowerTier[];
|
|
168
|
+
/**
|
|
169
|
+
* ffmpeg's own `-loglevel` for the media paths that shell out to it (live snapshot / record).
|
|
170
|
+
* Default `"error"` (quiet). A raised level (e.g. `"trace"`) reports a failing decode/mux;
|
|
171
|
+
* ffmpeg's stderr is then forwarded to the {@link EufyMegaOptions.logger} as `[ffmpeg]` debug lines
|
|
172
|
+
* — visible only where that logger shows `debug`. Independent of the SDK's own log level.
|
|
173
|
+
*/
|
|
174
|
+
ffmpegLogLevel?: FfmpegLevel;
|
|
175
|
+
/**
|
|
176
|
+
* Opt into unverified Tuya DP writes for `eufy_home_tuya` clean-line devices (G-series / X8).
|
|
177
|
+
*
|
|
178
|
+
* By default `TuyaCommandRouter` refuses to send `dp.publish` because the request shape
|
|
179
|
+
* has been reversed but not yet confirmed from a live on-device capture — a wrong shape comes back
|
|
180
|
+
* as a generic Tuya error indistinguishable from an actual device rejection. `true` sends it anyway,
|
|
181
|
+
* which is sound only where the full round-trip has been confirmed on a real device, or that
|
|
182
|
+
* ambiguity is accepted.
|
|
183
|
+
*/
|
|
184
|
+
tuyaAllowUnverified?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* The `ffmpeg` executable the media paths that shell out should run (live snapshot / record).
|
|
187
|
+
* Default: the bare name `"ffmpeg"`, looked up on `PATH`.
|
|
188
|
+
*
|
|
189
|
+
* Set it when the host ships or manages its own build — an absolute path is resolved without any
|
|
190
|
+
* `PATH` lookup, so those paths work on a host that has no system ffmpeg at all. The SDK never
|
|
191
|
+
* edits `process.env.PATH`; naming the binary here is the supported way to point it at one. The
|
|
192
|
+
* path is not probed, so a wrong one surfaces as the media call's own "not runnable" rejection.
|
|
193
|
+
*/
|
|
194
|
+
ffmpegPath?: string;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* What the SDK can honestly say about a device's liveness at one instant — the facts, never a verdict.
|
|
198
|
+
*
|
|
199
|
+
* There is deliberately **no `online: boolean`**. "Unreachable" is a threshold decision, and the right
|
|
200
|
+
* threshold differs per device: a mains camera reports constantly, while a battery contact sensor can
|
|
201
|
+
* be silent for days by design and is perfectly healthy. Baking one timeout into the SDK would force
|
|
202
|
+
* that choice on every host. The SDK reports when the device last spoke; the caller decides what that
|
|
203
|
+
* means — the same split as the snapshot cache TTL and the live power budget.
|
|
204
|
+
*
|
|
205
|
+
* P2P session state is **not** a liveness signal and is not carried here: sessions are opened only
|
|
206
|
+
* when something needs one and closed when idle, so "no session" is the resting state of a healthy
|
|
207
|
+
* device. Transport visibility lives on `getP2pSessions()` and the `p2pConnect`/`p2pClose` events,
|
|
208
|
+
* station-scoped like the session itself.
|
|
209
|
+
*
|
|
210
|
+
* The cloud record carries no connectivity field either: it has no `status` / `device_online`, and the
|
|
211
|
+
* connection-related fields it does carry are opaque routing strings, not booleans.
|
|
212
|
+
*/
|
|
213
|
+
export interface DeviceState {
|
|
214
|
+
sn: string;
|
|
215
|
+
/** The parent station whose P2P session covers this device (itself, when standalone). */
|
|
216
|
+
stationSn: string;
|
|
217
|
+
/**
|
|
218
|
+
* When the device last reported to the cloud, in ms. Bounded by
|
|
219
|
+
* the cloud's own slow refresh — minutes, not seconds — so it answers "is this device alive at all",
|
|
220
|
+
* not "what is it doing right now".
|
|
221
|
+
*/
|
|
222
|
+
lastSeenMs?: number;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* A single semantic event tagged with its name — the payload of the catch-all `"event"` listener.
|
|
226
|
+
* A discriminated union over {@link DeviceEventMap}, so switching on `e.eventName` narrows `e` to that
|
|
227
|
+
* event's payload.
|
|
228
|
+
*
|
|
229
|
+
* The tag is `eventName`, not `name`: an event payload may legitimately carry its own `name` field, and
|
|
230
|
+
* overwriting it to tag the event would destroy data. Matches the `eventName` carried on a push event.
|
|
231
|
+
*/
|
|
232
|
+
export type AnyDeviceEvent = {
|
|
233
|
+
[K in keyof DeviceEventMap]: DeviceEventMap[K] & {
|
|
234
|
+
eventName: K;
|
|
235
|
+
};
|
|
236
|
+
}[keyof DeviceEventMap];
|
|
237
|
+
/**
|
|
238
|
+
* The complete typed event surface of {@link EufyMega} — event name → listener-argument tuple.
|
|
239
|
+
*
|
|
240
|
+
* Two groups:
|
|
241
|
+
* - **Semantic events** (motion, doorbellPress, lockState, ptzNotify, …) — projected from the
|
|
242
|
+
* capability modules via {@link DeviceEventMap}, so adding a capability event adds a typed event
|
|
243
|
+
* here automatically (one line in that map).
|
|
244
|
+
* - **Low-level / lifecycle events** — the raw escape hatches and transport lifecycle.
|
|
245
|
+
*/
|
|
246
|
+
export type EufyMegaEventMap = {
|
|
247
|
+
[K in keyof DeviceEventMap]: [DeviceEventMap[K]];
|
|
248
|
+
} & {
|
|
249
|
+
/** Catch-all: fires for EVERY semantic event, payload tagged with its `eventName`. */
|
|
250
|
+
event: [AnyDeviceEvent];
|
|
251
|
+
/**
|
|
252
|
+
* A device appeared on the account since the previous poll — a pairing, or a device that became
|
|
253
|
+
* visible again. Account topology, so it lives here rather than on the per-device capability map.
|
|
254
|
+
*
|
|
255
|
+
* Fires only for a device the SDK has seen the account WITHOUT; the first enumeration after login is
|
|
256
|
+
* not a stream of additions. Suppressed when the baseline it would be measured against only partly
|
|
257
|
+
* resolved, so a recovering outage doesn't read as a burst of pairings.
|
|
258
|
+
*/
|
|
259
|
+
deviceAdded: [device: EufyDevice];
|
|
260
|
+
/**
|
|
261
|
+
* A device is gone from the account — unpaired, or moved away.
|
|
262
|
+
*
|
|
263
|
+
* Deliberately conservative: suppressed when a poll only partially resolved (a failed house query
|
|
264
|
+
* returns a subset), because an absence caused by an outage is not a removal.
|
|
265
|
+
*/
|
|
266
|
+
deviceRemoved: [device: EufyDevice];
|
|
267
|
+
/**
|
|
268
|
+
* A device a caller is holding gained capabilities, because it reported evidence it hadn't before.
|
|
269
|
+
* `gained` is what is newly available; `capabilities` is the full set after widening.
|
|
270
|
+
*
|
|
271
|
+
* A `Device` resolves its capabilities from the evidence available at the time, so one resolved
|
|
272
|
+
* before the device had reported a param lacks the capability that param proves. When a later poll
|
|
273
|
+
* supplies it, the object is re-resolved and re-bound in place — the new accessor is live on the
|
|
274
|
+
* instance the host already has. Capabilities are never retracted, so this only ever widens.
|
|
275
|
+
*/
|
|
276
|
+
deviceCapabilities: [info: {
|
|
277
|
+
deviceSn: string;
|
|
278
|
+
gained: Capability[];
|
|
279
|
+
capabilities: Capability[];
|
|
280
|
+
}];
|
|
281
|
+
connect: [];
|
|
282
|
+
disconnect: [reason?: unknown];
|
|
283
|
+
message: [msg: RealtimeMessage];
|
|
284
|
+
/**
|
|
285
|
+
* One frame off a clean-line device's map stream — the `biz/…/res` leg, which carries pixel planes,
|
|
286
|
+
* room outlines and names, virtual walls and the live pose.
|
|
287
|
+
*
|
|
288
|
+
* The frame is unwrapped as far as its bytes and no further: `frame.payload` is a Raw-DP frame in
|
|
289
|
+
* the base64 a codec reads, and `frame.channelId` says which `stream.proto` message it holds. That
|
|
290
|
+
* split is deliberate while the decoders are being built — the meaning of a channel is settled in one
|
|
291
|
+
* place rather than in this event's shape.
|
|
292
|
+
*/
|
|
293
|
+
mapFrame: [info: {
|
|
294
|
+
deviceSn: string;
|
|
295
|
+
frame: BizMapFrame;
|
|
296
|
+
}];
|
|
297
|
+
/**
|
|
298
|
+
* A device's map changed — a new cell plane, a renamed room, a zone the user drew.
|
|
299
|
+
*
|
|
300
|
+
* Carries the whole snapshot rather than the piece that changed, because the pieces are only useful
|
|
301
|
+
* together: a room outline without the room list names nothing. Emitted only when something actually
|
|
302
|
+
* changed; the robot republishes its map throughout a clean and a repeat of what is already held is
|
|
303
|
+
* dropped rather than woken on.
|
|
304
|
+
*/
|
|
305
|
+
map: [info: {
|
|
306
|
+
deviceSn: string;
|
|
307
|
+
map: VacuumMapSnapshot;
|
|
308
|
+
}];
|
|
309
|
+
/**
|
|
310
|
+
* A device reported to the cloud since the last poll — its {@link DeviceState.lastSeenMs} advanced.
|
|
311
|
+
* Carries {@link DeviceState}; the host applies its own staleness threshold.
|
|
312
|
+
*
|
|
313
|
+
* Transport/session lifecycle is NOT this event: that's `p2pConnect`/`p2pClose`, station-scoped where
|
|
314
|
+
* a session actually lives.
|
|
315
|
+
*/
|
|
316
|
+
deviceState: [state: DeviceState];
|
|
317
|
+
/**
|
|
318
|
+
* A verified vendor-wire availability observation. Duplicate states are coalesced; silence,
|
|
319
|
+
* `lastSeenMs`, operation failure and transport lifecycle never emit or clear this event.
|
|
320
|
+
*/
|
|
321
|
+
availability: [observation: AvailabilityObservation];
|
|
322
|
+
p2pConnect: [stationSn: string];
|
|
323
|
+
p2pClose: [stationSn: string];
|
|
324
|
+
p2pLevel2Ready: [info: {
|
|
325
|
+
stationSn: string;
|
|
326
|
+
cipherId: number;
|
|
327
|
+
}];
|
|
328
|
+
p2p: [frame: P2PFrame];
|
|
329
|
+
pushConnect: [];
|
|
330
|
+
pushDisconnect: [];
|
|
331
|
+
pushRaw: [raw: RawPushMessage];
|
|
332
|
+
push: [event: PushEvent];
|
|
333
|
+
/**
|
|
334
|
+
* A transport-level command got an acknowledgement (or didn't) — emitted by the MQTT command router.
|
|
335
|
+
* For `ff09-actuate` the reply is a "device received it"
|
|
336
|
+
* signal, not a physical-actuation-complete one (see that handler's doc); for `ff09-autolock` the GET
|
|
337
|
+
* step already threw on no reply by the time this fires — `getAcked` is always `true` here, `acked`
|
|
338
|
+
* reports the SET step's fire-and-forget ack. `dispatch()`/`lock()`/`unlock()`/`setAutoLock()` stay
|
|
339
|
+
* `Promise<void>` and never throw on a missing SET ack (fire-and-forget, same as every other write) —
|
|
340
|
+
* this event is the optional channel for delivery visibility, without the dispatch
|
|
341
|
+
* contract itself changing shape. Secure-MQTT DP writes use the persistent account connection and
|
|
342
|
+
* report broker publication (`acked: true`) without an `instanceIp`; that is not device convergence.
|
|
343
|
+
*/
|
|
344
|
+
commandAck: [info: {
|
|
345
|
+
sn: string;
|
|
346
|
+
kind: string;
|
|
347
|
+
acked: boolean;
|
|
348
|
+
instanceIp?: string;
|
|
349
|
+
getAcked?: boolean;
|
|
350
|
+
}];
|
|
351
|
+
/**
|
|
352
|
+
* A write was acknowledged and its declared observation then never converged, so the device never
|
|
353
|
+
* reported the state the write asked for.
|
|
354
|
+
*
|
|
355
|
+
* This is the answer to the question `dispatch` deliberately does not wait for. A command resolves once the
|
|
356
|
+
* transport has carried it, and the observation a member declares decides separately whether the device
|
|
357
|
+
* applied it; where that observation times out, the wire accepted the write and the device ignored it — seen
|
|
358
|
+
* on a battery camera whose power write is acknowledged and never acted on. It is reported here rather than
|
|
359
|
+
* on `error` because it is an outcome and not a fault, for the same reason `commandAck` has its own
|
|
360
|
+
* channel. `observed` is what the param read when the deadline passed, absent where the
|
|
361
|
+
* device reported none at all.
|
|
362
|
+
*/
|
|
363
|
+
commandUnconfirmed: [
|
|
364
|
+
info: {
|
|
365
|
+
sn: string;
|
|
366
|
+
property: string;
|
|
367
|
+
param: number;
|
|
368
|
+
expected?: boolean | number | string;
|
|
369
|
+
observed?: boolean | number | string;
|
|
370
|
+
timeoutMs: number;
|
|
371
|
+
}
|
|
372
|
+
];
|
|
373
|
+
/**
|
|
374
|
+
* The cloud session was kicked or invalidated — another client logged into the same account, or the
|
|
375
|
+
* token expired. The SDK has already cleared the persisted session, so recovery is a fresh `login()`
|
|
376
|
+
* (which usually needs 2FA). Distinct from `error`: a session error is emitted ONLY here, not also
|
|
377
|
+
* on `error`.
|
|
378
|
+
*/
|
|
379
|
+
sessionExpired: [err: Error];
|
|
380
|
+
error: [err: Error];
|
|
381
|
+
};
|
|
382
|
+
/** Event names {@link EufyMega} can emit. */
|
|
383
|
+
export type EufyMegaEvent = keyof EufyMegaEventMap;
|