@apocaliss92/nodedreame 1.2.0 → 1.3.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 +48 -0
- package/dist/index.cjs +434 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +337 -1
- package/dist/index.d.ts +337 -1
- package/dist/index.js +432 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/support/version.ts
|
|
2
2
|
var LIBRARY_NAME = "nodedreame";
|
|
3
|
+
var LIBRARY_VERSION = "1.3.0";
|
|
3
4
|
|
|
4
5
|
// src/transport/errors.ts
|
|
5
6
|
var DreameError = class extends Error {
|
|
@@ -2769,11 +2770,11 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2769
2770
|
}
|
|
2770
2771
|
this.#requireCap(this.#caps.canCleanPerRoom, "cleanZones", "per-room cleaning");
|
|
2771
2772
|
const { repeats, fan, water } = this.#resolveCleanOpts(opts);
|
|
2772
|
-
const areas = zones.map((
|
|
2773
|
-
Math.round(
|
|
2774
|
-
Math.round(
|
|
2775
|
-
Math.round(
|
|
2776
|
-
Math.round(
|
|
2773
|
+
const areas = zones.map((z4) => [
|
|
2774
|
+
Math.round(z4.x0),
|
|
2775
|
+
Math.round(z4.y0),
|
|
2776
|
+
Math.round(z4.x1),
|
|
2777
|
+
Math.round(z4.y1),
|
|
2777
2778
|
repeats,
|
|
2778
2779
|
fan,
|
|
2779
2780
|
water
|
|
@@ -3858,7 +3859,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
3858
3859
|
if (zoneIds.length === 0) {
|
|
3859
3860
|
throw new RangeError("startMowingZones: zoneIds must not be empty");
|
|
3860
3861
|
}
|
|
3861
|
-
return this.#sendTask(buildZonePayload(zoneIds.map((
|
|
3862
|
+
return this.#sendTask(buildZonePayload(zoneIds.map((z4) => Math.trunc(z4))));
|
|
3862
3863
|
}
|
|
3863
3864
|
/** Edge / contour mowing (2:50 o:101). Contour ids are two-int pairs [[1,0]]. */
|
|
3864
3865
|
async startMowingEdges(contourIds) {
|
|
@@ -4082,6 +4083,429 @@ var Nodreame = class extends TypedEmitter {
|
|
|
4082
4083
|
await Promise.all(this.#devices.map((d) => d.applySession(session)));
|
|
4083
4084
|
}
|
|
4084
4085
|
};
|
|
4086
|
+
|
|
4087
|
+
// src/diagnostics/redact.ts
|
|
4088
|
+
var REDACTED = "[redacted]";
|
|
4089
|
+
var SENSITIVE_KEY_FRAGMENTS = [
|
|
4090
|
+
// identity / secrets
|
|
4091
|
+
"did",
|
|
4092
|
+
"uid",
|
|
4093
|
+
"token",
|
|
4094
|
+
// accessToken, refreshToken, refresh_token, token_type stripped too (safe)
|
|
4095
|
+
"mac",
|
|
4096
|
+
"serial",
|
|
4097
|
+
"email",
|
|
4098
|
+
"account",
|
|
4099
|
+
"password",
|
|
4100
|
+
"passwd",
|
|
4101
|
+
"secret",
|
|
4102
|
+
"authorization",
|
|
4103
|
+
"auth",
|
|
4104
|
+
"credential",
|
|
4105
|
+
"apikey",
|
|
4106
|
+
"api_key",
|
|
4107
|
+
"clientid",
|
|
4108
|
+
"client_id",
|
|
4109
|
+
// location / PII
|
|
4110
|
+
"gps",
|
|
4111
|
+
"coordinate",
|
|
4112
|
+
"latitude",
|
|
4113
|
+
"longitude",
|
|
4114
|
+
"lat",
|
|
4115
|
+
"lon",
|
|
4116
|
+
"lng",
|
|
4117
|
+
"ssid",
|
|
4118
|
+
"wifi",
|
|
4119
|
+
"bssid",
|
|
4120
|
+
"ipaddr",
|
|
4121
|
+
"localip",
|
|
4122
|
+
"binddomain",
|
|
4123
|
+
"host",
|
|
4124
|
+
"address",
|
|
4125
|
+
"room",
|
|
4126
|
+
"area_name",
|
|
4127
|
+
"areaname",
|
|
4128
|
+
"segmentname",
|
|
4129
|
+
"segment_name",
|
|
4130
|
+
// map binary / geometry (location-revealing)
|
|
4131
|
+
"map_info",
|
|
4132
|
+
"mapinfo",
|
|
4133
|
+
"mapblob",
|
|
4134
|
+
// free-text device names that may carry PII. NOTE: the bare fragment `name` is
|
|
4135
|
+
// intentionally NOT listed — it would over-match the catalog's command `name`
|
|
4136
|
+
// field (an enum-derived, non-sensitive label). The specific custom-name
|
|
4137
|
+
// fields below cover every PII-bearing case.
|
|
4138
|
+
"customname",
|
|
4139
|
+
"devicename",
|
|
4140
|
+
"nickname"
|
|
4141
|
+
];
|
|
4142
|
+
var EXACT_SENSITIVE_KEYS = /* @__PURE__ */ new Set(["ip"]);
|
|
4143
|
+
function isSensitiveKey(key2) {
|
|
4144
|
+
const lower = key2.toLowerCase();
|
|
4145
|
+
if (EXACT_SENSITIVE_KEYS.has(lower)) {
|
|
4146
|
+
return true;
|
|
4147
|
+
}
|
|
4148
|
+
return SENSITIVE_KEY_FRAGMENTS.some((frag) => lower.includes(frag));
|
|
4149
|
+
}
|
|
4150
|
+
function isPlainObject(v) {
|
|
4151
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4152
|
+
}
|
|
4153
|
+
function redact(value) {
|
|
4154
|
+
if (Array.isArray(value)) {
|
|
4155
|
+
return value.map((v) => redact(v));
|
|
4156
|
+
}
|
|
4157
|
+
if (isPlainObject(value)) {
|
|
4158
|
+
const out = {};
|
|
4159
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4160
|
+
out[k] = isSensitiveKey(k) ? REDACTED : redact(v);
|
|
4161
|
+
}
|
|
4162
|
+
return out;
|
|
4163
|
+
}
|
|
4164
|
+
return value;
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
// src/diagnostics/dump-format.ts
|
|
4168
|
+
import { z as z3 } from "zod";
|
|
4169
|
+
var DumpScalarSchema = z3.union([z3.string(), z3.number(), z3.boolean()]);
|
|
4170
|
+
var PropertyObservationSchema = z3.object({
|
|
4171
|
+
values: z3.array(DumpScalarSchema),
|
|
4172
|
+
unmapped: z3.array(DumpScalarSchema),
|
|
4173
|
+
enum: z3.string().optional(),
|
|
4174
|
+
count: z3.number(),
|
|
4175
|
+
firstSeen: z3.number(),
|
|
4176
|
+
lastSeen: z3.number()
|
|
4177
|
+
});
|
|
4178
|
+
var EventObservationSchema = z3.object({
|
|
4179
|
+
at: z3.number(),
|
|
4180
|
+
type: z3.string(),
|
|
4181
|
+
data: z3.unknown().optional()
|
|
4182
|
+
});
|
|
4183
|
+
var RawFrameSchema = z3.object({
|
|
4184
|
+
at: z3.number(),
|
|
4185
|
+
source: z3.string(),
|
|
4186
|
+
payload: z3.unknown()
|
|
4187
|
+
});
|
|
4188
|
+
var CommandSchema = z3.object({
|
|
4189
|
+
name: z3.string(),
|
|
4190
|
+
siid: z3.number().optional(),
|
|
4191
|
+
aiid: z3.number().optional()
|
|
4192
|
+
});
|
|
4193
|
+
var SensorSchema = z3.object({
|
|
4194
|
+
model: z3.string(),
|
|
4195
|
+
channel: z3.number().optional()
|
|
4196
|
+
});
|
|
4197
|
+
var DeviceDumpSchema = z3.object({
|
|
4198
|
+
schemaVersion: z3.literal(1),
|
|
4199
|
+
library: z3.union([z3.literal("nodedreame"), z3.literal("nodewitt")]),
|
|
4200
|
+
libraryVersion: z3.string(),
|
|
4201
|
+
device: z3.object({
|
|
4202
|
+
model: z3.string(),
|
|
4203
|
+
firmware: z3.string().optional(),
|
|
4204
|
+
region: z3.string().optional(),
|
|
4205
|
+
type: z3.string().optional()
|
|
4206
|
+
}),
|
|
4207
|
+
observations: z3.object({
|
|
4208
|
+
properties: z3.record(z3.string(), PropertyObservationSchema),
|
|
4209
|
+
events: z3.array(EventObservationSchema),
|
|
4210
|
+
rawFrames: z3.array(RawFrameSchema).optional()
|
|
4211
|
+
}),
|
|
4212
|
+
catalog: z3.object({
|
|
4213
|
+
commands: z3.array(CommandSchema).optional(),
|
|
4214
|
+
capabilities: z3.record(z3.string(), z3.unknown()).optional(),
|
|
4215
|
+
sensors: z3.array(SensorSchema).optional()
|
|
4216
|
+
}),
|
|
4217
|
+
meta: z3.object({
|
|
4218
|
+
startedAt: z3.number(),
|
|
4219
|
+
durationMs: z3.number(),
|
|
4220
|
+
generatedAt: z3.number()
|
|
4221
|
+
})
|
|
4222
|
+
});
|
|
4223
|
+
|
|
4224
|
+
// src/diagnostics/dumper.ts
|
|
4225
|
+
function commandsForFamily(family) {
|
|
4226
|
+
const commands = [];
|
|
4227
|
+
if (family === "vacuum") {
|
|
4228
|
+
for (const [name, ref] of Object.entries(VACUUM_ACTION)) {
|
|
4229
|
+
commands.push({ name, siid: ref.siid, aiid: ref.aiid });
|
|
4230
|
+
}
|
|
4231
|
+
} else if (family === "mower") {
|
|
4232
|
+
for (const [name, ref] of Object.entries(MOWER_ACTION)) {
|
|
4233
|
+
commands.push({ name, siid: ref.siid, aiid: ref.aiid });
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
return commands;
|
|
4237
|
+
}
|
|
4238
|
+
function decoderFor(enumName, members) {
|
|
4239
|
+
const lookup = enumLookup(members);
|
|
4240
|
+
return { enumName, isMember: (raw) => lookup(raw) !== null };
|
|
4241
|
+
}
|
|
4242
|
+
function vacuumDecoders() {
|
|
4243
|
+
return {
|
|
4244
|
+
"2.1": decoderFor(
|
|
4245
|
+
"MiotState",
|
|
4246
|
+
Object.values(MiotState).filter((v) => typeof v === "number")
|
|
4247
|
+
),
|
|
4248
|
+
"3.2": decoderFor(
|
|
4249
|
+
"ChargingStatus",
|
|
4250
|
+
Object.values(ChargingStatus).filter((v) => typeof v === "number")
|
|
4251
|
+
),
|
|
4252
|
+
"4.4": decoderFor(
|
|
4253
|
+
"SuctionLevel",
|
|
4254
|
+
Object.values(SuctionLevel).filter((v) => typeof v === "number")
|
|
4255
|
+
),
|
|
4256
|
+
"4.5": decoderFor(
|
|
4257
|
+
"WaterVolume",
|
|
4258
|
+
Object.values(WaterVolume).filter((v) => typeof v === "number")
|
|
4259
|
+
),
|
|
4260
|
+
"2.6": decoderFor(
|
|
4261
|
+
"CleaningMode",
|
|
4262
|
+
Object.values(CleaningMode).filter((v) => typeof v === "number")
|
|
4263
|
+
),
|
|
4264
|
+
"4.1": decoderFor(
|
|
4265
|
+
"TaskStatus",
|
|
4266
|
+
Object.values(TaskStatus).filter((v) => typeof v === "number")
|
|
4267
|
+
)
|
|
4268
|
+
};
|
|
4269
|
+
}
|
|
4270
|
+
function mowerDecoders() {
|
|
4271
|
+
return {
|
|
4272
|
+
"2.1": decoderFor(
|
|
4273
|
+
"MowerStatus",
|
|
4274
|
+
Object.values(MowerStatus).filter((v) => typeof v === "number")
|
|
4275
|
+
),
|
|
4276
|
+
"3.2": decoderFor(
|
|
4277
|
+
"MowerChargingStatus",
|
|
4278
|
+
Object.values(MowerChargingStatus).filter(
|
|
4279
|
+
(v) => typeof v === "number"
|
|
4280
|
+
)
|
|
4281
|
+
),
|
|
4282
|
+
"5.104": decoderFor(
|
|
4283
|
+
"MowerTaskStatus",
|
|
4284
|
+
Object.values(MowerTaskStatus).filter((v) => typeof v === "number")
|
|
4285
|
+
),
|
|
4286
|
+
"2.2": decoderFor(
|
|
4287
|
+
"MowerFault",
|
|
4288
|
+
Object.values(MowerFault).filter((v) => typeof v === "number")
|
|
4289
|
+
)
|
|
4290
|
+
};
|
|
4291
|
+
}
|
|
4292
|
+
function toScalar(value) {
|
|
4293
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4294
|
+
return value;
|
|
4295
|
+
}
|
|
4296
|
+
return null;
|
|
4297
|
+
}
|
|
4298
|
+
var PropertyAccumulator = class {
|
|
4299
|
+
#decoders;
|
|
4300
|
+
#entries = /* @__PURE__ */ new Map();
|
|
4301
|
+
constructor(decoders = {}) {
|
|
4302
|
+
this.#decoders = decoders;
|
|
4303
|
+
}
|
|
4304
|
+
record(siid, piid, rawValue, at) {
|
|
4305
|
+
const key2 = `${siid}.${piid}`;
|
|
4306
|
+
const scalar = toScalar(rawValue);
|
|
4307
|
+
let entry = this.#entries.get(key2);
|
|
4308
|
+
if (!entry) {
|
|
4309
|
+
entry = { values: [], unmapped: [], count: 0, firstSeen: at, lastSeen: at };
|
|
4310
|
+
this.#entries.set(key2, entry);
|
|
4311
|
+
}
|
|
4312
|
+
entry.count += 1;
|
|
4313
|
+
entry.lastSeen = at;
|
|
4314
|
+
if (scalar === null) {
|
|
4315
|
+
return;
|
|
4316
|
+
}
|
|
4317
|
+
if (!entry.values.includes(scalar)) {
|
|
4318
|
+
entry.values.push(scalar);
|
|
4319
|
+
}
|
|
4320
|
+
const decoder = this.#decoders[key2];
|
|
4321
|
+
if (decoder && typeof scalar === "number" && !decoder.isMember(scalar)) {
|
|
4322
|
+
if (!entry.unmapped.includes(scalar)) {
|
|
4323
|
+
entry.unmapped.push(scalar);
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
snapshot() {
|
|
4328
|
+
const out = {};
|
|
4329
|
+
for (const [key2, e] of this.#entries) {
|
|
4330
|
+
const decoder = this.#decoders[key2];
|
|
4331
|
+
const base = {
|
|
4332
|
+
values: [...e.values],
|
|
4333
|
+
unmapped: [...e.unmapped],
|
|
4334
|
+
count: e.count,
|
|
4335
|
+
firstSeen: e.firstSeen,
|
|
4336
|
+
lastSeen: e.lastSeen
|
|
4337
|
+
};
|
|
4338
|
+
out[key2] = decoder ? { ...base, enum: decoder.enumName } : base;
|
|
4339
|
+
}
|
|
4340
|
+
return out;
|
|
4341
|
+
}
|
|
4342
|
+
};
|
|
4343
|
+
var DEFAULT_REFRESH_INTERVAL_MS = 3e4;
|
|
4344
|
+
var DEFAULT_MAX_RAW_FRAMES = 500;
|
|
4345
|
+
var Dumper = class {
|
|
4346
|
+
#device;
|
|
4347
|
+
#opts;
|
|
4348
|
+
#acc;
|
|
4349
|
+
#events = [];
|
|
4350
|
+
#rawFrames = [];
|
|
4351
|
+
#catalog;
|
|
4352
|
+
#type;
|
|
4353
|
+
#onProperty;
|
|
4354
|
+
#onEvent;
|
|
4355
|
+
#timer = null;
|
|
4356
|
+
#startedAt = 0;
|
|
4357
|
+
#started = false;
|
|
4358
|
+
constructor(device, catalog, type, decoders, options = {}) {
|
|
4359
|
+
this.#device = device;
|
|
4360
|
+
this.#catalog = catalog;
|
|
4361
|
+
this.#type = type;
|
|
4362
|
+
this.#acc = new PropertyAccumulator(decoders);
|
|
4363
|
+
this.#opts = {
|
|
4364
|
+
captureRawFrames: options.captureRawFrames ?? false,
|
|
4365
|
+
maxRawFrames: options.maxRawFrames ?? DEFAULT_MAX_RAW_FRAMES,
|
|
4366
|
+
refreshIntervalMs: options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS
|
|
4367
|
+
};
|
|
4368
|
+
this.#onProperty = (e) => {
|
|
4369
|
+
this.#acc.record(e.siid, e.piid, e.value, Date.now());
|
|
4370
|
+
this.#pushRaw("mqtt:property", e);
|
|
4371
|
+
};
|
|
4372
|
+
this.#onEvent = (e) => {
|
|
4373
|
+
this.#events.push({
|
|
4374
|
+
at: Date.now(),
|
|
4375
|
+
type: `${e.siid}.${e.eiid}`,
|
|
4376
|
+
data: { arguments: e.arguments }
|
|
4377
|
+
});
|
|
4378
|
+
this.#pushRaw("mqtt:event", e);
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
/** Attach to the live stream + arm the periodic shadow refresh. Idempotent. */
|
|
4382
|
+
async start() {
|
|
4383
|
+
if (this.#started) {
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
this.#started = true;
|
|
4387
|
+
this.#startedAt = Date.now();
|
|
4388
|
+
this.#device.on("propertyChanged", this.#onProperty);
|
|
4389
|
+
this.#device.on("event", this.#onEvent);
|
|
4390
|
+
await this.#refresh();
|
|
4391
|
+
if (this.#opts.refreshIntervalMs > 0 && this.#device.refreshFromCache) {
|
|
4392
|
+
this.#timer = setInterval(() => void this.#refresh(), this.#opts.refreshIntervalMs);
|
|
4393
|
+
this.#timer.unref?.();
|
|
4394
|
+
}
|
|
4395
|
+
}
|
|
4396
|
+
/** Detach every listener + clear the timer. Idempotent. */
|
|
4397
|
+
stop() {
|
|
4398
|
+
if (!this.#started) {
|
|
4399
|
+
return Promise.resolve();
|
|
4400
|
+
}
|
|
4401
|
+
this.#started = false;
|
|
4402
|
+
if (this.#timer) {
|
|
4403
|
+
clearInterval(this.#timer);
|
|
4404
|
+
this.#timer = null;
|
|
4405
|
+
}
|
|
4406
|
+
this.#device.off("propertyChanged", this.#onProperty);
|
|
4407
|
+
this.#device.off("event", this.#onEvent);
|
|
4408
|
+
return Promise.resolve();
|
|
4409
|
+
}
|
|
4410
|
+
/** Build the anonymized, schema-valid dump. Works live or after {@link stop}. */
|
|
4411
|
+
export() {
|
|
4412
|
+
const now = Date.now();
|
|
4413
|
+
const dump = {
|
|
4414
|
+
schemaVersion: 1,
|
|
4415
|
+
library: "nodedreame",
|
|
4416
|
+
libraryVersion: LIBRARY_VERSION,
|
|
4417
|
+
device: {
|
|
4418
|
+
model: this.#device.model,
|
|
4419
|
+
type: this.#type
|
|
4420
|
+
},
|
|
4421
|
+
observations: {
|
|
4422
|
+
properties: this.#acc.snapshot(),
|
|
4423
|
+
events: [...this.#events],
|
|
4424
|
+
...this.#opts.captureRawFrames ? { rawFrames: this.#redactFrames() } : {}
|
|
4425
|
+
},
|
|
4426
|
+
catalog: this.#catalog,
|
|
4427
|
+
meta: {
|
|
4428
|
+
startedAt: this.#startedAt,
|
|
4429
|
+
durationMs: Math.max(0, now - this.#startedAt),
|
|
4430
|
+
generatedAt: now
|
|
4431
|
+
}
|
|
4432
|
+
};
|
|
4433
|
+
const anonymized = redact(dump);
|
|
4434
|
+
return DeviceDumpSchema.parse(anonymized);
|
|
4435
|
+
}
|
|
4436
|
+
/** Deterministic pretty JSON of {@link export}. */
|
|
4437
|
+
exportJson() {
|
|
4438
|
+
return JSON.stringify(this.export(), null, 2);
|
|
4439
|
+
}
|
|
4440
|
+
async #refresh() {
|
|
4441
|
+
if (this.#device.refreshFromCache) {
|
|
4442
|
+
try {
|
|
4443
|
+
await this.#device.refreshFromCache();
|
|
4444
|
+
} catch {
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
#pushRaw(source, payload) {
|
|
4449
|
+
if (!this.#opts.captureRawFrames) {
|
|
4450
|
+
return;
|
|
4451
|
+
}
|
|
4452
|
+
this.#rawFrames.push({ at: Date.now(), source, payload });
|
|
4453
|
+
if (this.#rawFrames.length > this.#opts.maxRawFrames) {
|
|
4454
|
+
this.#rawFrames.shift();
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
#redactFrames() {
|
|
4458
|
+
return this.#rawFrames.map((f) => ({ at: f.at, source: f.source, payload: redact(f.payload) }));
|
|
4459
|
+
}
|
|
4460
|
+
};
|
|
4461
|
+
function familyOf(device) {
|
|
4462
|
+
if (device instanceof VacuumDevice) {
|
|
4463
|
+
return "vacuum";
|
|
4464
|
+
}
|
|
4465
|
+
if (device instanceof MowerDevice) {
|
|
4466
|
+
return "mower";
|
|
4467
|
+
}
|
|
4468
|
+
if (!(device instanceof BaseDevice)) {
|
|
4469
|
+
if (device.model.startsWith("dreame.vacuum.")) {
|
|
4470
|
+
return "vacuum";
|
|
4471
|
+
}
|
|
4472
|
+
if (device.model.startsWith("dreame.mower.")) {
|
|
4473
|
+
return "mower";
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
return "device";
|
|
4477
|
+
}
|
|
4478
|
+
function decodersFor(device) {
|
|
4479
|
+
switch (familyOf(device)) {
|
|
4480
|
+
case "vacuum":
|
|
4481
|
+
return vacuumDecoders();
|
|
4482
|
+
case "mower":
|
|
4483
|
+
return mowerDecoders();
|
|
4484
|
+
default:
|
|
4485
|
+
return {};
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
function typeFor(device) {
|
|
4489
|
+
return familyOf(device);
|
|
4490
|
+
}
|
|
4491
|
+
function catalogFor(device) {
|
|
4492
|
+
return {
|
|
4493
|
+
commands: commandsForFamily(familyOf(device)),
|
|
4494
|
+
capabilities: { tokens: [...device.capabilities.list()] }
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
function createDumper(target, options) {
|
|
4498
|
+
return new Dumper(
|
|
4499
|
+
target,
|
|
4500
|
+
catalogFor(target),
|
|
4501
|
+
typeFor(target),
|
|
4502
|
+
decodersFor(target),
|
|
4503
|
+
options ?? {}
|
|
4504
|
+
);
|
|
4505
|
+
}
|
|
4506
|
+
function createClientDumper(client, options) {
|
|
4507
|
+
return client.devices.map((d) => createDumper(d, options));
|
|
4508
|
+
}
|
|
4085
4509
|
export {
|
|
4086
4510
|
BaseDevice,
|
|
4087
4511
|
ChargingStatus,
|
|
@@ -4110,6 +4534,8 @@ export {
|
|
|
4110
4534
|
VacuumCapabilityResolver,
|
|
4111
4535
|
VacuumDevice,
|
|
4112
4536
|
WaterVolume,
|
|
4537
|
+
createClientDumper,
|
|
4538
|
+
createDumper,
|
|
4113
4539
|
getMowerCapabilities,
|
|
4114
4540
|
getVacuumCapabilities,
|
|
4115
4541
|
renderMowerSvg,
|