@mega-yfue/eufy-sdk 0.2.0-beta.20 → 0.2.0-beta.22
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/types.d.ts +6 -2
- package/dist/index.js +171 -24
- package/dist/index.js.map +2 -2
- package/dist/model/capabilities/index.d.ts +1 -1
- package/dist/model/capabilities/types.d.ts +48 -7
- package/dist/model/capabilities/vacuum-clean.d.ts +59 -0
- package/dist/transport/http/mega-client.d.ts +50 -3
- package/package.json +1 -1
|
@@ -481,7 +481,7 @@ export declare function buildActions(caps: readonly Capability[], deps: MemberDe
|
|
|
481
481
|
* @internal
|
|
482
482
|
*/
|
|
483
483
|
export declare function describeCapabilities(bound: Partial<DeviceActionMap>, ctx?: AvailabilityContext): CapabilityDescriptor[];
|
|
484
|
-
export type { CommandContext, CapabilityActions, CapabilityStateReader, CapabilityModule, DetectionSpec, CapabilityFrame, CapabilityEvent, InboundSignal, EventMapping, ProductLine, ActionArgSpec, DecodedState, } from "./types.js";
|
|
484
|
+
export type { CommandContext, CapabilityActions, CapabilityStateReader, CapabilityModule, DetectionSpec, CapabilityFrame, CapabilityEvent, InboundSignal, EventClaim, EventMapping, ProductLine, ActionArgSpec, DecodedState, } from "./types.js";
|
|
485
485
|
/**
|
|
486
486
|
* The value-kind vocabulary a read is annotated with, re-exported from the barrel that publishes the
|
|
487
487
|
* read itself, so the union a member's `kind` is drawn from is reachable from the same import.
|
|
@@ -136,6 +136,45 @@ export interface EventMapping {
|
|
|
136
136
|
* capture. Return `{}` when this signal doesn't carry the field, so nothing is invented.
|
|
137
137
|
*/
|
|
138
138
|
derive?(signal: InboundSignal): Record<string, unknown>;
|
|
139
|
+
/**
|
|
140
|
+
* Evidence that this device deals in the classification the id names, for an id whose presence in
|
|
141
|
+
* the wire vocabulary does not prove it.
|
|
142
|
+
*
|
|
143
|
+
* The AI-detection ids are shared verbatim across the camera families, so the id space says what an
|
|
144
|
+
* integer MEANS and never which units classify that way. A claim is how a mapping states the
|
|
145
|
+
* evidence that separates them, and it narrows the DESCRIPTION only: {@link EventMapping} stays in
|
|
146
|
+
* the dispatch index unclaimed, so a device that sends the push still gets the event. Under-reporting
|
|
147
|
+
* what a device is expected to emit is recoverable; dropping an event it did emit is not.
|
|
148
|
+
*
|
|
149
|
+
* Every field must hold — the AND to {@link DetectionSpec}'s OR, because a claim rules a family OUT
|
|
150
|
+
* rather than finding one more reason to say yes. A fact the context does not carry rules nothing
|
|
151
|
+
* out: only evidence that positively contradicts the claim withdraws the event.
|
|
152
|
+
*/
|
|
153
|
+
claim?: EventClaim;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Evidence separating the families that share one inbound id.
|
|
157
|
+
*
|
|
158
|
+
* Both fields are optional and independent; an empty claim asserts nothing and is the same as none.
|
|
159
|
+
*/
|
|
160
|
+
export interface EventClaim {
|
|
161
|
+
/**
|
|
162
|
+
* The codecs whose devices issue the id. For an id drawn from a vocabulary one device family owns:
|
|
163
|
+
* the AI-detection ids belong to the camera families, and a standalone sensor announces its own
|
|
164
|
+
* motion under a different id entirely.
|
|
165
|
+
*/
|
|
166
|
+
codecs?: readonly Codec[];
|
|
167
|
+
/**
|
|
168
|
+
* Member names whose INSTALLED getter is the evidence — the device reported the parameter behind
|
|
169
|
+
* the classification, which is the same bar every typed read is held to.
|
|
170
|
+
*/
|
|
171
|
+
reads?: readonly string[];
|
|
172
|
+
/**
|
|
173
|
+
* The topology the id belongs to: `true` for an id only a station's attached device sends, `false`
|
|
174
|
+
* for one only a standalone unit sends. Compared against {@link AvailabilityContext.homeBaseAttached},
|
|
175
|
+
* and ignored where that is absent.
|
|
176
|
+
*/
|
|
177
|
+
homeBaseAttached?: boolean;
|
|
139
178
|
}
|
|
140
179
|
/**
|
|
141
180
|
* A decoded inbound event a capability wants surfaced on the SDK. `event` is the EufyMega event
|
|
@@ -197,6 +236,15 @@ export interface AvailabilityContext {
|
|
|
197
236
|
* `undefined` as an empty set — `ctx.paramIds?.has(dp) ?? false`.
|
|
198
237
|
*/
|
|
199
238
|
paramIds?: ReadonlySet<number>;
|
|
239
|
+
/**
|
|
240
|
+
* Whether the device hangs off a HomeBase (a `parent_sn` other than its own) rather than standing
|
|
241
|
+
* alone. A DEVICE fact, not a transport one — the same class of routing evidence as {@link hasP2p} —
|
|
242
|
+
* which is why it sits here rather than on {@link CommandContext}: it is as true of a described
|
|
243
|
+
* device as of a commanded one. The `rtsp` capability gates on it because a station serves an
|
|
244
|
+
* attached camera's stream itself and ignores that camera's authentication setting, so the write
|
|
245
|
+
* cannot do what its name promises there.
|
|
246
|
+
*/
|
|
247
|
+
homeBaseAttached?: boolean;
|
|
200
248
|
}
|
|
201
249
|
export interface CommandContext extends AvailabilityContext {
|
|
202
250
|
/** Device channel (0 for standalone, `device_channel` on a HomeBase). */
|
|
@@ -259,13 +307,6 @@ export interface CommandContext extends AvailabilityContext {
|
|
|
259
307
|
* capability uses this to route lock/unlock to P2P vs. reject with a clear MQTT-not-wired error.
|
|
260
308
|
*/
|
|
261
309
|
hasP2p?: boolean;
|
|
262
|
-
/**
|
|
263
|
-
* Whether the device hangs off a HomeBase (a `parent_sn` other than its own) rather than standing
|
|
264
|
-
* alone. A DEVICE fact, not a transport one — the same class of routing evidence as {@link hasP2p}.
|
|
265
|
-
* The `rtsp` capability gates on it because a station serves an attached camera's stream itself and
|
|
266
|
-
* ignores that camera's authentication setting, so the write cannot do what its name promises there.
|
|
267
|
-
*/
|
|
268
|
-
homeBaseAttached?: boolean;
|
|
269
310
|
/**
|
|
270
311
|
* Parsed `get_product_data_point` catalog for this device's SKU — present for vacuum/mower devices,
|
|
271
312
|
* absent for all other codecs. Capabilities use it for per-model feature-availability and value-range
|
|
@@ -379,6 +379,23 @@ export type CarpetStrategy = (typeof CARPET_STRATEGIES)[number];
|
|
|
379
379
|
*/
|
|
380
380
|
export declare const CLEAN_EXTENTS: readonly ["normal", "narrow", "quick"];
|
|
381
381
|
export type CleanExtent = (typeof CLEAN_EXTENTS)[number];
|
|
382
|
+
/**
|
|
383
|
+
* Build a `CleanParamRequest` (DP 154) stating the three cleaning settings a run uses.
|
|
384
|
+
*
|
|
385
|
+
* The message is `{ clean_param: CleanParam }` — field 1 of the request, carrying the same `CleanParam`
|
|
386
|
+
* this module decodes out of field 1 of the reports it receives, so the shape a write sends is the
|
|
387
|
+
* shape a read has already been proven against on live hardware.
|
|
388
|
+
*
|
|
389
|
+
* All three settings are stated together because one message carries all three: a write naming fewer
|
|
390
|
+
* would be a `CleanParam` with the rest silent, and what a robot does with a half-stated one is not
|
|
391
|
+
* something this SDK has observed. `clean_times`(7) is never written — the vendor's own field comment
|
|
392
|
+
* makes zero mean "not stated", so omitting it leaves the robot's configured pass count alone — and
|
|
393
|
+
* neither is `fan`(6), which belongs to the suction capability's own data point.
|
|
394
|
+
*
|
|
395
|
+
* {@link VACUUM_CLEAN_MEMBERS.setCleanParam} dispatches this.
|
|
396
|
+
* @internal
|
|
397
|
+
*/
|
|
398
|
+
export declare function encodeCleanParam(cleanType: VacuumCleanType, cleanExtent: CleanExtent, mopLevel: MopLevel): string;
|
|
382
399
|
/**
|
|
383
400
|
* Read one setting out of the CONFIGURED `CleanParam` (DP 154), by its field number.
|
|
384
401
|
*
|
|
@@ -1963,6 +1980,48 @@ export declare const VACUUM_CLEAN_MEMBERS: {
|
|
|
1963
1980
|
readonly startScene: import("./members.js").MethodMember<(sceneId: number) => Promise<void>> & {
|
|
1964
1981
|
available: (ctx: import("./types.js").CommandContext) => boolean;
|
|
1965
1982
|
};
|
|
1983
|
+
/**
|
|
1984
|
+
* State the cleaning settings a run uses — `CleanParamRequest.clean_param` over DP 154.
|
|
1985
|
+
*
|
|
1986
|
+
* The write counterpart of {@link VACUUM_CLEAN_MEMBERS.cleanType},
|
|
1987
|
+
* {@link VACUUM_CLEAN_MEMBERS.cleanExtent} and {@link VACUUM_CLEAN_MEMBERS.mopLevel}: one message
|
|
1988
|
+
* carries all three, so they are set together rather than through three setters that would each send
|
|
1989
|
+
* the same message with the other two silent.
|
|
1990
|
+
*
|
|
1991
|
+
* **The evidence, and its limit.** The frame is field 1 of `CleanParamRequest`, which carries the
|
|
1992
|
+
* very `CleanParam` this module decodes out of field 1 of the reports a live T2351 sends — the
|
|
1993
|
+
* field numbers, the single-field wrappers and the `mop_mode.level` scale are all read off that
|
|
1994
|
+
* capture, and `encodeCleanParam` writes what `decodeCleanParamValue` reads. What is NOT captured is
|
|
1995
|
+
* the write direction itself. It ships as a method rather than an unverified write because the
|
|
1996
|
+
* hazard that rule answers does not arise here: DP 154 is the robot's own settings report, so a frame
|
|
1997
|
+
* it does not accept leaves those three reads unchanged, where a wrong fire-and-forget command would
|
|
1998
|
+
* look exactly like success.
|
|
1999
|
+
*
|
|
2000
|
+
* Suction is not here. It has its own data point and its own capability — a `fan` field exists in
|
|
2001
|
+
* this message and is deliberately not written, for the same reason it is not read.
|
|
2002
|
+
*/
|
|
2003
|
+
readonly setCleanParam: {
|
|
2004
|
+
readonly method: (deps: import("./members.js").MemberDeps) => (cleanType: VacuumCleanType, cleanExtent: CleanExtent, mopLevel: MopLevel) => Promise<void>;
|
|
2005
|
+
readonly description: string;
|
|
2006
|
+
readonly available: ((ctx: import("./types.js").CommandContext) => boolean) & ((ctx: import("./types.js").CommandContext) => boolean);
|
|
2007
|
+
readonly answers?: true;
|
|
2008
|
+
readonly args: readonly [{
|
|
2009
|
+
readonly name: "cleanType";
|
|
2010
|
+
readonly kind: "enum";
|
|
2011
|
+
readonly values: readonly ["sweep", "mop", "sweepAndMop", "sweepThenMop"];
|
|
2012
|
+
readonly description: "What the robot does with a surface.";
|
|
2013
|
+
}, {
|
|
2014
|
+
readonly name: "cleanExtent";
|
|
2015
|
+
readonly kind: "enum";
|
|
2016
|
+
readonly values: readonly ["normal", "narrow", "quick"];
|
|
2017
|
+
readonly description: "How far past the mapped edge a job reaches. Wire order, not app order.";
|
|
2018
|
+
}, {
|
|
2019
|
+
readonly name: "mopLevel";
|
|
2020
|
+
readonly kind: "enum";
|
|
2021
|
+
readonly values: readonly ["low", "middle", "high"];
|
|
2022
|
+
readonly description: "How much water the mop lays down. Only meaningful for a clean type that mops.";
|
|
2023
|
+
}];
|
|
2024
|
+
};
|
|
1966
2025
|
/**
|
|
1967
2026
|
* Clean the named rooms of a named map (ModeCtrlRequest method 1 over DP 152).
|
|
1968
2027
|
*
|
|
@@ -71,9 +71,34 @@ export declare class MegaApiError extends Error {
|
|
|
71
71
|
* device-list params carry the same `{param_type, param_value, update_time}` and are not owner-gated.
|
|
72
72
|
*/
|
|
73
73
|
export declare const OWNER_ONLY_CODE = 20004;
|
|
74
|
-
/**
|
|
74
|
+
/**
|
|
75
|
+
* Thrown when a persisted/expired session is rejected (401). Re-login to recover.
|
|
76
|
+
*
|
|
77
|
+
* It carries the rate the client has already worked out for replacing a rejected token, because the
|
|
78
|
+
* rejection is where that rate stops being the client's alone: a login driven from here spends the same
|
|
79
|
+
* session the client's own recovery would have, and repeated logins are what makes an account start
|
|
80
|
+
* demanding captchas. {@link retryAfterMs} is how long the next replacement is barred for, and
|
|
81
|
+
* {@link contended} whether this rejection landed inside that bar — the shape repeated displacement has.
|
|
82
|
+
*/
|
|
75
83
|
export declare class SessionExpiredError extends Error {
|
|
76
|
-
|
|
84
|
+
/**
|
|
85
|
+
* How long the next session replacement is barred for, in milliseconds; `0` when nothing bars one now.
|
|
86
|
+
*
|
|
87
|
+
* The remainder of the client's own hold-off, which doubles per consecutive replacement and is capped —
|
|
88
|
+
* and which every replacement extends, whether the client spent it or a login made on this error did.
|
|
89
|
+
*/
|
|
90
|
+
readonly retryAfterMs: number;
|
|
91
|
+
/**
|
|
92
|
+
* Whether this rejection landed inside that bar — a token replaced recently and rejected again since.
|
|
93
|
+
*
|
|
94
|
+
* It says the session is being DISPLACED rather than expiring: something else is signing in on this
|
|
95
|
+
* account, and replacing the token again only trades one login for another.
|
|
96
|
+
*/
|
|
97
|
+
readonly contended: boolean;
|
|
98
|
+
constructor(message: string, opts?: {
|
|
99
|
+
retryAfterMs?: number;
|
|
100
|
+
contended?: boolean;
|
|
101
|
+
});
|
|
77
102
|
}
|
|
78
103
|
/**
|
|
79
104
|
* eufy cloud gateway error codes — the numeric `code` carried in a response envelope alongside the
|
|
@@ -223,9 +248,11 @@ export declare class MegaHttpClient {
|
|
|
223
248
|
private loggingIn;
|
|
224
249
|
/** The one in-flight re-login every call rejected on the same dead token waits on. */
|
|
225
250
|
private reauthAttempt?;
|
|
226
|
-
/** Replacements since the held session last proved stable, and when the last one ran — see {@link
|
|
251
|
+
/** Replacements since the held session last proved stable, and when the last one ran — see {@link holdOffRemainingMs}. */
|
|
227
252
|
private recoveries;
|
|
228
253
|
private lastRecoveryAt;
|
|
254
|
+
/** A token of ours has been rejected and not yet replaced — see {@link noteTokenReplacement}. */
|
|
255
|
+
private rejectedTokenPending;
|
|
229
256
|
constructor(cfg: MegaClientConfig);
|
|
230
257
|
/**
|
|
231
258
|
* Install the session the store holds, if it holds a usable one: the token + its bound ECDH key, skipping
|
|
@@ -500,6 +527,26 @@ export declare class MegaHttpClient {
|
|
|
500
527
|
* and a caller is told the honest reason instead of being served a fight.
|
|
501
528
|
*/
|
|
502
529
|
private recoveryDue;
|
|
530
|
+
/**
|
|
531
|
+
* How much longer a token replacement must wait, in milliseconds; `0` when one may run now.
|
|
532
|
+
*
|
|
533
|
+
* The wait doubles per consecutive replacement and is capped, and it is what {@link recoveryDue} gates
|
|
534
|
+
* this client's own recovery on — and what {@link SessionExpiredError.retryAfterMs} hands a host that
|
|
535
|
+
* drives its own. One function so the two cannot disagree about the rate, which they would have to for
|
|
536
|
+
* a host to be told it may retry while this client is still holding off.
|
|
537
|
+
*/
|
|
538
|
+
private holdOffRemainingMs;
|
|
539
|
+
/**
|
|
540
|
+
* Count one token replacement against the hold-off, and clear the rejection it answered.
|
|
541
|
+
*
|
|
542
|
+
* Every replacement passes through here, wherever it was spent from: {@link recoverRejectedSession}, and
|
|
543
|
+
* a {@link login} that follows a rejection this client surfaced. A hold-off that counted only its own
|
|
544
|
+
* would be no bound at all — the wait would sit at its first value however many sessions had been spent,
|
|
545
|
+
* and {@link SessionExpiredError.retryAfterMs} would report a minute while logins ran every few seconds.
|
|
546
|
+
* Which of the two counted a given replacement is the flag: the recovery path clears it before logging
|
|
547
|
+
* in, so the login cannot count the same one again.
|
|
548
|
+
*/
|
|
549
|
+
private noteTokenReplacement;
|
|
503
550
|
/**
|
|
504
551
|
* Note that the held session is working. A replacement that keeps serving calls for long enough is not
|
|
505
552
|
* contention, so the hold-off is forgotten and the next genuine expiry recovers immediately.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mega-yfue/eufy-sdk",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.22",
|
|
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",
|