@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
package/dist/client/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `interface EufyMega` (the typed on/once/off/emit overloads) stays in `eufy-mega.ts` next to the
|
|
6
6
|
* class — TS declaration merging requires both in the same module.
|
|
7
7
|
*/
|
|
8
|
-
import type { MegaClientConfig } from "../transport/http/mega-client.js";
|
|
8
|
+
import type { MegaClientConfig, SessionExpiredError } from "../transport/http/mega-client.js";
|
|
9
9
|
import type { FcmStore } from "../transport/push/store.js";
|
|
10
10
|
import type { FfmpegLevel } from "../transport/ffmpeg.js";
|
|
11
11
|
import type { DeviceEventMap } from "../model/capabilities/index.js";
|
|
@@ -375,8 +375,12 @@ export type EufyMegaEventMap = {
|
|
|
375
375
|
* token expired. The SDK has already cleared the persisted session, so recovery is a fresh `login()`
|
|
376
376
|
* (which usually needs 2FA). Distinct from `error`: a session error is emitted ONLY here, not also
|
|
377
377
|
* on `error`.
|
|
378
|
+
*
|
|
379
|
+
* The error carries the rate: `err.retryAfterMs` is how long the next session replacement is barred
|
|
380
|
+
* for, and `err.contended` says this session is being displaced by another client rather than expiring
|
|
381
|
+
* — which a re-login does not answer. A login made before that wait elapses extends it.
|
|
378
382
|
*/
|
|
379
|
-
sessionExpired: [err:
|
|
383
|
+
sessionExpired: [err: SessionExpiredError];
|
|
380
384
|
error: [err: Error];
|
|
381
385
|
};
|
|
382
386
|
/** Event names {@link EufyMega} can emit. */
|
package/dist/index.js
CHANGED
|
@@ -1092,9 +1092,25 @@ var MegaApiError = class extends Error {
|
|
|
1092
1092
|
};
|
|
1093
1093
|
var OWNER_ONLY_CODE = 20004;
|
|
1094
1094
|
var SessionExpiredError = class extends Error {
|
|
1095
|
-
|
|
1095
|
+
/**
|
|
1096
|
+
* How long the next session replacement is barred for, in milliseconds; `0` when nothing bars one now.
|
|
1097
|
+
*
|
|
1098
|
+
* The remainder of the client's own hold-off, which doubles per consecutive replacement and is capped —
|
|
1099
|
+
* and which every replacement extends, whether the client spent it or a login made on this error did.
|
|
1100
|
+
*/
|
|
1101
|
+
retryAfterMs;
|
|
1102
|
+
/**
|
|
1103
|
+
* Whether this rejection landed inside that bar — a token replaced recently and rejected again since.
|
|
1104
|
+
*
|
|
1105
|
+
* It says the session is being DISPLACED rather than expiring: something else is signing in on this
|
|
1106
|
+
* account, and replacing the token again only trades one login for another.
|
|
1107
|
+
*/
|
|
1108
|
+
contended;
|
|
1109
|
+
constructor(message, opts = {}) {
|
|
1096
1110
|
super(message);
|
|
1097
1111
|
this.name = "SessionExpiredError";
|
|
1112
|
+
this.retryAfterMs = opts.retryAfterMs ?? 0;
|
|
1113
|
+
this.contended = opts.contended ?? false;
|
|
1098
1114
|
}
|
|
1099
1115
|
};
|
|
1100
1116
|
var EufyCloudErrorCode = {
|
|
@@ -1133,7 +1149,7 @@ var LoginStatus = {
|
|
|
1133
1149
|
var REAUTH_HOLD_OFF_MS = 6e4;
|
|
1134
1150
|
var REAUTH_HOLD_OFF_CAP_MS = 30 * 6e4;
|
|
1135
1151
|
var REAUTH_STABLE_MS = 10 * 6e4;
|
|
1136
|
-
var CONTENDED_SESSION_HINT = "another client
|
|
1152
|
+
var CONTENDED_SESSION_HINT = "another client signed in on this account keeps displacing this session \u2014 the cloud holds about one session per account and device identity, so each login ends the other's; where the other client is another SDK install, give each its own openudid";
|
|
1137
1153
|
function tokenRejected(code, msg) {
|
|
1138
1154
|
return code === EufyCloudErrorCode.SESSION_KICKED || /user_id is empty|invalid[^,]*\btoken\b|\btoken\b[^,]*(expired|error|not exist)|kicked|(?:\btoken\b|\bsession\b)[^,]*does not exist|unauthor/i.test(msg ?? "");
|
|
1139
1155
|
}
|
|
@@ -1183,9 +1199,11 @@ var MegaHttpClient = class {
|
|
|
1183
1199
|
loggingIn = false;
|
|
1184
1200
|
/** The one in-flight re-login every call rejected on the same dead token waits on. */
|
|
1185
1201
|
reauthAttempt;
|
|
1186
|
-
/** Replacements since the held session last proved stable, and when the last one ran — see {@link
|
|
1202
|
+
/** Replacements since the held session last proved stable, and when the last one ran — see {@link holdOffRemainingMs}. */
|
|
1187
1203
|
recoveries = 0;
|
|
1188
1204
|
lastRecoveryAt = 0;
|
|
1205
|
+
/** A token of ours has been rejected and not yet replaced — see {@link noteTokenReplacement}. */
|
|
1206
|
+
rejectedTokenPending = false;
|
|
1189
1207
|
constructor(cfg) {
|
|
1190
1208
|
this.cfg = {
|
|
1191
1209
|
appName: "eufy_mega",
|
|
@@ -1468,12 +1486,17 @@ var MegaHttpClient = class {
|
|
|
1468
1486
|
const tokenFinished = identityError && retry.identity || authed && last?.status === 401 && tokenRejected(last.code, last.msg);
|
|
1469
1487
|
if (tokenFinished) {
|
|
1470
1488
|
const reason = `${path} failed (${last?.status}/${last?.code}): ${last?.msg}`;
|
|
1489
|
+
this.rejectedTokenPending = true;
|
|
1471
1490
|
const recovery = retry.reauth ? "unrecoverable" : await this.recoverRejectedSession(sentToken);
|
|
1472
1491
|
if (recovery === "retry")
|
|
1473
1492
|
return this.postSigned(host, path, body, authed, { ...retry, reauth: true }, headerOverrides);
|
|
1474
1493
|
if (!this.sessionReplacedSince(sentToken))
|
|
1475
1494
|
this.clearSession();
|
|
1476
|
-
|
|
1495
|
+
const contended = recovery === "held-off";
|
|
1496
|
+
throw new SessionExpiredError(contended ? `${reason} \u2014 ${CONTENDED_SESSION_HINT}` : reason, {
|
|
1497
|
+
retryAfterMs: this.holdOffRemainingMs(),
|
|
1498
|
+
contended
|
|
1499
|
+
});
|
|
1477
1500
|
}
|
|
1478
1501
|
throw new MegaApiError(`${path} failed (${last?.status}/${last?.code}): ${last?.msg}`, last?.code, last?.status);
|
|
1479
1502
|
}
|
|
@@ -1798,8 +1821,7 @@ var MegaHttpClient = class {
|
|
|
1798
1821
|
return "unrecoverable";
|
|
1799
1822
|
if (!this.recoveryDue())
|
|
1800
1823
|
return "held-off";
|
|
1801
|
-
this.
|
|
1802
|
-
this.lastRecoveryAt = Date.now();
|
|
1824
|
+
this.noteTokenReplacement();
|
|
1803
1825
|
this.clearSession();
|
|
1804
1826
|
return await this.reauthenticate() ? "retry" : "unrecoverable";
|
|
1805
1827
|
}
|
|
@@ -1814,20 +1836,47 @@ var MegaHttpClient = class {
|
|
|
1814
1836
|
* and a caller is told the honest reason instead of being served a fight.
|
|
1815
1837
|
*/
|
|
1816
1838
|
recoveryDue() {
|
|
1817
|
-
|
|
1839
|
+
const remaining = this.holdOffRemainingMs();
|
|
1840
|
+
if (remaining === 0)
|
|
1818
1841
|
return true;
|
|
1819
|
-
|
|
1820
|
-
const waited = Date.now() - this.lastRecoveryAt;
|
|
1821
|
-
if (waited >= wait)
|
|
1822
|
-
return true;
|
|
1823
|
-
this.logger.warn(`[mega] token rejected ${Math.round(waited / 1e3)}s after the last replacement \u2014 holding off ${Math.round(wait / 1e3)}s. ${CONTENDED_SESSION_HINT}`);
|
|
1842
|
+
this.logger.warn(`[mega] token rejected ${Math.round((Date.now() - this.lastRecoveryAt) / 1e3)}s after the last replacement \u2014 holding off ${Math.round(remaining / 1e3)}s more. ${CONTENDED_SESSION_HINT}`);
|
|
1824
1843
|
return false;
|
|
1825
1844
|
}
|
|
1845
|
+
/**
|
|
1846
|
+
* How much longer a token replacement must wait, in milliseconds; `0` when one may run now.
|
|
1847
|
+
*
|
|
1848
|
+
* The wait doubles per consecutive replacement and is capped, and it is what {@link recoveryDue} gates
|
|
1849
|
+
* this client's own recovery on — and what {@link SessionExpiredError.retryAfterMs} hands a host that
|
|
1850
|
+
* drives its own. One function so the two cannot disagree about the rate, which they would have to for
|
|
1851
|
+
* a host to be told it may retry while this client is still holding off.
|
|
1852
|
+
*/
|
|
1853
|
+
holdOffRemainingMs() {
|
|
1854
|
+
if (this.recoveries === 0)
|
|
1855
|
+
return 0;
|
|
1856
|
+
const wait = Math.min(REAUTH_HOLD_OFF_MS * 2 ** (this.recoveries - 1), REAUTH_HOLD_OFF_CAP_MS);
|
|
1857
|
+
return Math.max(0, wait - (Date.now() - this.lastRecoveryAt));
|
|
1858
|
+
}
|
|
1859
|
+
/**
|
|
1860
|
+
* Count one token replacement against the hold-off, and clear the rejection it answered.
|
|
1861
|
+
*
|
|
1862
|
+
* Every replacement passes through here, wherever it was spent from: {@link recoverRejectedSession}, and
|
|
1863
|
+
* a {@link login} that follows a rejection this client surfaced. A hold-off that counted only its own
|
|
1864
|
+
* would be no bound at all — the wait would sit at its first value however many sessions had been spent,
|
|
1865
|
+
* and {@link SessionExpiredError.retryAfterMs} would report a minute while logins ran every few seconds.
|
|
1866
|
+
* Which of the two counted a given replacement is the flag: the recovery path clears it before logging
|
|
1867
|
+
* in, so the login cannot count the same one again.
|
|
1868
|
+
*/
|
|
1869
|
+
noteTokenReplacement() {
|
|
1870
|
+
this.recoveries++;
|
|
1871
|
+
this.lastRecoveryAt = Date.now();
|
|
1872
|
+
this.rejectedTokenPending = false;
|
|
1873
|
+
}
|
|
1826
1874
|
/**
|
|
1827
1875
|
* Note that the held session is working. A replacement that keeps serving calls for long enough is not
|
|
1828
1876
|
* contention, so the hold-off is forgotten and the next genuine expiry recovers immediately.
|
|
1829
1877
|
*/
|
|
1830
1878
|
noteSessionWorking() {
|
|
1879
|
+
this.rejectedTokenPending = false;
|
|
1831
1880
|
if (this.recoveries > 0 && Date.now() - this.lastRecoveryAt > REAUTH_STABLE_MS)
|
|
1832
1881
|
this.recoveries = 0;
|
|
1833
1882
|
}
|
|
@@ -1944,6 +1993,8 @@ var MegaHttpClient = class {
|
|
|
1944
1993
|
this.sessionKey = void 0;
|
|
1945
1994
|
await this.ensureSessionKey();
|
|
1946
1995
|
this.persist();
|
|
1996
|
+
if (this.rejectedTokenPending)
|
|
1997
|
+
this.noteTokenReplacement();
|
|
1947
1998
|
return { status: LoginStatus.Ok, session: { userId, authToken, geoKey: this.auth_.geoKey, raw: res } };
|
|
1948
1999
|
}
|
|
1949
2000
|
/** Save the current token + session key for reuse across runs. */
|
|
@@ -3179,7 +3230,14 @@ function describeBound(modules, bound, ctx) {
|
|
|
3179
3230
|
else
|
|
3180
3231
|
undescribedActions.push(name);
|
|
3181
3232
|
}
|
|
3182
|
-
out.push({
|
|
3233
|
+
out.push({
|
|
3234
|
+
capability: m.capability,
|
|
3235
|
+
accessor,
|
|
3236
|
+
reads: reads2,
|
|
3237
|
+
actions,
|
|
3238
|
+
undescribedActions,
|
|
3239
|
+
events: emitsOf(m, reads2, ctx)
|
|
3240
|
+
});
|
|
3183
3241
|
}
|
|
3184
3242
|
return out;
|
|
3185
3243
|
}
|
|
@@ -3198,8 +3256,22 @@ function readDescriptor(name, m, descriptors, ctx) {
|
|
|
3198
3256
|
description: m.description
|
|
3199
3257
|
};
|
|
3200
3258
|
}
|
|
3201
|
-
function emitsOf(m) {
|
|
3202
|
-
|
|
3259
|
+
function emitsOf(m, reads2, ctx) {
|
|
3260
|
+
const installed = new Set(reads2.map((r) => r.accessor));
|
|
3261
|
+
const claimed = (m.events ?? []).filter((e) => holds(e.claim, installed, ctx)).map((e) => e.emit);
|
|
3262
|
+
return [.../* @__PURE__ */ new Set([...claimed, ...m.emits ?? []])];
|
|
3263
|
+
}
|
|
3264
|
+
function holds(claim, installed, ctx) {
|
|
3265
|
+
if (!claim)
|
|
3266
|
+
return true;
|
|
3267
|
+
if (claim.codecs && (ctx?.codec === void 0 || !claim.codecs.includes(ctx.codec)))
|
|
3268
|
+
return false;
|
|
3269
|
+
if (claim.reads?.some((name) => !installed.has(name)))
|
|
3270
|
+
return false;
|
|
3271
|
+
if (claim.homeBaseAttached !== void 0 && ctx?.homeBaseAttached !== void 0) {
|
|
3272
|
+
return ctx.homeBaseAttached === claim.homeBaseAttached;
|
|
3273
|
+
}
|
|
3274
|
+
return true;
|
|
3203
3275
|
}
|
|
3204
3276
|
|
|
3205
3277
|
// dist/model/capabilities/video.js
|
|
@@ -3704,6 +3776,9 @@ var AiDetectType = {
|
|
|
3704
3776
|
vehicle: 4,
|
|
3705
3777
|
pet: 8
|
|
3706
3778
|
};
|
|
3779
|
+
var CAMERA_AI_CLAIM = { codecs: ["camera"] };
|
|
3780
|
+
var VEHICLE_CLAIM = { ...CAMERA_AI_CLAIM, reads: ["aiDetectType"] };
|
|
3781
|
+
var DOG_CLAIM = { ...CAMERA_AI_CLAIM, homeBaseAttached: true };
|
|
3707
3782
|
var SENSITIVITY_SCALES = [
|
|
3708
3783
|
/** Standalone PIR sensor: five steps counting DOWN. */
|
|
3709
3784
|
{
|
|
@@ -4095,21 +4170,24 @@ var MOTION = {
|
|
|
4095
4170
|
events: [
|
|
4096
4171
|
{ source: "push", match: DoorbellPushEvent.MOTION_DETECTION, emit: "motion" },
|
|
4097
4172
|
{ source: "push", match: CusPushEvent.MOTION_SENSOR_PIR, emit: "motion" },
|
|
4098
|
-
{ source: "push", match: IndoorPushEvent.CRYING_DETECTION, emit: "cryingDetected" },
|
|
4099
|
-
{ source: "push", match: IndoorPushEvent.SOUND_DETECTION, emit: "soundDetected" },
|
|
4100
|
-
{ source: "push", match:
|
|
4101
|
-
{ source: "push", match:
|
|
4173
|
+
{ source: "push", match: IndoorPushEvent.CRYING_DETECTION, emit: "cryingDetected", claim: CAMERA_AI_CLAIM },
|
|
4174
|
+
{ source: "push", match: IndoorPushEvent.SOUND_DETECTION, emit: "soundDetected", claim: CAMERA_AI_CLAIM },
|
|
4175
|
+
{ source: "push", match: IndoorPushEvent.PET_DETECTION, emit: "petDetection", claim: CAMERA_AI_CLAIM },
|
|
4176
|
+
{ source: "push", match: DoorbellPushEvent.VEHICLE_DETECTION, emit: "vehicleDetected", claim: VEHICLE_CLAIM },
|
|
4177
|
+
{ source: "push", match: HB3PairedDevicePushEvent.DOG_DETECTION, emit: "dogDetected", claim: DOG_CLAIM },
|
|
4102
4178
|
{
|
|
4103
4179
|
source: "push",
|
|
4104
4180
|
match: HB3PairedDevicePushEvent.DOG_LICK_DETECTION,
|
|
4105
4181
|
emit: "dogDetected",
|
|
4106
|
-
payload: { kind: "lick" }
|
|
4182
|
+
payload: { kind: "lick" },
|
|
4183
|
+
claim: DOG_CLAIM
|
|
4107
4184
|
},
|
|
4108
4185
|
{
|
|
4109
4186
|
source: "push",
|
|
4110
4187
|
match: HB3PairedDevicePushEvent.DOG_POOP_DETECTION,
|
|
4111
4188
|
emit: "dogDetected",
|
|
4112
|
-
payload: { kind: "poop" }
|
|
4189
|
+
payload: { kind: "poop" },
|
|
4190
|
+
claim: DOG_CLAIM
|
|
4113
4191
|
}
|
|
4114
4192
|
]
|
|
4115
4193
|
};
|
|
@@ -6779,10 +6857,16 @@ var DOORBELL = {
|
|
|
6779
6857
|
properties: propertiesOf(DOORBELL_MEMBERS),
|
|
6780
6858
|
/** Doorbells self-report no single unambiguous param; the model name is the reliable signal. */
|
|
6781
6859
|
detection: { modelHints: [/doorbell/i] },
|
|
6782
|
-
/**
|
|
6860
|
+
/**
|
|
6861
|
+
* Inbound FCM doorbell events (`DoorbellPushEvent`): ring press and the package trio.
|
|
6862
|
+
*
|
|
6863
|
+
* Pet (3106) is NOT here. The id is declared identically in the doorbell, indoor and HB3-paired
|
|
6864
|
+
* vocabularies, so it belongs to the camera-wide `motion` module that every camera binds; claiming
|
|
6865
|
+
* it here as well would make it a contested id that a doorbell — which has both capabilities —
|
|
6866
|
+
* matches twice, emitting one push as two events.
|
|
6867
|
+
*/
|
|
6783
6868
|
events: [
|
|
6784
6869
|
{ source: "push", match: DoorbellPushEvent.PRESS_DOORBELL, emit: "doorbellPress" },
|
|
6785
|
-
{ source: "push", match: DoorbellPushEvent.PET_DETECTION, emit: "petDetection" },
|
|
6786
6870
|
{ source: "push", match: DoorbellPushEvent.PACKAGE_DELIVERED, emit: "packageDelivered" },
|
|
6787
6871
|
{ source: "push", match: DoorbellPushEvent.PACKAGE_TAKEN, emit: "packageTaken" },
|
|
6788
6872
|
{ source: "push", match: DoorbellPushEvent.PACKAGE_STRANDED, emit: "packageStranded" }
|
|
@@ -8275,6 +8359,25 @@ var CARPET_STRATEGIES = ["autoRaise", "avoid", "ignore"];
|
|
|
8275
8359
|
var CARPET_STRATEGY = { 0: "autoRaise", 1: "avoid", 2: "ignore" };
|
|
8276
8360
|
var CLEAN_EXTENTS = ["normal", "narrow", "quick"];
|
|
8277
8361
|
var CLEAN_EXTENT = { 0: "normal", 1: "narrow", 2: "quick" };
|
|
8362
|
+
function cleanParamWireValue(names, name) {
|
|
8363
|
+
const found = Object.entries(names).find(([, candidate]) => candidate === name);
|
|
8364
|
+
if (found === void 0)
|
|
8365
|
+
throw new Error(`clean param: ${name} is not a value this setting takes`);
|
|
8366
|
+
return Number(found[0]);
|
|
8367
|
+
}
|
|
8368
|
+
function writeCleanSetting(p, wrapper, inner, value) {
|
|
8369
|
+
p.sub(wrapper, (w) => {
|
|
8370
|
+
if (value !== 0)
|
|
8371
|
+
w.int(inner, value);
|
|
8372
|
+
});
|
|
8373
|
+
}
|
|
8374
|
+
function encodeCleanParam(cleanType, cleanExtent, mopLevel) {
|
|
8375
|
+
return rawDp((w) => w.sub(CLEAN_PARAM_FIELD.CONFIGURED, (p) => {
|
|
8376
|
+
writeCleanSetting(p, CLEAN_PARAM_FIELD.CLEAN_TYPE, CLEAN_PARAM_FIELD.VALUE, cleanParamWireValue(CLEAN_TYPE, cleanType));
|
|
8377
|
+
writeCleanSetting(p, CLEAN_PARAM_FIELD.CLEAN_EXTENT, CLEAN_PARAM_FIELD.VALUE, cleanParamWireValue(CLEAN_EXTENT, cleanExtent));
|
|
8378
|
+
writeCleanSetting(p, CLEAN_PARAM_FIELD.MOP_MODE, CLEAN_PARAM_FIELD.MOP_LEVEL, cleanParamWireValue(MOP_LEVEL, mopLevel));
|
|
8379
|
+
}));
|
|
8380
|
+
}
|
|
8278
8381
|
function decodeCleanParamValue(raw, codec, field, inner) {
|
|
8279
8382
|
if (typeof raw !== "string" || !codec)
|
|
8280
8383
|
return void 0;
|
|
@@ -9954,6 +10057,49 @@ var VACUUM_CLEAN_MEMBERS = {
|
|
|
9954
10057
|
* WATCHED: run on a T2351, it started the named scene.
|
|
9955
10058
|
*/
|
|
9956
10059
|
startScene: method(({ sink }) => (sceneId) => sink.dispatch(aiotDp(VACUUM_DP.MODE_CTRL, encodeSceneClean(sceneId))), "Run a saved cleaning scene by its id (ModeCtrlRequest method 24 over DP 152).", isAiotVacuum),
|
|
10060
|
+
/**
|
|
10061
|
+
* State the cleaning settings a run uses — `CleanParamRequest.clean_param` over DP 154.
|
|
10062
|
+
*
|
|
10063
|
+
* The write counterpart of {@link VACUUM_CLEAN_MEMBERS.cleanType},
|
|
10064
|
+
* {@link VACUUM_CLEAN_MEMBERS.cleanExtent} and {@link VACUUM_CLEAN_MEMBERS.mopLevel}: one message
|
|
10065
|
+
* carries all three, so they are set together rather than through three setters that would each send
|
|
10066
|
+
* the same message with the other two silent.
|
|
10067
|
+
*
|
|
10068
|
+
* **The evidence, and its limit.** The frame is field 1 of `CleanParamRequest`, which carries the
|
|
10069
|
+
* very `CleanParam` this module decodes out of field 1 of the reports a live T2351 sends — the
|
|
10070
|
+
* field numbers, the single-field wrappers and the `mop_mode.level` scale are all read off that
|
|
10071
|
+
* capture, and `encodeCleanParam` writes what `decodeCleanParamValue` reads. What is NOT captured is
|
|
10072
|
+
* the write direction itself. It ships as a method rather than an unverified write because the
|
|
10073
|
+
* hazard that rule answers does not arise here: DP 154 is the robot's own settings report, so a frame
|
|
10074
|
+
* it does not accept leaves those three reads unchanged, where a wrong fire-and-forget command would
|
|
10075
|
+
* look exactly like success.
|
|
10076
|
+
*
|
|
10077
|
+
* Suction is not here. It has its own data point and its own capability — a `fan` field exists in
|
|
10078
|
+
* this message and is deliberately not written, for the same reason it is not read.
|
|
10079
|
+
*/
|
|
10080
|
+
setCleanParam: {
|
|
10081
|
+
...method(({ sink }) => (cleanType, cleanExtent, mopLevel) => sink.dispatch(aiotDp(VACUUM_DP.CLEAN_PARAM, encodeCleanParam(cleanType, cleanExtent, mopLevel))), "Set the cleaning type, extent and mop water level together (CleanParamRequest.clean_param over DP 154).", isAiotVacuum),
|
|
10082
|
+
args: [
|
|
10083
|
+
{
|
|
10084
|
+
name: "cleanType",
|
|
10085
|
+
kind: "enum",
|
|
10086
|
+
values: VACUUM_CLEAN_TYPES,
|
|
10087
|
+
description: "What the robot does with a surface."
|
|
10088
|
+
},
|
|
10089
|
+
{
|
|
10090
|
+
name: "cleanExtent",
|
|
10091
|
+
kind: "enum",
|
|
10092
|
+
values: CLEAN_EXTENTS,
|
|
10093
|
+
description: "How far past the mapped edge a job reaches. Wire order, not app order."
|
|
10094
|
+
},
|
|
10095
|
+
{
|
|
10096
|
+
name: "mopLevel",
|
|
10097
|
+
kind: "enum",
|
|
10098
|
+
values: MOP_LEVELS,
|
|
10099
|
+
description: "How much water the mop lays down. Only meaningful for a clean type that mops."
|
|
10100
|
+
}
|
|
10101
|
+
]
|
|
10102
|
+
},
|
|
9957
10103
|
/**
|
|
9958
10104
|
* Clean the named rooms of a named map (ModeCtrlRequest method 1 over DP 152).
|
|
9959
10105
|
*
|
|
@@ -12992,7 +13138,8 @@ var Device = class _Device {
|
|
|
12992
13138
|
details: describeCapabilities(this.actionMap, {
|
|
12993
13139
|
codec: this.codec,
|
|
12994
13140
|
model: this.model,
|
|
12995
|
-
capabilities: new Set(this.capabilities)
|
|
13141
|
+
capabilities: new Set(this.capabilities),
|
|
13142
|
+
homeBaseAttached: this.stationSn !== this.sn
|
|
12996
13143
|
})
|
|
12997
13144
|
};
|
|
12998
13145
|
}
|