@apocaliss92/nodedreame 1.5.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 +150 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -1
- package/dist/index.d.ts +76 -1
- package/dist/index.js +146 -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));
|
|
@@ -2980,6 +3112,7 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2980
3112
|
VACUUM_PROP.WATER_VOLUME,
|
|
2981
3113
|
VACUUM_PROP.CLEAN_MODE_SETTING,
|
|
2982
3114
|
VACUUM_PROP.TASK_PROGRESS_PCT,
|
|
3115
|
+
VACUUM_PROP.AI_DETECTION,
|
|
2983
3116
|
BATTERY_PROP.LEVEL,
|
|
2984
3117
|
BATTERY_PROP.CHARGING_STATUS,
|
|
2985
3118
|
CONSUMABLE_PROP.MAIN_BRUSH_LEFT,
|
|
@@ -3152,7 +3285,7 @@ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
|
|
|
3152
3285
|
})(MowerFault || {});
|
|
3153
3286
|
|
|
3154
3287
|
// src/models/mower/decode.ts
|
|
3155
|
-
function
|
|
3288
|
+
function isRecord2(v) {
|
|
3156
3289
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3157
3290
|
}
|
|
3158
3291
|
function numArrayOrNull(v) {
|
|
@@ -3169,12 +3302,12 @@ function numArrayOrNull(v) {
|
|
|
3169
3302
|
return out;
|
|
3170
3303
|
}
|
|
3171
3304
|
function parseTaskDescriptor(value) {
|
|
3172
|
-
if (!
|
|
3305
|
+
if (!isRecord2(value)) {
|
|
3173
3306
|
return null;
|
|
3174
3307
|
}
|
|
3175
3308
|
const t = value["t"];
|
|
3176
3309
|
const d = value["d"];
|
|
3177
|
-
if (typeof t !== "string" || !
|
|
3310
|
+
if (typeof t !== "string" || !isRecord2(d)) {
|
|
3178
3311
|
return null;
|
|
3179
3312
|
}
|
|
3180
3313
|
const exe = d["exe"];
|
|
@@ -3203,7 +3336,7 @@ function controlActionFor(code) {
|
|
|
3203
3336
|
return controlLookup(code);
|
|
3204
3337
|
}
|
|
3205
3338
|
function parseControlStatus(value) {
|
|
3206
|
-
if (!
|
|
3339
|
+
if (!isRecord2(value)) {
|
|
3207
3340
|
return null;
|
|
3208
3341
|
}
|
|
3209
3342
|
const status = value["status"];
|
|
@@ -3313,7 +3446,7 @@ var MowerCapabilityResolver = class {
|
|
|
3313
3446
|
// src/models/mower/map/parser.ts
|
|
3314
3447
|
var PATH_SENTINEL_X = 32767;
|
|
3315
3448
|
var PATH_SENTINEL_Y = -32768;
|
|
3316
|
-
function
|
|
3449
|
+
function isRecord3(v) {
|
|
3317
3450
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3318
3451
|
}
|
|
3319
3452
|
function asNumber(v, fallback) {
|
|
@@ -3351,7 +3484,7 @@ function parseSplitPos(info) {
|
|
|
3351
3484
|
return 0;
|
|
3352
3485
|
}
|
|
3353
3486
|
function parsePolygonList(dataMap) {
|
|
3354
|
-
if (!
|
|
3487
|
+
if (!isRecord3(dataMap) || dataMap["dataType"] !== "Map") {
|
|
3355
3488
|
return [];
|
|
3356
3489
|
}
|
|
3357
3490
|
const value = dataMap["value"];
|
|
@@ -3363,7 +3496,7 @@ function extractPathCoords(pathList) {
|
|
|
3363
3496
|
}
|
|
3364
3497
|
const out = [];
|
|
3365
3498
|
for (const p of pathList) {
|
|
3366
|
-
if (
|
|
3499
|
+
if (isRecord3(p) && typeof p["x"] === "number" && typeof p["y"] === "number") {
|
|
3367
3500
|
out.push({ x: p["x"], y: p["y"] });
|
|
3368
3501
|
}
|
|
3369
3502
|
}
|
|
@@ -3398,7 +3531,7 @@ function asEntry(entry) {
|
|
|
3398
3531
|
}
|
|
3399
3532
|
const tuple = entry;
|
|
3400
3533
|
const data = tuple[1];
|
|
3401
|
-
if (!
|
|
3534
|
+
if (!isRecord3(data)) {
|
|
3402
3535
|
return null;
|
|
3403
3536
|
}
|
|
3404
3537
|
return { id: tuple[0], data };
|
|
@@ -3476,7 +3609,7 @@ function parseContours(list) {
|
|
|
3476
3609
|
return out;
|
|
3477
3610
|
}
|
|
3478
3611
|
function parseBoundary(raw) {
|
|
3479
|
-
if (!
|
|
3612
|
+
if (!isRecord3(raw)) {
|
|
3480
3613
|
return null;
|
|
3481
3614
|
}
|
|
3482
3615
|
const { x1, y1, x2, y2 } = raw;
|
|
@@ -3487,7 +3620,7 @@ function parseBoundary(raw) {
|
|
|
3487
3620
|
}
|
|
3488
3621
|
function parseMowerMap(mapJsonStr) {
|
|
3489
3622
|
const data = JSON.parse(mapJsonStr);
|
|
3490
|
-
if (!
|
|
3623
|
+
if (!isRecord3(data)) {
|
|
3491
3624
|
throw new Error("Map JSON is not an object");
|
|
3492
3625
|
}
|
|
3493
3626
|
const mapIndex = asNumber(data["mapIndex"], 0);
|
|
@@ -4657,6 +4790,8 @@ function createClientDumper(client, options) {
|
|
|
4657
4790
|
}
|
|
4658
4791
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4659
4792
|
0 && (module.exports = {
|
|
4793
|
+
AI_FEATURE_BIT,
|
|
4794
|
+
AI_FEATURE_JSON_KEY,
|
|
4660
4795
|
BaseDevice,
|
|
4661
4796
|
ChargingStatus,
|
|
4662
4797
|
CleaningMode,
|
|
@@ -4686,6 +4821,8 @@ function createClientDumper(client, options) {
|
|
|
4686
4821
|
WaterVolume,
|
|
4687
4822
|
createClientDumper,
|
|
4688
4823
|
createDumper,
|
|
4824
|
+
decodeAiFeature,
|
|
4825
|
+
encodeAiFeatureWrite,
|
|
4689
4826
|
getMowerCapabilities,
|
|
4690
4827
|
getVacuumCapabilities,
|
|
4691
4828
|
renderMowerSvg,
|