@apocaliss92/nodedreame 1.4.0 → 1.6.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 +167 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -1
- package/dist/index.d.ts +89 -1
- package/dist/index.js +163 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,6 +30,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
AI_FEATURE_BIT: () => AI_FEATURE_BIT,
|
|
34
|
+
AI_FEATURE_JSON_KEY: () => AI_FEATURE_JSON_KEY,
|
|
33
35
|
BaseDevice: () => BaseDevice,
|
|
34
36
|
ChargingStatus: () => ChargingStatus,
|
|
35
37
|
CleaningMode: () => CleaningMode,
|
|
@@ -59,6 +61,8 @@ __export(index_exports, {
|
|
|
59
61
|
WaterVolume: () => WaterVolume,
|
|
60
62
|
createClientDumper: () => createClientDumper,
|
|
61
63
|
createDumper: () => createDumper,
|
|
64
|
+
decodeAiFeature: () => decodeAiFeature,
|
|
65
|
+
encodeAiFeatureWrite: () => encodeAiFeatureWrite,
|
|
62
66
|
getMowerCapabilities: () => getMowerCapabilities,
|
|
63
67
|
getVacuumCapabilities: () => getVacuumCapabilities,
|
|
64
68
|
renderMowerSvg: () => renderMowerSvg,
|
|
@@ -69,7 +73,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
69
73
|
|
|
70
74
|
// src/support/version.ts
|
|
71
75
|
var LIBRARY_NAME = "nodedreame";
|
|
72
|
-
var LIBRARY_VERSION = "1.
|
|
76
|
+
var LIBRARY_VERSION = "1.6.0";
|
|
73
77
|
|
|
74
78
|
// src/transport/errors.ts
|
|
75
79
|
var DreameError = class extends Error {
|
|
@@ -1218,6 +1222,14 @@ var VACUUM_PROP = {
|
|
|
1218
1222
|
CLEANING_MODE: { siid: 4, piid: 23 },
|
|
1219
1223
|
/** VERIFIED r2532a — Child Lock boolean. */
|
|
1220
1224
|
CHILD_LOCK: { siid: 4, piid: 27 },
|
|
1225
|
+
/**
|
|
1226
|
+
* AI obstacle-detection toggle bundle (Tasshack types.py:1609 — `AI_DETECTION`,
|
|
1227
|
+
* siid 4 piid 22). The value packs ALL AI-obstacle switches into ONE property,
|
|
1228
|
+
* encoded as EITHER an int bitmask (`DreameVacuumAIProperty`) OR a JSON string
|
|
1229
|
+
* (`DreameVacuumStrAIProperty`) depending on firmware. Decode/encode per-feature
|
|
1230
|
+
* via `ai-detection.ts` ({@link decodeAiFeature} / {@link encodeAiFeatureWrite}).
|
|
1231
|
+
*/
|
|
1232
|
+
AI_DETECTION: { siid: 4, piid: 22 },
|
|
1221
1233
|
/** VERIFIED r2532a 2026-05-03 — task progress percentage 0..100. */
|
|
1222
1234
|
TASK_PROGRESS_PCT: { siid: 4, piid: 63 },
|
|
1223
1235
|
/** VERIFIED r2532a — mop-drying progress (minutes ticking during MopDrying). */
|
|
@@ -1556,7 +1568,91 @@ function parseFaultList(value) {
|
|
|
1556
1568
|
return codes;
|
|
1557
1569
|
}
|
|
1558
1570
|
|
|
1571
|
+
// src/models/vacuum/ai-detection.ts
|
|
1572
|
+
var AI_FEATURE_BIT = {
|
|
1573
|
+
furnitureDetection: 1,
|
|
1574
|
+
obstacleDetection: 2,
|
|
1575
|
+
obstaclePicture: 4,
|
|
1576
|
+
fluidDetection: 8,
|
|
1577
|
+
petDetection: 16,
|
|
1578
|
+
obstacleImageUpload: 32,
|
|
1579
|
+
// AI_IMAGE = 64 is an internal flag, not a user toggle — intentionally omitted.
|
|
1580
|
+
petAvoidance: 128,
|
|
1581
|
+
fuzzyObstacleDetection: 256,
|
|
1582
|
+
petPicture: 512,
|
|
1583
|
+
petFocusedDetection: 1024,
|
|
1584
|
+
largeParticlesBoost: 2048
|
|
1585
|
+
};
|
|
1586
|
+
var AI_FEATURE_JSON_KEY = {
|
|
1587
|
+
obstacleDetection: "obstacle_detect_switch",
|
|
1588
|
+
obstacleImageUpload: "obstacle_app_display_switch",
|
|
1589
|
+
petDetection: "whether_have_pet",
|
|
1590
|
+
humanDetection: "human_detect_switch",
|
|
1591
|
+
furnitureDetection: "furniture_detect_switch",
|
|
1592
|
+
fluidDetection: "fluid_detect_switch"
|
|
1593
|
+
};
|
|
1594
|
+
function isRecord(v) {
|
|
1595
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1596
|
+
}
|
|
1597
|
+
function parseJsonPayload(raw) {
|
|
1598
|
+
try {
|
|
1599
|
+
const parsed = JSON.parse(raw);
|
|
1600
|
+
return isRecord(parsed) ? parsed : null;
|
|
1601
|
+
} catch {
|
|
1602
|
+
return null;
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
function coerceBool(v) {
|
|
1606
|
+
if (typeof v === "boolean") return v;
|
|
1607
|
+
if (v === 1 || v === "1") return true;
|
|
1608
|
+
if (v === 0 || v === "0") return false;
|
|
1609
|
+
return null;
|
|
1610
|
+
}
|
|
1611
|
+
function decodeAiFeature(raw, feature) {
|
|
1612
|
+
if (typeof raw === "number") {
|
|
1613
|
+
const bit = AI_FEATURE_BIT[feature];
|
|
1614
|
+
if (bit === void 0) return null;
|
|
1615
|
+
return (raw & bit) === bit;
|
|
1616
|
+
}
|
|
1617
|
+
if (typeof raw === "string") {
|
|
1618
|
+
const key2 = AI_FEATURE_JSON_KEY[feature];
|
|
1619
|
+
if (key2 === void 0) return null;
|
|
1620
|
+
const payload = parseJsonPayload(raw);
|
|
1621
|
+
if (payload === null) return null;
|
|
1622
|
+
return coerceBool(payload[key2]);
|
|
1623
|
+
}
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
function encodeAiFeatureWrite(raw, feature, value) {
|
|
1627
|
+
if (typeof raw === "number") {
|
|
1628
|
+
const bit = AI_FEATURE_BIT[feature];
|
|
1629
|
+
if (bit === void 0) {
|
|
1630
|
+
throw new Error(`encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`);
|
|
1631
|
+
}
|
|
1632
|
+
return value ? raw | bit : raw & ~bit;
|
|
1633
|
+
}
|
|
1634
|
+
if (typeof raw === "string") {
|
|
1635
|
+
const key2 = AI_FEATURE_JSON_KEY[feature];
|
|
1636
|
+
if (key2 === void 0) {
|
|
1637
|
+
throw new Error(`encodeAiFeatureWrite: feature "${feature}" has no JSON-key representation`);
|
|
1638
|
+
}
|
|
1639
|
+
return JSON.stringify({ [key2]: value });
|
|
1640
|
+
}
|
|
1641
|
+
throw new Error(
|
|
1642
|
+
`encodeAiFeatureWrite: AI_DETECTION value not yet known \u2014 cannot safely toggle "${feature}"`
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1559
1646
|
// src/models/vacuum/capabilities.ts
|
|
1647
|
+
var X50_AI_FEATURES = [
|
|
1648
|
+
"obstacleDetection",
|
|
1649
|
+
"petDetection",
|
|
1650
|
+
"furnitureDetection",
|
|
1651
|
+
"fluidDetection",
|
|
1652
|
+
"obstaclePicture",
|
|
1653
|
+
"obstacleImageUpload",
|
|
1654
|
+
"fuzzyObstacleDetection"
|
|
1655
|
+
];
|
|
1560
1656
|
var FALLBACK = {
|
|
1561
1657
|
verified: false,
|
|
1562
1658
|
canMop: false,
|
|
@@ -1585,7 +1681,9 @@ var FALLBACK = {
|
|
|
1585
1681
|
2 /* Intense */,
|
|
1586
1682
|
3 /* Max */
|
|
1587
1683
|
],
|
|
1588
|
-
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */]
|
|
1684
|
+
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */],
|
|
1685
|
+
// An unknown model advertises no AI toggles until its feature set is verified.
|
|
1686
|
+
supportedAiFeatures: []
|
|
1589
1687
|
};
|
|
1590
1688
|
var X50_FAMILY = {
|
|
1591
1689
|
canMop: true,
|
|
@@ -1611,7 +1709,8 @@ var X50_FAMILY = {
|
|
|
1611
1709
|
2 /* Intense */,
|
|
1612
1710
|
3 /* Max */
|
|
1613
1711
|
],
|
|
1614
|
-
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */]
|
|
1712
|
+
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */],
|
|
1713
|
+
supportedAiFeatures: X50_AI_FEATURES
|
|
1615
1714
|
};
|
|
1616
1715
|
function deepFreeze(obj) {
|
|
1617
1716
|
if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
|
|
@@ -2745,6 +2844,39 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2745
2844
|
get volume() {
|
|
2746
2845
|
return this.#num(SETTINGS_PROP.VOLUME.siid, SETTINGS_PROP.VOLUME.piid);
|
|
2747
2846
|
}
|
|
2847
|
+
// -- AI obstacle-detection ----------------------------------------------
|
|
2848
|
+
/**
|
|
2849
|
+
* The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON
|
|
2850
|
+
* string, depending on firmware — or `null` when not yet observed. Prefer the
|
|
2851
|
+
* decoded {@link aiFeature} / {@link supportedAiFeatures} surface.
|
|
2852
|
+
*/
|
|
2853
|
+
get aiDetectionRaw() {
|
|
2854
|
+
const v = this.getProperty(VACUUM_PROP.AI_DETECTION.siid, VACUUM_PROP.AI_DETECTION.piid)?.value;
|
|
2855
|
+
return typeof v === "number" || typeof v === "string" ? v : null;
|
|
2856
|
+
}
|
|
2857
|
+
/** The AI-obstacle toggles this model exposes (from its capability record). */
|
|
2858
|
+
get supportedAiFeatures() {
|
|
2859
|
+
return this.#caps.supportedAiFeatures;
|
|
2860
|
+
}
|
|
2861
|
+
/**
|
|
2862
|
+
* Read ONE AI-obstacle toggle's on/off state, decoded from the packed
|
|
2863
|
+
* `AI_DETECTION` value (transparent across the int / JSON encodings). `null`
|
|
2864
|
+
* when the value is unobserved or the feature is not representable.
|
|
2865
|
+
*/
|
|
2866
|
+
aiFeature(feature) {
|
|
2867
|
+
return decodeAiFeature(this.aiDetectionRaw, feature);
|
|
2868
|
+
}
|
|
2869
|
+
/**
|
|
2870
|
+
* Toggle ONE AI-obstacle feature, preserving the others via a read-modify-write
|
|
2871
|
+
* of the packed `AI_DETECTION` property (mirrors Tasshack `set_ai_detection`).
|
|
2872
|
+
* Capability-gated on `hasAiObstacleDetection`; rejects when the current value
|
|
2873
|
+
* is unknown (a blind write would clobber the other toggles).
|
|
2874
|
+
*/
|
|
2875
|
+
async setAiFeature(feature, value) {
|
|
2876
|
+
this.#requireCap(this.#caps.hasAiObstacleDetection, "setAiFeature", "AI obstacle detection");
|
|
2877
|
+
const next = encodeAiFeatureWrite(this.aiDetectionRaw, feature, value);
|
|
2878
|
+
return this.setProperty({ ...VACUUM_PROP.AI_DETECTION, value: next });
|
|
2879
|
+
}
|
|
2748
2880
|
// -- command helpers ----------------------------------------------------
|
|
2749
2881
|
#resolveCleanOpts(opts) {
|
|
2750
2882
|
const repeats = Math.max(1, Math.trunc(opts.repeats ?? 1));
|
|
@@ -2908,6 +3040,23 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2908
3040
|
if (filename === null) return null;
|
|
2909
3041
|
return this.getMap({ filename, ...opts });
|
|
2910
3042
|
}
|
|
3043
|
+
/**
|
|
3044
|
+
* Seed {@link mapFilename} from the CLOUD SHADOW (the last value the robot
|
|
3045
|
+
* pushed for siid 6 piid 3) WITHOUT waking it — so a docked/idle robot can
|
|
3046
|
+
* surface its LAST cleaning map without a fresh clean. Emits the usual
|
|
3047
|
+
* `propertyChanged`/`stateChanged` (so a map-watching consumer re-renders) and
|
|
3048
|
+
* returns the resolved {@link mapFilename} (null when the model has no map or
|
|
3049
|
+
* the shadow carries no map path yet).
|
|
3050
|
+
*
|
|
3051
|
+
* NOTE: the shadow holds the last LIVE-PATH filename; its OSS blob may have
|
|
3052
|
+
* expired, in which case a follow-up {@link fetchLatestMap} rejects — treat
|
|
3053
|
+
* that as "no saved map available".
|
|
3054
|
+
*/
|
|
3055
|
+
async refreshSavedMapFilename() {
|
|
3056
|
+
if (!this.#caps.canMap) return null;
|
|
3057
|
+
await this.refreshCachedProperties([VACUUM_PROP.MAP_PATH]);
|
|
3058
|
+
return this.mapFilename;
|
|
3059
|
+
}
|
|
2911
3060
|
/**
|
|
2912
3061
|
* The current room/segment id, derived from the most-recently-decoded map's
|
|
2913
3062
|
* active-segment set (`sa`). `null` when no map has been fetched or no
|
|
@@ -2963,6 +3112,7 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2963
3112
|
VACUUM_PROP.WATER_VOLUME,
|
|
2964
3113
|
VACUUM_PROP.CLEAN_MODE_SETTING,
|
|
2965
3114
|
VACUUM_PROP.TASK_PROGRESS_PCT,
|
|
3115
|
+
VACUUM_PROP.AI_DETECTION,
|
|
2966
3116
|
BATTERY_PROP.LEVEL,
|
|
2967
3117
|
BATTERY_PROP.CHARGING_STATUS,
|
|
2968
3118
|
CONSUMABLE_PROP.MAIN_BRUSH_LEFT,
|
|
@@ -3135,7 +3285,7 @@ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
|
|
|
3135
3285
|
})(MowerFault || {});
|
|
3136
3286
|
|
|
3137
3287
|
// src/models/mower/decode.ts
|
|
3138
|
-
function
|
|
3288
|
+
function isRecord2(v) {
|
|
3139
3289
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3140
3290
|
}
|
|
3141
3291
|
function numArrayOrNull(v) {
|
|
@@ -3152,12 +3302,12 @@ function numArrayOrNull(v) {
|
|
|
3152
3302
|
return out;
|
|
3153
3303
|
}
|
|
3154
3304
|
function parseTaskDescriptor(value) {
|
|
3155
|
-
if (!
|
|
3305
|
+
if (!isRecord2(value)) {
|
|
3156
3306
|
return null;
|
|
3157
3307
|
}
|
|
3158
3308
|
const t = value["t"];
|
|
3159
3309
|
const d = value["d"];
|
|
3160
|
-
if (typeof t !== "string" || !
|
|
3310
|
+
if (typeof t !== "string" || !isRecord2(d)) {
|
|
3161
3311
|
return null;
|
|
3162
3312
|
}
|
|
3163
3313
|
const exe = d["exe"];
|
|
@@ -3186,7 +3336,7 @@ function controlActionFor(code) {
|
|
|
3186
3336
|
return controlLookup(code);
|
|
3187
3337
|
}
|
|
3188
3338
|
function parseControlStatus(value) {
|
|
3189
|
-
if (!
|
|
3339
|
+
if (!isRecord2(value)) {
|
|
3190
3340
|
return null;
|
|
3191
3341
|
}
|
|
3192
3342
|
const status = value["status"];
|
|
@@ -3296,7 +3446,7 @@ var MowerCapabilityResolver = class {
|
|
|
3296
3446
|
// src/models/mower/map/parser.ts
|
|
3297
3447
|
var PATH_SENTINEL_X = 32767;
|
|
3298
3448
|
var PATH_SENTINEL_Y = -32768;
|
|
3299
|
-
function
|
|
3449
|
+
function isRecord3(v) {
|
|
3300
3450
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3301
3451
|
}
|
|
3302
3452
|
function asNumber(v, fallback) {
|
|
@@ -3334,7 +3484,7 @@ function parseSplitPos(info) {
|
|
|
3334
3484
|
return 0;
|
|
3335
3485
|
}
|
|
3336
3486
|
function parsePolygonList(dataMap) {
|
|
3337
|
-
if (!
|
|
3487
|
+
if (!isRecord3(dataMap) || dataMap["dataType"] !== "Map") {
|
|
3338
3488
|
return [];
|
|
3339
3489
|
}
|
|
3340
3490
|
const value = dataMap["value"];
|
|
@@ -3346,7 +3496,7 @@ function extractPathCoords(pathList) {
|
|
|
3346
3496
|
}
|
|
3347
3497
|
const out = [];
|
|
3348
3498
|
for (const p of pathList) {
|
|
3349
|
-
if (
|
|
3499
|
+
if (isRecord3(p) && typeof p["x"] === "number" && typeof p["y"] === "number") {
|
|
3350
3500
|
out.push({ x: p["x"], y: p["y"] });
|
|
3351
3501
|
}
|
|
3352
3502
|
}
|
|
@@ -3381,7 +3531,7 @@ function asEntry(entry) {
|
|
|
3381
3531
|
}
|
|
3382
3532
|
const tuple = entry;
|
|
3383
3533
|
const data = tuple[1];
|
|
3384
|
-
if (!
|
|
3534
|
+
if (!isRecord3(data)) {
|
|
3385
3535
|
return null;
|
|
3386
3536
|
}
|
|
3387
3537
|
return { id: tuple[0], data };
|
|
@@ -3459,7 +3609,7 @@ function parseContours(list) {
|
|
|
3459
3609
|
return out;
|
|
3460
3610
|
}
|
|
3461
3611
|
function parseBoundary(raw) {
|
|
3462
|
-
if (!
|
|
3612
|
+
if (!isRecord3(raw)) {
|
|
3463
3613
|
return null;
|
|
3464
3614
|
}
|
|
3465
3615
|
const { x1, y1, x2, y2 } = raw;
|
|
@@ -3470,7 +3620,7 @@ function parseBoundary(raw) {
|
|
|
3470
3620
|
}
|
|
3471
3621
|
function parseMowerMap(mapJsonStr) {
|
|
3472
3622
|
const data = JSON.parse(mapJsonStr);
|
|
3473
|
-
if (!
|
|
3623
|
+
if (!isRecord3(data)) {
|
|
3474
3624
|
throw new Error("Map JSON is not an object");
|
|
3475
3625
|
}
|
|
3476
3626
|
const mapIndex = asNumber(data["mapIndex"], 0);
|
|
@@ -4640,6 +4790,8 @@ function createClientDumper(client, options) {
|
|
|
4640
4790
|
}
|
|
4641
4791
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4642
4792
|
0 && (module.exports = {
|
|
4793
|
+
AI_FEATURE_BIT,
|
|
4794
|
+
AI_FEATURE_JSON_KEY,
|
|
4643
4795
|
BaseDevice,
|
|
4644
4796
|
ChargingStatus,
|
|
4645
4797
|
CleaningMode,
|
|
@@ -4669,6 +4821,8 @@ function createClientDumper(client, options) {
|
|
|
4669
4821
|
WaterVolume,
|
|
4670
4822
|
createClientDumper,
|
|
4671
4823
|
createDumper,
|
|
4824
|
+
decodeAiFeature,
|
|
4825
|
+
encodeAiFeatureWrite,
|
|
4672
4826
|
getMowerCapabilities,
|
|
4673
4827
|
getVacuumCapabilities,
|
|
4674
4828
|
renderMowerSvg,
|