@apocaliss92/nodedreame 1.7.2 → 1.8.1
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 +209 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -1
- package/dist/index.d.ts +90 -1
- package/dist/index.js +203 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AI_FEATURE_BIT: () => AI_FEATURE_BIT,
|
|
34
34
|
AI_FEATURE_JSON_KEY: () => AI_FEATURE_JSON_KEY,
|
|
35
|
+
AUTO_SWITCH_JSON_KEY: () => AUTO_SWITCH_JSON_KEY,
|
|
35
36
|
BaseDevice: () => BaseDevice,
|
|
36
37
|
ChargingStatus: () => ChargingStatus,
|
|
37
38
|
CleaningMode: () => CleaningMode,
|
|
@@ -64,19 +65,23 @@ __export(index_exports, {
|
|
|
64
65
|
createClientDumper: () => createClientDumper,
|
|
65
66
|
createDumper: () => createDumper,
|
|
66
67
|
decodeAiFeature: () => decodeAiFeature,
|
|
68
|
+
decodeAutoSwitch: () => decodeAutoSwitch,
|
|
69
|
+
decodeAutoSwitchAll: () => decodeAutoSwitchAll,
|
|
67
70
|
encodeAiFeatureWrite: () => encodeAiFeatureWrite,
|
|
71
|
+
encodeAutoSwitchWrite: () => encodeAutoSwitchWrite,
|
|
68
72
|
getMowerCapabilities: () => getMowerCapabilities,
|
|
69
73
|
getVacuumCapabilities: () => getVacuumCapabilities,
|
|
70
74
|
isDreameConsumableKey: () => isDreameConsumableKey,
|
|
71
75
|
renderMowerSvg: () => renderMowerSvg,
|
|
72
76
|
renderVacuumPng: () => renderVacuumPng,
|
|
73
|
-
resolveCapabilities: () => resolveCapabilities
|
|
77
|
+
resolveCapabilities: () => resolveCapabilities,
|
|
78
|
+
supportedAutoSwitchKeys: () => supportedAutoSwitchKeys
|
|
74
79
|
});
|
|
75
80
|
module.exports = __toCommonJS(index_exports);
|
|
76
81
|
|
|
77
82
|
// src/support/version.ts
|
|
78
83
|
var LIBRARY_NAME = "nodedreame";
|
|
79
|
-
var LIBRARY_VERSION = "1.
|
|
84
|
+
var LIBRARY_VERSION = "1.8.1";
|
|
80
85
|
|
|
81
86
|
// src/transport/errors.ts
|
|
82
87
|
var DreameError = class extends Error {
|
|
@@ -1233,6 +1238,12 @@ var VACUUM_PROP = {
|
|
|
1233
1238
|
* via `ai-detection.ts` ({@link decodeAiFeature} / {@link encodeAiFeatureWrite}).
|
|
1234
1239
|
*/
|
|
1235
1240
|
AI_DETECTION: { siid: 4, piid: 22 },
|
|
1241
|
+
/**
|
|
1242
|
+
* Auto-switch settings bundle (Tasshack types.py — `AUTO_SWITCH_SETTINGS`,
|
|
1243
|
+
* siid 4 piid 50). A JSON string packing every secondary toggle/setting as
|
|
1244
|
+
* `[{"k":<key>,"v":<int>}, …]`. Decode/encode per-setting via `auto-switch.ts`.
|
|
1245
|
+
*/
|
|
1246
|
+
AUTO_SWITCH_SETTINGS: { siid: 4, piid: 50 },
|
|
1236
1247
|
/** VERIFIED r2532a 2026-05-03 — task progress percentage 0..100. */
|
|
1237
1248
|
TASK_PROGRESS_PCT: { siid: 4, piid: 63 },
|
|
1238
1249
|
/** VERIFIED r2532a — mop-drying progress (minutes ticking during MopDrying). */
|
|
@@ -1646,6 +1657,105 @@ function encodeAiFeatureWrite(raw, feature, value) {
|
|
|
1646
1657
|
);
|
|
1647
1658
|
}
|
|
1648
1659
|
|
|
1660
|
+
// src/models/vacuum/auto-switch.ts
|
|
1661
|
+
var AUTO_SWITCH_JSON_KEY = {
|
|
1662
|
+
collisionAvoidance: "LessColl",
|
|
1663
|
+
fillLight: "FillinLight",
|
|
1664
|
+
autoDrying: "AutoDry",
|
|
1665
|
+
stainAvoidance: "StainIdentify",
|
|
1666
|
+
moppingType: "CleanType",
|
|
1667
|
+
cleanGenius: "SmartHost",
|
|
1668
|
+
widerCornerCoverage: "MeticulousTwist",
|
|
1669
|
+
floorDirectionCleaning: "MaterialDirectionClean",
|
|
1670
|
+
petFocusedCleaning: "PetPartClean",
|
|
1671
|
+
autoRecleaning: "SmartAutoMop",
|
|
1672
|
+
autoRewashing: "SmartAutoWash",
|
|
1673
|
+
mopPadSwing: "MopScalable",
|
|
1674
|
+
autoCharging: "SmartCharge",
|
|
1675
|
+
humanFollow: "MonitorHumanFollow",
|
|
1676
|
+
maxSuctionPower: "SuctionMax",
|
|
1677
|
+
smartDrying: "SmartDrying",
|
|
1678
|
+
drainageConfirmResult: "FluctuationConfirmResult",
|
|
1679
|
+
drainageTestResult: "FluctuationTestResult",
|
|
1680
|
+
hotWashing: "HotWash",
|
|
1681
|
+
uvSterilization: "UVLight",
|
|
1682
|
+
cleaningRoute: "CleanRoute",
|
|
1683
|
+
customMoppingMode: "MopEffectSwitch",
|
|
1684
|
+
moppingMode: "MopEffectState",
|
|
1685
|
+
selfCleanFrequency: "BackWashType",
|
|
1686
|
+
intensiveCarpetCleaning: "CarpetFineClean",
|
|
1687
|
+
gapCleaningExtension: "LacuneMopScalable",
|
|
1688
|
+
moppingUnderFurnitures: "MopScalable2",
|
|
1689
|
+
ultraCleanMode: "SuperWash",
|
|
1690
|
+
streamingVoicePrompt: "MonitorPromptLevel",
|
|
1691
|
+
mopExtend: "MopExtrSwitch",
|
|
1692
|
+
mopExtendFrequency: "ExtrFreq",
|
|
1693
|
+
sideReach: "SbrushExtrSwitch",
|
|
1694
|
+
intelligentStainCleaning: "HeavyStainSmart"
|
|
1695
|
+
};
|
|
1696
|
+
var JSON_KEY_TO_CANONICAL = Object.fromEntries(
|
|
1697
|
+
Object.entries(AUTO_SWITCH_JSON_KEY).map(([k, v]) => [v, k])
|
|
1698
|
+
);
|
|
1699
|
+
function coerceInt(v) {
|
|
1700
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : null;
|
|
1701
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
1702
|
+
const n = Number(v);
|
|
1703
|
+
return Number.isFinite(n) ? n : null;
|
|
1704
|
+
}
|
|
1705
|
+
return null;
|
|
1706
|
+
}
|
|
1707
|
+
function parseAutoSwitchMap(raw) {
|
|
1708
|
+
if (typeof raw !== "string" || raw.length <= 2) return null;
|
|
1709
|
+
let parsed;
|
|
1710
|
+
try {
|
|
1711
|
+
parsed = JSON.parse(raw);
|
|
1712
|
+
} catch {
|
|
1713
|
+
return null;
|
|
1714
|
+
}
|
|
1715
|
+
const out = /* @__PURE__ */ new Map();
|
|
1716
|
+
const add = (entry) => {
|
|
1717
|
+
if (entry === null || typeof entry !== "object") return;
|
|
1718
|
+
const rec = entry;
|
|
1719
|
+
if (typeof rec["k"] !== "string") return;
|
|
1720
|
+
const value = coerceInt(rec["v"]);
|
|
1721
|
+
if (value !== null) out.set(rec["k"], value);
|
|
1722
|
+
};
|
|
1723
|
+
if (Array.isArray(parsed)) {
|
|
1724
|
+
for (const entry of parsed) add(entry);
|
|
1725
|
+
} else {
|
|
1726
|
+
add(parsed);
|
|
1727
|
+
}
|
|
1728
|
+
return out.size > 0 ? out : null;
|
|
1729
|
+
}
|
|
1730
|
+
function decodeAutoSwitch(raw, key2) {
|
|
1731
|
+
const map = parseAutoSwitchMap(raw);
|
|
1732
|
+
if (map === null) return null;
|
|
1733
|
+
const jsonKey = AUTO_SWITCH_JSON_KEY[key2];
|
|
1734
|
+
return map.has(jsonKey) ? map.get(jsonKey) ?? null : null;
|
|
1735
|
+
}
|
|
1736
|
+
function supportedAutoSwitchKeys(raw) {
|
|
1737
|
+
const map = parseAutoSwitchMap(raw);
|
|
1738
|
+
if (map === null) return [];
|
|
1739
|
+
const present = [];
|
|
1740
|
+
for (const key2 of Object.keys(AUTO_SWITCH_JSON_KEY)) {
|
|
1741
|
+
if (map.has(AUTO_SWITCH_JSON_KEY[key2])) present.push(key2);
|
|
1742
|
+
}
|
|
1743
|
+
return present;
|
|
1744
|
+
}
|
|
1745
|
+
function decodeAutoSwitchAll(raw) {
|
|
1746
|
+
const map = parseAutoSwitchMap(raw);
|
|
1747
|
+
if (map === null) return {};
|
|
1748
|
+
const out = {};
|
|
1749
|
+
for (const [jsonKey, value] of map) {
|
|
1750
|
+
const canonical = JSON_KEY_TO_CANONICAL[jsonKey];
|
|
1751
|
+
if (canonical !== void 0) out[canonical] = value;
|
|
1752
|
+
}
|
|
1753
|
+
return out;
|
|
1754
|
+
}
|
|
1755
|
+
function encodeAutoSwitchWrite(key2, value) {
|
|
1756
|
+
return JSON.stringify({ k: AUTO_SWITCH_JSON_KEY[key2], v: Math.trunc(value) });
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1649
1759
|
// src/models/vacuum/consumables.ts
|
|
1650
1760
|
var VACUUM_CONSUMABLES = [
|
|
1651
1761
|
{ key: "main-brush", label: "Main Brush", life: { siid: 9, piid: 2 }, reset: { siid: 9, aiid: 1 } },
|
|
@@ -1853,11 +1963,43 @@ function unwrapEnvelope(value, opts = {}) {
|
|
|
1853
1963
|
}
|
|
1854
1964
|
bytes = aesCbcDecrypt(bytes, key2, opts.iv);
|
|
1855
1965
|
}
|
|
1966
|
+
let inflated;
|
|
1856
1967
|
try {
|
|
1857
|
-
|
|
1968
|
+
inflated = zlib.inflateSync(bytes);
|
|
1858
1969
|
} catch (err) {
|
|
1859
1970
|
throw new MapDecodeError("envelope: zlib inflate failed", { cause: err });
|
|
1860
1971
|
}
|
|
1972
|
+
let guard = 0;
|
|
1973
|
+
while (guard < MAX_EXTRA_WRAP_LAYERS && looksLikeBase64Zlib(inflated)) {
|
|
1974
|
+
const text = inflated.toString("latin1").replace(/-/g, "+").replace(/_/g, "/");
|
|
1975
|
+
try {
|
|
1976
|
+
inflated = zlib.inflateSync(Buffer.from(text, "base64"));
|
|
1977
|
+
} catch (err) {
|
|
1978
|
+
throw new MapDecodeError("envelope: nested zlib inflate failed", { cause: err });
|
|
1979
|
+
}
|
|
1980
|
+
guard += 1;
|
|
1981
|
+
}
|
|
1982
|
+
return inflated;
|
|
1983
|
+
}
|
|
1984
|
+
var MAX_EXTRA_WRAP_LAYERS = 4;
|
|
1985
|
+
function looksLikeBase64Zlib(buf) {
|
|
1986
|
+
if (buf.length < 8) return false;
|
|
1987
|
+
for (const b of buf) {
|
|
1988
|
+
const isB64 = b >= 65 && b <= 90 || // A-Z
|
|
1989
|
+
b >= 97 && b <= 122 || // a-z
|
|
1990
|
+
b >= 48 && b <= 57 || // 0-9
|
|
1991
|
+
b === 43 || b === 47 || // + /
|
|
1992
|
+
b === 45 || b === 95 || // - _ (url-safe)
|
|
1993
|
+
b === 61;
|
|
1994
|
+
if (!isB64) return false;
|
|
1995
|
+
}
|
|
1996
|
+
try {
|
|
1997
|
+
const prefix = buf.subarray(0, 16).toString("latin1").replace(/-/g, "+").replace(/_/g, "/");
|
|
1998
|
+
const decoded = Buffer.from(prefix, "base64");
|
|
1999
|
+
return decoded.length >= 2 && decoded[0] === 120;
|
|
2000
|
+
} catch {
|
|
2001
|
+
return false;
|
|
2002
|
+
}
|
|
1861
2003
|
}
|
|
1862
2004
|
function aesCbcDecrypt(cipher, rawKey, iv) {
|
|
1863
2005
|
const keyBytes = Buffer.from(
|
|
@@ -2493,7 +2635,7 @@ function decodeCleanedAreaPixels(pixels, width, height) {
|
|
|
2493
2635
|
|
|
2494
2636
|
// src/models/vacuum/map/decode.ts
|
|
2495
2637
|
function decodeVacuumMap(input, opts = {}) {
|
|
2496
|
-
const inflated = typeof input === "string" ? unwrapEnvelope(input, opts) : input;
|
|
2638
|
+
const inflated = typeof input === "string" ? unwrapEnvelope(input, opts) : looksLikeBase64Zlib(input) ? unwrapEnvelope(input.toString("latin1"), opts) : input;
|
|
2497
2639
|
const { header, tail } = parseFrame(inflated);
|
|
2498
2640
|
const dimensions = mergeDimensions(header, tail);
|
|
2499
2641
|
const robot = pose(header.robotX, header.robotY, header.robotA, tail.nr === true);
|
|
@@ -2962,6 +3104,63 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2962
3104
|
const next = encodeAiFeatureWrite(this.aiDetectionRaw, feature, value);
|
|
2963
3105
|
return this.setProperty({ ...VACUUM_PROP.AI_DETECTION, value: next });
|
|
2964
3106
|
}
|
|
3107
|
+
// -- auto-switch settings -----------------------------------------------
|
|
3108
|
+
/**
|
|
3109
|
+
* The raw `AUTO_SWITCH_SETTINGS` value (siid 4 piid 50) — a JSON string packing
|
|
3110
|
+
* every secondary toggle/setting — or `null` when not yet observed. Prefer the
|
|
3111
|
+
* decoded {@link autoSwitchProperty} / {@link supportedAutoSwitchKeys} surface.
|
|
3112
|
+
*/
|
|
3113
|
+
get autoSwitchRaw() {
|
|
3114
|
+
const v = this.getProperty(
|
|
3115
|
+
VACUUM_PROP.AUTO_SWITCH_SETTINGS.siid,
|
|
3116
|
+
VACUUM_PROP.AUTO_SWITCH_SETTINGS.piid
|
|
3117
|
+
)?.value;
|
|
3118
|
+
return typeof v === "string" ? v : null;
|
|
3119
|
+
}
|
|
3120
|
+
/** Whether this model reports the packed auto-switch bundle at all. */
|
|
3121
|
+
get hasAutoSwitchSettings() {
|
|
3122
|
+
return this.autoSwitchRaw !== null;
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* The auto-switch settings this model reports, decoded from the packed value.
|
|
3126
|
+
* Presence-driven (a key appears iff the payload carries it) — mirrors
|
|
3127
|
+
* Tasshack `auto_switch_data`.
|
|
3128
|
+
*/
|
|
3129
|
+
get supportedAutoSwitchKeys() {
|
|
3130
|
+
return supportedAutoSwitchKeys(this.autoSwitchRaw);
|
|
3131
|
+
}
|
|
3132
|
+
/**
|
|
3133
|
+
* Read ONE auto-switch setting's int value, decoded from the packed
|
|
3134
|
+
* `AUTO_SWITCH_SETTINGS` payload. `null` when the value is unobserved or the
|
|
3135
|
+
* key is not present. The value is an opaque int (boolean 0/1, a small enum, or
|
|
3136
|
+
* a `-1`/`-n` "not applicable" sentinel) — the caller interprets it.
|
|
3137
|
+
*/
|
|
3138
|
+
autoSwitchProperty(key2) {
|
|
3139
|
+
return decodeAutoSwitch(this.autoSwitchRaw, key2);
|
|
3140
|
+
}
|
|
3141
|
+
/**
|
|
3142
|
+
* Seed {@link autoSwitchRaw} from the CLOUD SHADOW (the last value the robot
|
|
3143
|
+
* pushed for `AUTO_SWITCH_SETTINGS`, siid 4 piid 50) WITHOUT waking it. Like the
|
|
3144
|
+
* AI bundle these are STATIC settings the robot rarely re-pushes over MQTT, so a
|
|
3145
|
+
* fresh connect to an idle robot has no value cached — call this on activate to
|
|
3146
|
+
* surface the current settings. Returns the resolved raw value (`null` when the
|
|
3147
|
+
* shadow carries none yet).
|
|
3148
|
+
*/
|
|
3149
|
+
async refreshAutoSwitch() {
|
|
3150
|
+
await this.refreshCachedProperties([VACUUM_PROP.AUTO_SWITCH_SETTINGS]);
|
|
3151
|
+
return this.autoSwitchRaw;
|
|
3152
|
+
}
|
|
3153
|
+
/**
|
|
3154
|
+
* Set ONE auto-switch setting. Mirrors Tasshack `set_auto_switch_property`: a
|
|
3155
|
+
* single-key `{"k":<key>,"v":<int>}` write the device merges server-side (the
|
|
3156
|
+
* other settings are preserved without a read-modify-write). Rejects when the
|
|
3157
|
+
* model does not report the bundle.
|
|
3158
|
+
*/
|
|
3159
|
+
async setAutoSwitchProperty(key2, value) {
|
|
3160
|
+
this.#requireCap(this.hasAutoSwitchSettings, "setAutoSwitchProperty", "auto-switch settings");
|
|
3161
|
+
const next = encodeAutoSwitchWrite(key2, value);
|
|
3162
|
+
return this.setProperty({ ...VACUUM_PROP.AUTO_SWITCH_SETTINGS, value: next });
|
|
3163
|
+
}
|
|
2965
3164
|
// -- command helpers ----------------------------------------------------
|
|
2966
3165
|
#resolveCleanOpts(opts) {
|
|
2967
3166
|
const repeats = Math.max(1, Math.trunc(opts.repeats ?? 1));
|
|
@@ -4877,6 +5076,7 @@ function createClientDumper(client, options) {
|
|
|
4877
5076
|
0 && (module.exports = {
|
|
4878
5077
|
AI_FEATURE_BIT,
|
|
4879
5078
|
AI_FEATURE_JSON_KEY,
|
|
5079
|
+
AUTO_SWITCH_JSON_KEY,
|
|
4880
5080
|
BaseDevice,
|
|
4881
5081
|
ChargingStatus,
|
|
4882
5082
|
CleaningMode,
|
|
@@ -4909,12 +5109,16 @@ function createClientDumper(client, options) {
|
|
|
4909
5109
|
createClientDumper,
|
|
4910
5110
|
createDumper,
|
|
4911
5111
|
decodeAiFeature,
|
|
5112
|
+
decodeAutoSwitch,
|
|
5113
|
+
decodeAutoSwitchAll,
|
|
4912
5114
|
encodeAiFeatureWrite,
|
|
5115
|
+
encodeAutoSwitchWrite,
|
|
4913
5116
|
getMowerCapabilities,
|
|
4914
5117
|
getVacuumCapabilities,
|
|
4915
5118
|
isDreameConsumableKey,
|
|
4916
5119
|
renderMowerSvg,
|
|
4917
5120
|
renderVacuumPng,
|
|
4918
|
-
resolveCapabilities
|
|
5121
|
+
resolveCapabilities,
|
|
5122
|
+
supportedAutoSwitchKeys
|
|
4919
5123
|
});
|
|
4920
5124
|
//# sourceMappingURL=index.cjs.map
|