@apocaliss92/nodedreame 1.0.0 → 1.1.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/dist/index.cjs +101 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +101 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -92,6 +92,12 @@ interface PropertyResult {
|
|
|
92
92
|
piid?: number | undefined;
|
|
93
93
|
value?: unknown;
|
|
94
94
|
code?: number | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Epoch-ms the cloud last observed this value. Only populated by the
|
|
97
|
+
* cloud-shadow read (`getCachedProperties`); absent on live `get_properties`.
|
|
98
|
+
* Surfaced so callers can report the cache age of a standby device.
|
|
99
|
+
*/
|
|
100
|
+
updateDate?: number | undefined;
|
|
95
101
|
[key: string]: unknown;
|
|
96
102
|
}
|
|
97
103
|
|
|
@@ -289,6 +295,8 @@ interface PushLike {
|
|
|
289
295
|
interface BaseDeviceDeps {
|
|
290
296
|
createPush(device: DreameDevice, session: DreameSession, region: DreameRegion): PushLike;
|
|
291
297
|
getProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;
|
|
298
|
+
/** Read the cloud-cached (shadow) values WITHOUT waking the device. */
|
|
299
|
+
getCachedProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;
|
|
292
300
|
setProperties(base: CommonInput, writes: PropertyWrite[]): Promise<PropertyResult[]>;
|
|
293
301
|
callAction(base: CommonInput, action: {
|
|
294
302
|
siid: number;
|
|
@@ -345,6 +353,13 @@ declare class BaseDevice<Events extends BaseDeviceEvents = BaseDeviceEvents> ext
|
|
|
345
353
|
start(): Promise<void>;
|
|
346
354
|
/** Live-read properties, update the cache, return the raw results. */
|
|
347
355
|
refreshProperties(props: MiotProp[]): Promise<PropertyResult[]>;
|
|
356
|
+
/**
|
|
357
|
+
* Read the CLOUD-CACHED (shadow) values of `props` WITHOUT waking the device,
|
|
358
|
+
* update the cache, and emit `propertyChanged`/`stateChanged` just like
|
|
359
|
+
* {@link refreshProperties} — but sourced from the cloud shadow endpoint, so
|
|
360
|
+
* it works for standby/offline robots (and never surfaces a false 80001).
|
|
361
|
+
*/
|
|
362
|
+
refreshCachedProperties(props: MiotProp[]): Promise<PropertyResult[]>;
|
|
348
363
|
/** Write a property to the device. */
|
|
349
364
|
setProperty(write: PropertyWrite): Promise<PropertyResult[]>;
|
|
350
365
|
/** Invoke a MIoT action on the device. */
|
|
@@ -1080,6 +1095,14 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1080
1095
|
x: number;
|
|
1081
1096
|
y: number;
|
|
1082
1097
|
}, opts?: CleanOpts): Promise<unknown>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
|
|
1100
|
+
* robot — reads {@link VacuumDevice.DEFAULT_PROPS} from the cloud-cached
|
|
1101
|
+
* endpoint. After it resolves, every typed getter (status/battery/suction/
|
|
1102
|
+
* water/cleaningMode/error/charging…) reflects the cached values, so a
|
|
1103
|
+
* standby/docked vacuum reports its state exactly as the Dreamehome app does.
|
|
1104
|
+
*/
|
|
1105
|
+
refreshFromCache(): Promise<void>;
|
|
1083
1106
|
/** The most-recently-decoded map, or `null` until {@link getMap} succeeds. */
|
|
1084
1107
|
get lastMap(): VacuumMap | null;
|
|
1085
1108
|
/**
|
|
@@ -1451,6 +1474,14 @@ declare class MowerDevice extends BaseDevice {
|
|
|
1451
1474
|
startMowingEdges(contourIds: number[][]): Promise<unknown>;
|
|
1452
1475
|
/** Spot mowing (2:50 o:103). */
|
|
1453
1476
|
startMowingSpots(spotAreaIds: number[]): Promise<unknown>;
|
|
1477
|
+
/**
|
|
1478
|
+
* Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
|
|
1479
|
+
* mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached
|
|
1480
|
+
* endpoint. After it resolves, every typed getter (status/battery/charging/
|
|
1481
|
+
* coverage/task/controlAction…) reflects the cached values, so a docked/
|
|
1482
|
+
* standby mower reports its state exactly as the Dreamehome app does.
|
|
1483
|
+
*/
|
|
1484
|
+
refreshFromCache(): Promise<void>;
|
|
1454
1485
|
/** The most-recently-parsed map, or `null` until {@link getMap} succeeds. */
|
|
1455
1486
|
get lastMap(): MowerMap | null;
|
|
1456
1487
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -92,6 +92,12 @@ interface PropertyResult {
|
|
|
92
92
|
piid?: number | undefined;
|
|
93
93
|
value?: unknown;
|
|
94
94
|
code?: number | undefined;
|
|
95
|
+
/**
|
|
96
|
+
* Epoch-ms the cloud last observed this value. Only populated by the
|
|
97
|
+
* cloud-shadow read (`getCachedProperties`); absent on live `get_properties`.
|
|
98
|
+
* Surfaced so callers can report the cache age of a standby device.
|
|
99
|
+
*/
|
|
100
|
+
updateDate?: number | undefined;
|
|
95
101
|
[key: string]: unknown;
|
|
96
102
|
}
|
|
97
103
|
|
|
@@ -289,6 +295,8 @@ interface PushLike {
|
|
|
289
295
|
interface BaseDeviceDeps {
|
|
290
296
|
createPush(device: DreameDevice, session: DreameSession, region: DreameRegion): PushLike;
|
|
291
297
|
getProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;
|
|
298
|
+
/** Read the cloud-cached (shadow) values WITHOUT waking the device. */
|
|
299
|
+
getCachedProperties(base: CommonInput, props: MiotProp[]): Promise<PropertyResult[]>;
|
|
292
300
|
setProperties(base: CommonInput, writes: PropertyWrite[]): Promise<PropertyResult[]>;
|
|
293
301
|
callAction(base: CommonInput, action: {
|
|
294
302
|
siid: number;
|
|
@@ -345,6 +353,13 @@ declare class BaseDevice<Events extends BaseDeviceEvents = BaseDeviceEvents> ext
|
|
|
345
353
|
start(): Promise<void>;
|
|
346
354
|
/** Live-read properties, update the cache, return the raw results. */
|
|
347
355
|
refreshProperties(props: MiotProp[]): Promise<PropertyResult[]>;
|
|
356
|
+
/**
|
|
357
|
+
* Read the CLOUD-CACHED (shadow) values of `props` WITHOUT waking the device,
|
|
358
|
+
* update the cache, and emit `propertyChanged`/`stateChanged` just like
|
|
359
|
+
* {@link refreshProperties} — but sourced from the cloud shadow endpoint, so
|
|
360
|
+
* it works for standby/offline robots (and never surfaces a false 80001).
|
|
361
|
+
*/
|
|
362
|
+
refreshCachedProperties(props: MiotProp[]): Promise<PropertyResult[]>;
|
|
348
363
|
/** Write a property to the device. */
|
|
349
364
|
setProperty(write: PropertyWrite): Promise<PropertyResult[]>;
|
|
350
365
|
/** Invoke a MIoT action on the device. */
|
|
@@ -1080,6 +1095,14 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1080
1095
|
x: number;
|
|
1081
1096
|
y: number;
|
|
1082
1097
|
}, opts?: CleanOpts): Promise<unknown>;
|
|
1098
|
+
/**
|
|
1099
|
+
* Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
|
|
1100
|
+
* robot — reads {@link VacuumDevice.DEFAULT_PROPS} from the cloud-cached
|
|
1101
|
+
* endpoint. After it resolves, every typed getter (status/battery/suction/
|
|
1102
|
+
* water/cleaningMode/error/charging…) reflects the cached values, so a
|
|
1103
|
+
* standby/docked vacuum reports its state exactly as the Dreamehome app does.
|
|
1104
|
+
*/
|
|
1105
|
+
refreshFromCache(): Promise<void>;
|
|
1083
1106
|
/** The most-recently-decoded map, or `null` until {@link getMap} succeeds. */
|
|
1084
1107
|
get lastMap(): VacuumMap | null;
|
|
1085
1108
|
/**
|
|
@@ -1451,6 +1474,14 @@ declare class MowerDevice extends BaseDevice {
|
|
|
1451
1474
|
startMowingEdges(contourIds: number[][]): Promise<unknown>;
|
|
1452
1475
|
/** Spot mowing (2:50 o:103). */
|
|
1453
1476
|
startMowingSpots(spotAreaIds: number[]): Promise<unknown>;
|
|
1477
|
+
/**
|
|
1478
|
+
* Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
|
|
1479
|
+
* mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached
|
|
1480
|
+
* endpoint. After it resolves, every typed getter (status/battery/charging/
|
|
1481
|
+
* coverage/task/controlAction…) reflects the cached values, so a docked/
|
|
1482
|
+
* standby mower reports its state exactly as the Dreamehome app does.
|
|
1483
|
+
*/
|
|
1484
|
+
refreshFromCache(): Promise<void>;
|
|
1454
1485
|
/** The most-recently-parsed map, or `null` until {@link getMap} succeeds. */
|
|
1455
1486
|
get lastMap(): MowerMap | null;
|
|
1456
1487
|
/**
|
package/dist/index.js
CHANGED
|
@@ -285,6 +285,16 @@ var PropertyResultSchema = z.object({
|
|
|
285
285
|
value: z.unknown().optional(),
|
|
286
286
|
code: z.number().optional()
|
|
287
287
|
}).passthrough();
|
|
288
|
+
var CachedPropEntrySchema = z.object({
|
|
289
|
+
key: z.string(),
|
|
290
|
+
value: z.unknown().optional(),
|
|
291
|
+
updateDate: z.number().optional()
|
|
292
|
+
}).passthrough();
|
|
293
|
+
var CachedPropsResponseSchema = z.object({
|
|
294
|
+
code: z.number().optional(),
|
|
295
|
+
msg: z.string().optional(),
|
|
296
|
+
data: z.array(CachedPropEntrySchema).optional()
|
|
297
|
+
}).passthrough();
|
|
288
298
|
var SendCommandResponseSchema = z.object({
|
|
289
299
|
code: z.number().optional(),
|
|
290
300
|
msg: z.string().optional(),
|
|
@@ -474,6 +484,57 @@ async function getProperties(base, props, opts = {}) {
|
|
|
474
484
|
const res = await sendCommand({ ...base, ...opts, method: "get_properties", params });
|
|
475
485
|
return extractResultArray(res, "get_properties");
|
|
476
486
|
}
|
|
487
|
+
async function getCachedProperties(base, props, opts = {}) {
|
|
488
|
+
const ctx = base.ctx ?? RequestContext.from({ ...base, host: base.apiHost });
|
|
489
|
+
const keys = props.map((p) => `${p.siid}.${p.piid}`).join(",");
|
|
490
|
+
const signal = opts.signal ?? base.signal;
|
|
491
|
+
const timeoutMs = opts.timeoutMs ?? base.timeoutMs;
|
|
492
|
+
const raw = await httpPostJsonBody({
|
|
493
|
+
ctx,
|
|
494
|
+
path: "/dreame-user-iot/iotstatus/props",
|
|
495
|
+
accessToken: base.session.accessToken,
|
|
496
|
+
body: { did: base.did, keys },
|
|
497
|
+
context: "cached properties",
|
|
498
|
+
...signal !== void 0 ? { signal } : {},
|
|
499
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
500
|
+
});
|
|
501
|
+
const parsed = CachedPropsResponseSchema.parse(raw);
|
|
502
|
+
if (parsed.code !== void 0 && parsed.code !== 0) {
|
|
503
|
+
throw new DreameApiError(
|
|
504
|
+
`cached properties rejected: code=${parsed.code} msg=${parsed.msg ?? "?"}`,
|
|
505
|
+
200,
|
|
506
|
+
parsed
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
return (parsed.data ?? []).flatMap((entry) => {
|
|
510
|
+
const [siidStr, piidStr] = entry.key.split(".");
|
|
511
|
+
const siid = Number(siidStr);
|
|
512
|
+
const piid = Number(piidStr);
|
|
513
|
+
if (!Number.isFinite(siid) || !Number.isFinite(piid)) {
|
|
514
|
+
return [];
|
|
515
|
+
}
|
|
516
|
+
const result = { siid, piid, value: coerceShadowValue(entry.value) };
|
|
517
|
+
if (entry.updateDate !== void 0) {
|
|
518
|
+
result.updateDate = entry.updateDate;
|
|
519
|
+
}
|
|
520
|
+
return [result];
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
function coerceShadowValue(value) {
|
|
524
|
+
if (typeof value !== "string") {
|
|
525
|
+
return value;
|
|
526
|
+
}
|
|
527
|
+
if (value === "true") {
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
if (value === "false") {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
if (value.trim() !== "" && Number.isFinite(Number(value))) {
|
|
534
|
+
return Number(value);
|
|
535
|
+
}
|
|
536
|
+
return value;
|
|
537
|
+
}
|
|
477
538
|
async function setProperties(base, writes, opts = {}) {
|
|
478
539
|
const params = writes.map((p) => ({
|
|
479
540
|
did: base.did,
|
|
@@ -851,6 +912,7 @@ function defaultBaseDeviceDeps() {
|
|
|
851
912
|
return {
|
|
852
913
|
createPush: (device, session, region) => new DreamePush({ device, session, region }),
|
|
853
914
|
getProperties: (base, props) => getProperties(base, props),
|
|
915
|
+
getCachedProperties: (base, props) => getCachedProperties(base, props),
|
|
854
916
|
setProperties: (base, writes) => setProperties(base, writes),
|
|
855
917
|
callAction: (base, action) => callAction(base, action)
|
|
856
918
|
};
|
|
@@ -939,6 +1001,23 @@ var BaseDevice = class extends TypedEmitter {
|
|
|
939
1001
|
async refreshProperties(props) {
|
|
940
1002
|
this.#assertOpen();
|
|
941
1003
|
const results = await this.#deps.getProperties(this.#base(), props);
|
|
1004
|
+
this.#seedFromResults(results);
|
|
1005
|
+
return results;
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Read the CLOUD-CACHED (shadow) values of `props` WITHOUT waking the device,
|
|
1009
|
+
* update the cache, and emit `propertyChanged`/`stateChanged` just like
|
|
1010
|
+
* {@link refreshProperties} — but sourced from the cloud shadow endpoint, so
|
|
1011
|
+
* it works for standby/offline robots (and never surfaces a false 80001).
|
|
1012
|
+
*/
|
|
1013
|
+
async refreshCachedProperties(props) {
|
|
1014
|
+
this.#assertOpen();
|
|
1015
|
+
const results = await this.#deps.getCachedProperties(this.#base(), props);
|
|
1016
|
+
this.#seedFromResults(results);
|
|
1017
|
+
return results;
|
|
1018
|
+
}
|
|
1019
|
+
/** Mirror a PropertyResult[] into the cache + emit the change events. */
|
|
1020
|
+
#seedFromResults(results) {
|
|
942
1021
|
const changes = [];
|
|
943
1022
|
for (const r of results) {
|
|
944
1023
|
if (typeof r.siid === "number" && typeof r.piid === "number") {
|
|
@@ -948,7 +1027,6 @@ var BaseDevice = class extends TypedEmitter {
|
|
|
948
1027
|
if (changes.length > 0) {
|
|
949
1028
|
this.#onProperties(changes);
|
|
950
1029
|
}
|
|
951
|
-
return results;
|
|
952
1030
|
}
|
|
953
1031
|
/** Write a property to the device. */
|
|
954
1032
|
async setProperty(write) {
|
|
@@ -2342,7 +2420,7 @@ var TASK = enumLookup([
|
|
|
2342
2420
|
12 /* TransientPauseEdge */,
|
|
2343
2421
|
14 /* NeedsIntervention */
|
|
2344
2422
|
]);
|
|
2345
|
-
var VacuumDevice = class extends BaseDevice {
|
|
2423
|
+
var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
2346
2424
|
#caps;
|
|
2347
2425
|
#lastMap = null;
|
|
2348
2426
|
constructor(input) {
|
|
@@ -2557,6 +2635,16 @@ var VacuumDevice = class extends BaseDevice {
|
|
|
2557
2635
|
const points = [[Math.round(point.x), Math.round(point.y), repeats, fan, water]];
|
|
2558
2636
|
return this.#startCustom(CUSTOM_CLEAN_MODE.SPOT, { points });
|
|
2559
2637
|
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
|
|
2640
|
+
* robot — reads {@link VacuumDevice.DEFAULT_PROPS} from the cloud-cached
|
|
2641
|
+
* endpoint. After it resolves, every typed getter (status/battery/suction/
|
|
2642
|
+
* water/cleaningMode/error/charging…) reflects the cached values, so a
|
|
2643
|
+
* standby/docked vacuum reports its state exactly as the Dreamehome app does.
|
|
2644
|
+
*/
|
|
2645
|
+
async refreshFromCache() {
|
|
2646
|
+
await this.refreshCachedProperties([..._VacuumDevice.DEFAULT_PROPS]);
|
|
2647
|
+
}
|
|
2560
2648
|
// -- maps ---------------------------------------------------------------
|
|
2561
2649
|
/** The most-recently-decoded map, or `null` until {@link getMap} succeeds. */
|
|
2562
2650
|
get lastMap() {
|
|
@@ -3389,7 +3477,7 @@ var STATUS = enumLookup(
|
|
|
3389
3477
|
var CHARGING2 = enumLookup(
|
|
3390
3478
|
Object.values(MowerChargingStatus).filter((v) => typeof v === "number")
|
|
3391
3479
|
);
|
|
3392
|
-
var MowerDevice = class extends BaseDevice {
|
|
3480
|
+
var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
3393
3481
|
#caps;
|
|
3394
3482
|
#fetchBatch;
|
|
3395
3483
|
#lastMap = null;
|
|
@@ -3529,6 +3617,16 @@ var MowerDevice = class extends BaseDevice {
|
|
|
3529
3617
|
}
|
|
3530
3618
|
return this.#sendTask(buildSpotPayload(spotAreaIds.map((s) => Math.trunc(s))));
|
|
3531
3619
|
}
|
|
3620
|
+
/**
|
|
3621
|
+
* Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
|
|
3622
|
+
* mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached
|
|
3623
|
+
* endpoint. After it resolves, every typed getter (status/battery/charging/
|
|
3624
|
+
* coverage/task/controlAction…) reflects the cached values, so a docked/
|
|
3625
|
+
* standby mower reports its state exactly as the Dreamehome app does.
|
|
3626
|
+
*/
|
|
3627
|
+
async refreshFromCache() {
|
|
3628
|
+
await this.refreshCachedProperties([..._MowerDevice.DEFAULT_PROPS]);
|
|
3629
|
+
}
|
|
3532
3630
|
// -- maps ---------------------------------------------------------------
|
|
3533
3631
|
/** The most-recently-parsed map, or `null` until {@link getMap} succeeds. */
|
|
3534
3632
|
get lastMap() {
|