@apocaliss92/nodedreame 1.11.0 → 1.11.2
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 +134 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +132 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(index_exports, {
|
|
|
44
44
|
DreameTransportError: () => DreameTransportError,
|
|
45
45
|
LIBRARY_NAME: () => LIBRARY_NAME,
|
|
46
46
|
MOWER_MODEL_CAPABILITIES: () => MODEL_CAPABILITIES2,
|
|
47
|
+
MOWER_TASK_SUBSTATES: () => MOWER_TASK_SUBSTATES,
|
|
47
48
|
MiotError: () => MiotError,
|
|
48
49
|
MiotState: () => MiotState,
|
|
49
50
|
MowerCapabilityResolver: () => MowerCapabilityResolver,
|
|
@@ -75,6 +76,7 @@ __export(index_exports, {
|
|
|
75
76
|
isDreameConsumableKey: () => isDreameConsumableKey,
|
|
76
77
|
mowerConsumableIndex: () => mowerConsumableIndex,
|
|
77
78
|
parseMowerConsumables: () => parseMowerConsumables,
|
|
79
|
+
parseMowerHeartbeat: () => parseMowerHeartbeat,
|
|
78
80
|
renderMowerSvg: () => renderMowerSvg,
|
|
79
81
|
renderVacuumPng: () => renderVacuumPng,
|
|
80
82
|
resolveCapabilities: () => resolveCapabilities,
|
|
@@ -84,7 +86,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
84
86
|
|
|
85
87
|
// src/support/version.ts
|
|
86
88
|
var LIBRARY_NAME = "nodedreame";
|
|
87
|
-
var LIBRARY_VERSION = "1.11.
|
|
89
|
+
var LIBRARY_VERSION = "1.11.2";
|
|
88
90
|
|
|
89
91
|
// src/transport/errors.ts
|
|
90
92
|
var DreameError = class extends Error {
|
|
@@ -700,6 +702,7 @@ var DreamePush = class extends TypedEmitter {
|
|
|
700
702
|
#region;
|
|
701
703
|
#connect;
|
|
702
704
|
#backoffMs;
|
|
705
|
+
#keepaliveSeconds;
|
|
703
706
|
#rejectUnauthorized;
|
|
704
707
|
#topic;
|
|
705
708
|
#client = null;
|
|
@@ -720,6 +723,7 @@ var DreamePush = class extends TypedEmitter {
|
|
|
720
723
|
this.#region = input.region;
|
|
721
724
|
this.#connect = input.connect ?? defaultConnect;
|
|
722
725
|
this.#backoffMs = input.reconnectBackoffMs ?? 5e3;
|
|
726
|
+
this.#keepaliveSeconds = input.keepaliveSeconds ?? 60;
|
|
723
727
|
this.#rejectUnauthorized = input.rejectUnauthorized ?? false;
|
|
724
728
|
this.#topic = buildStatusTopic(this.#device, this.#session.uid, this.#region);
|
|
725
729
|
this.#onClose = () => {
|
|
@@ -777,6 +781,7 @@ var DreamePush = class extends TypedEmitter {
|
|
|
777
781
|
reconnectPeriod: 0,
|
|
778
782
|
// we drive reconnect ourselves
|
|
779
783
|
connectTimeout: 15e3,
|
|
784
|
+
keepalive: this.#keepaliveSeconds,
|
|
780
785
|
rejectUnauthorized: this.#rejectUnauthorized,
|
|
781
786
|
clean: true
|
|
782
787
|
});
|
|
@@ -784,25 +789,37 @@ var DreamePush = class extends TypedEmitter {
|
|
|
784
789
|
client.on("message", (_topic, payload) => this.#handleMessage(payload));
|
|
785
790
|
client.on("error", (err) => this.emit("error", err));
|
|
786
791
|
client.on("close", this.#onClose);
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
792
|
+
try {
|
|
793
|
+
await new Promise((resolve, reject) => {
|
|
794
|
+
const onConnect = () => {
|
|
795
|
+
client.removeListener("error", onError);
|
|
796
|
+
client.subscribe(this.#topic, { qos: 0 }, (err) => {
|
|
797
|
+
if (err) {
|
|
798
|
+
reject(new DreameTransportError(`mqtt subscribe failed: ${err.message}`, err));
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
this.emit("connect");
|
|
802
|
+
resolve();
|
|
803
|
+
});
|
|
804
|
+
};
|
|
805
|
+
const onError = (err) => {
|
|
806
|
+
client.removeListener("connect", onConnect);
|
|
807
|
+
reject(new DreameTransportError(`mqtt connect failed: ${err.message}`, err));
|
|
808
|
+
};
|
|
809
|
+
client.once("connect", onConnect);
|
|
810
|
+
client.once("error", onError);
|
|
811
|
+
});
|
|
812
|
+
} catch (err) {
|
|
813
|
+
client.removeListener("close", this.#onClose);
|
|
814
|
+
try {
|
|
815
|
+
client.end(true, {}, () => void 0);
|
|
816
|
+
} catch {
|
|
817
|
+
}
|
|
818
|
+
if (this.#client === client) {
|
|
819
|
+
this.#client = null;
|
|
820
|
+
}
|
|
821
|
+
throw err;
|
|
822
|
+
}
|
|
806
823
|
}
|
|
807
824
|
#scheduleReconnect() {
|
|
808
825
|
if (this.#closed || this.#reconnectTimer) {
|
|
@@ -1644,7 +1661,9 @@ function encodeAiFeatureWrite(raw, feature, value) {
|
|
|
1644
1661
|
if (typeof raw === "number") {
|
|
1645
1662
|
const bit = AI_FEATURE_BIT[feature];
|
|
1646
1663
|
if (bit === void 0) {
|
|
1647
|
-
throw new Error(
|
|
1664
|
+
throw new Error(
|
|
1665
|
+
`encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`
|
|
1666
|
+
);
|
|
1648
1667
|
}
|
|
1649
1668
|
return value ? raw | bit : raw & ~bit;
|
|
1650
1669
|
}
|
|
@@ -1761,18 +1780,53 @@ function encodeAutoSwitchWrite(key2, value) {
|
|
|
1761
1780
|
|
|
1762
1781
|
// src/models/vacuum/consumables.ts
|
|
1763
1782
|
var VACUUM_CONSUMABLES = [
|
|
1764
|
-
{
|
|
1765
|
-
|
|
1783
|
+
{
|
|
1784
|
+
key: "main-brush",
|
|
1785
|
+
label: "Main Brush",
|
|
1786
|
+
life: { siid: 9, piid: 2 },
|
|
1787
|
+
reset: { siid: 9, aiid: 1 }
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
key: "side-brush",
|
|
1791
|
+
label: "Side Brush",
|
|
1792
|
+
life: { siid: 10, piid: 2 },
|
|
1793
|
+
reset: { siid: 10, aiid: 1 }
|
|
1794
|
+
},
|
|
1766
1795
|
{ key: "filter", label: "Filter", life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },
|
|
1767
1796
|
{ key: "sensor", label: "Sensor", life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },
|
|
1768
|
-
{
|
|
1797
|
+
{
|
|
1798
|
+
key: "tank-filter",
|
|
1799
|
+
label: "Tank Filter",
|
|
1800
|
+
life: { siid: 17, piid: 1 },
|
|
1801
|
+
reset: { siid: 17, aiid: 1 }
|
|
1802
|
+
},
|
|
1769
1803
|
{ key: "mop-pad", label: "Mop Pad", life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },
|
|
1770
|
-
{
|
|
1771
|
-
|
|
1804
|
+
{
|
|
1805
|
+
key: "silver-ion",
|
|
1806
|
+
label: "Silver-ion",
|
|
1807
|
+
life: { siid: 19, piid: 2 },
|
|
1808
|
+
reset: { siid: 19, aiid: 1 }
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
key: "detergent",
|
|
1812
|
+
label: "Detergent",
|
|
1813
|
+
life: { siid: 20, piid: 1 },
|
|
1814
|
+
reset: { siid: 20, aiid: 1 }
|
|
1815
|
+
},
|
|
1772
1816
|
{ key: "squeegee", label: "Squeegee", life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },
|
|
1773
|
-
{
|
|
1817
|
+
{
|
|
1818
|
+
key: "deodorizer",
|
|
1819
|
+
label: "Deodorizer",
|
|
1820
|
+
life: { siid: 29, piid: 2 },
|
|
1821
|
+
reset: { siid: 29, aiid: 1 }
|
|
1822
|
+
},
|
|
1774
1823
|
{ key: "wheel", label: "Wheel", life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },
|
|
1775
|
-
{
|
|
1824
|
+
{
|
|
1825
|
+
key: "scale-inhibitor",
|
|
1826
|
+
label: "Scale Inhibitor",
|
|
1827
|
+
life: { siid: 31, piid: 2 },
|
|
1828
|
+
reset: { siid: 31, aiid: 1 }
|
|
1829
|
+
},
|
|
1776
1830
|
{ key: "dust-bag", label: "Dust Bag", life: { siid: 27, piid: 17 }, reset: null }
|
|
1777
1831
|
];
|
|
1778
1832
|
function consumableSpec(key2) {
|
|
@@ -3887,6 +3941,9 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
3887
3941
|
|
|
3888
3942
|
// src/models/mower/properties.ts
|
|
3889
3943
|
var MOWER_PROP = {
|
|
3944
|
+
/** VERIFIED-by-donor — 20-byte heartbeat (sentinel 0xCE at [0]/[19]); byte 11
|
|
3945
|
+
* battery, byte 12 main-state, byte 13 task sub-state. See parseHeartbeat. */
|
|
3946
|
+
HEARTBEAT: { siid: 1, piid: 1 },
|
|
3890
3947
|
/** VERIFIED-by-donor — pose + coverage telemetry (binary). P4 uses progress % only. */
|
|
3891
3948
|
POSE_COVERAGE: { siid: 1, piid: 4 },
|
|
3892
3949
|
/** VERIFIED-by-donor — MowerStatus (DeviceStatus). */
|
|
@@ -4236,6 +4293,39 @@ function parseMowerConsumables(result) {
|
|
|
4236
4293
|
};
|
|
4237
4294
|
});
|
|
4238
4295
|
}
|
|
4296
|
+
var MOWER_TASK_SUBSTATES = [
|
|
4297
|
+
"idle",
|
|
4298
|
+
"starting",
|
|
4299
|
+
"mowing",
|
|
4300
|
+
"paused",
|
|
4301
|
+
"finished",
|
|
4302
|
+
"failed",
|
|
4303
|
+
"exit",
|
|
4304
|
+
"returning-to-dock"
|
|
4305
|
+
];
|
|
4306
|
+
var HEARTBEAT_SENTINEL = 206;
|
|
4307
|
+
var HEARTBEAT_SUBSTATE_BASE = 33;
|
|
4308
|
+
var HEARTBEAT_MAIN_STATE_MOWING = 4;
|
|
4309
|
+
function parseMowerHeartbeat(value) {
|
|
4310
|
+
if (!Array.isArray(value) || value.length < 14) {
|
|
4311
|
+
return null;
|
|
4312
|
+
}
|
|
4313
|
+
const bytes = [];
|
|
4314
|
+
for (const b of value) {
|
|
4315
|
+
if (typeof b !== "number") {
|
|
4316
|
+
return null;
|
|
4317
|
+
}
|
|
4318
|
+
bytes.push(b);
|
|
4319
|
+
}
|
|
4320
|
+
if (bytes[0] !== HEARTBEAT_SENTINEL || bytes[bytes.length - 1] !== HEARTBEAT_SENTINEL) {
|
|
4321
|
+
return null;
|
|
4322
|
+
}
|
|
4323
|
+
const rawBattery = bytes[11] ?? 0;
|
|
4324
|
+
const mainState = ((bytes[12] ?? 0) & 15) - 1;
|
|
4325
|
+
const subStateRaw = bytes[13] ?? 0;
|
|
4326
|
+
const taskSubState = mainState === HEARTBEAT_MAIN_STATE_MOWING ? MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null : null;
|
|
4327
|
+
return { rawBattery, mainState, subStateRaw, taskSubState };
|
|
4328
|
+
}
|
|
4239
4329
|
|
|
4240
4330
|
// src/models/mower/capabilities.ts
|
|
4241
4331
|
var FALLBACK2 = {
|
|
@@ -4909,6 +4999,19 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
4909
4999
|
get fault() {
|
|
4910
5000
|
return FAULT(this.faultRaw);
|
|
4911
5001
|
}
|
|
5002
|
+
/**
|
|
5003
|
+
* Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —
|
|
5004
|
+
* idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null
|
|
5005
|
+
* when no mowing session is in progress (or the heartbeat is absent). This is
|
|
5006
|
+
* the donor's "current task" sensor; the heartbeat is push-only (the mower does
|
|
5007
|
+
* not answer live reads), so it tracks the device while it is actively mowing.
|
|
5008
|
+
*/
|
|
5009
|
+
get taskSubState() {
|
|
5010
|
+
const hb = parseMowerHeartbeat(
|
|
5011
|
+
this.getProperty(MOWER_PROP.HEARTBEAT.siid, MOWER_PROP.HEARTBEAT.piid)?.value
|
|
5012
|
+
);
|
|
5013
|
+
return hb?.taskSubState ?? null;
|
|
5014
|
+
}
|
|
4912
5015
|
/** Parsed scheduling task descriptor (2:50), or null. */
|
|
4913
5016
|
get task() {
|
|
4914
5017
|
return parseTaskDescriptor(
|
|
@@ -5100,6 +5203,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
|
|
|
5100
5203
|
}
|
|
5101
5204
|
/** Props worth seeding on start() / polling — exported for the facade. */
|
|
5102
5205
|
static DEFAULT_PROPS = [
|
|
5206
|
+
MOWER_PROP.HEARTBEAT,
|
|
5103
5207
|
MOWER_PROP.STATUS,
|
|
5104
5208
|
MOWER_PROP.DEVICE_CODE,
|
|
5105
5209
|
MOWER_PROP.BATTERY,
|
|
@@ -5728,6 +5832,7 @@ function createClientDumper(client, options) {
|
|
|
5728
5832
|
DreameTransportError,
|
|
5729
5833
|
LIBRARY_NAME,
|
|
5730
5834
|
MOWER_MODEL_CAPABILITIES,
|
|
5835
|
+
MOWER_TASK_SUBSTATES,
|
|
5731
5836
|
MiotError,
|
|
5732
5837
|
MiotState,
|
|
5733
5838
|
MowerCapabilityResolver,
|
|
@@ -5759,6 +5864,7 @@ function createClientDumper(client, options) {
|
|
|
5759
5864
|
isDreameConsumableKey,
|
|
5760
5865
|
mowerConsumableIndex,
|
|
5761
5866
|
parseMowerConsumables,
|
|
5867
|
+
parseMowerHeartbeat,
|
|
5762
5868
|
renderMowerSvg,
|
|
5763
5869
|
renderVacuumPng,
|
|
5764
5870
|
resolveCapabilities,
|