@apocaliss92/nodedreame 1.11.1 → 1.11.3

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 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.1";
89
+ var LIBRARY_VERSION = "1.11.3";
88
90
 
89
91
  // src/transport/errors.ts
90
92
  var DreameError = class extends Error {
@@ -142,8 +144,11 @@ function buildRlcHeader(region, lang, country) {
142
144
  function randomMqttClientId() {
143
145
  return "p_" + (0, import_node_crypto.randomBytes)(8).toString("hex");
144
146
  }
147
+ var MAX_REQUEST_ID = 16777215;
148
+ var nextRequestId = Math.floor(Math.random() * 100) + 1;
145
149
  function randomRequestId() {
146
- return Math.floor(Math.random() * 2147483647) + 1;
150
+ nextRequestId = nextRequestId >= MAX_REQUEST_ID ? 1 : nextRequestId + 1;
151
+ return nextRequestId;
147
152
  }
148
153
 
149
154
  // src/auth/config.ts
@@ -342,7 +347,7 @@ var OAuthTokenResponseSchema = import_zod.z.object({
342
347
  error: import_zod.z.string().optional(),
343
348
  error_description: import_zod.z.string().optional(),
344
349
  code: import_zod.z.number().optional(),
345
- msg: import_zod.z.string().optional()
350
+ msg: import_zod.z.string().nullish()
346
351
  }).passthrough();
347
352
  var RawDeviceSchema = import_zod.z.object({
348
353
  did: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
@@ -357,7 +362,7 @@ var RawDeviceSchema = import_zod.z.object({
357
362
  }).passthrough();
358
363
  var DeviceListResponseSchema = import_zod.z.object({
359
364
  code: import_zod.z.number().optional(),
360
- msg: import_zod.z.string().optional(),
365
+ msg: import_zod.z.string().nullish(),
361
366
  data: import_zod.z.object({
362
367
  page: import_zod.z.object({ records: import_zod.z.array(RawDeviceSchema).optional() }).partial().optional(),
363
368
  records: import_zod.z.array(RawDeviceSchema).optional()
@@ -377,12 +382,12 @@ var CachedPropEntrySchema = import_zod.z.object({
377
382
  }).passthrough();
378
383
  var CachedPropsResponseSchema = import_zod.z.object({
379
384
  code: import_zod.z.number().optional(),
380
- msg: import_zod.z.string().optional(),
385
+ msg: import_zod.z.string().nullish(),
381
386
  data: import_zod.z.array(CachedPropEntrySchema).optional()
382
387
  }).passthrough();
383
388
  var SendCommandResponseSchema = import_zod.z.object({
384
389
  code: import_zod.z.number().optional(),
385
- msg: import_zod.z.string().optional(),
390
+ msg: import_zod.z.string().nullish(),
386
391
  data: import_zod.z.object({ result: import_zod.z.unknown().optional() }).passthrough().optional(),
387
392
  result: import_zod.z.unknown().optional()
388
393
  }).passthrough();
@@ -1659,7 +1664,9 @@ function encodeAiFeatureWrite(raw, feature, value) {
1659
1664
  if (typeof raw === "number") {
1660
1665
  const bit = AI_FEATURE_BIT[feature];
1661
1666
  if (bit === void 0) {
1662
- throw new Error(`encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`);
1667
+ throw new Error(
1668
+ `encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`
1669
+ );
1663
1670
  }
1664
1671
  return value ? raw | bit : raw & ~bit;
1665
1672
  }
@@ -1776,18 +1783,53 @@ function encodeAutoSwitchWrite(key2, value) {
1776
1783
 
1777
1784
  // src/models/vacuum/consumables.ts
1778
1785
  var VACUUM_CONSUMABLES = [
1779
- { key: "main-brush", label: "Main Brush", life: { siid: 9, piid: 2 }, reset: { siid: 9, aiid: 1 } },
1780
- { key: "side-brush", label: "Side Brush", life: { siid: 10, piid: 2 }, reset: { siid: 10, aiid: 1 } },
1786
+ {
1787
+ key: "main-brush",
1788
+ label: "Main Brush",
1789
+ life: { siid: 9, piid: 2 },
1790
+ reset: { siid: 9, aiid: 1 }
1791
+ },
1792
+ {
1793
+ key: "side-brush",
1794
+ label: "Side Brush",
1795
+ life: { siid: 10, piid: 2 },
1796
+ reset: { siid: 10, aiid: 1 }
1797
+ },
1781
1798
  { key: "filter", label: "Filter", life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },
1782
1799
  { key: "sensor", label: "Sensor", life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },
1783
- { key: "tank-filter", label: "Tank Filter", life: { siid: 17, piid: 1 }, reset: { siid: 17, aiid: 1 } },
1800
+ {
1801
+ key: "tank-filter",
1802
+ label: "Tank Filter",
1803
+ life: { siid: 17, piid: 1 },
1804
+ reset: { siid: 17, aiid: 1 }
1805
+ },
1784
1806
  { key: "mop-pad", label: "Mop Pad", life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },
1785
- { key: "silver-ion", label: "Silver-ion", life: { siid: 19, piid: 2 }, reset: { siid: 19, aiid: 1 } },
1786
- { key: "detergent", label: "Detergent", life: { siid: 20, piid: 1 }, reset: { siid: 20, aiid: 1 } },
1807
+ {
1808
+ key: "silver-ion",
1809
+ label: "Silver-ion",
1810
+ life: { siid: 19, piid: 2 },
1811
+ reset: { siid: 19, aiid: 1 }
1812
+ },
1813
+ {
1814
+ key: "detergent",
1815
+ label: "Detergent",
1816
+ life: { siid: 20, piid: 1 },
1817
+ reset: { siid: 20, aiid: 1 }
1818
+ },
1787
1819
  { key: "squeegee", label: "Squeegee", life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },
1788
- { key: "deodorizer", label: "Deodorizer", life: { siid: 29, piid: 2 }, reset: { siid: 29, aiid: 1 } },
1820
+ {
1821
+ key: "deodorizer",
1822
+ label: "Deodorizer",
1823
+ life: { siid: 29, piid: 2 },
1824
+ reset: { siid: 29, aiid: 1 }
1825
+ },
1789
1826
  { key: "wheel", label: "Wheel", life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },
1790
- { key: "scale-inhibitor", label: "Scale Inhibitor", life: { siid: 31, piid: 2 }, reset: { siid: 31, aiid: 1 } },
1827
+ {
1828
+ key: "scale-inhibitor",
1829
+ label: "Scale Inhibitor",
1830
+ life: { siid: 31, piid: 2 },
1831
+ reset: { siid: 31, aiid: 1 }
1832
+ },
1791
1833
  { key: "dust-bag", label: "Dust Bag", life: { siid: 27, piid: 17 }, reset: null }
1792
1834
  ];
1793
1835
  function consumableSpec(key2) {
@@ -3902,6 +3944,9 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
3902
3944
 
3903
3945
  // src/models/mower/properties.ts
3904
3946
  var MOWER_PROP = {
3947
+ /** VERIFIED-by-donor — 20-byte heartbeat (sentinel 0xCE at [0]/[19]); byte 11
3948
+ * battery, byte 12 main-state, byte 13 task sub-state. See parseHeartbeat. */
3949
+ HEARTBEAT: { siid: 1, piid: 1 },
3905
3950
  /** VERIFIED-by-donor — pose + coverage telemetry (binary). P4 uses progress % only. */
3906
3951
  POSE_COVERAGE: { siid: 1, piid: 4 },
3907
3952
  /** VERIFIED-by-donor — MowerStatus (DeviceStatus). */
@@ -4251,6 +4296,39 @@ function parseMowerConsumables(result) {
4251
4296
  };
4252
4297
  });
4253
4298
  }
4299
+ var MOWER_TASK_SUBSTATES = [
4300
+ "idle",
4301
+ "starting",
4302
+ "mowing",
4303
+ "paused",
4304
+ "finished",
4305
+ "failed",
4306
+ "exit",
4307
+ "returning-to-dock"
4308
+ ];
4309
+ var HEARTBEAT_SENTINEL = 206;
4310
+ var HEARTBEAT_SUBSTATE_BASE = 33;
4311
+ var HEARTBEAT_MAIN_STATE_MOWING = 4;
4312
+ function parseMowerHeartbeat(value) {
4313
+ if (!Array.isArray(value) || value.length < 14) {
4314
+ return null;
4315
+ }
4316
+ const bytes = [];
4317
+ for (const b of value) {
4318
+ if (typeof b !== "number") {
4319
+ return null;
4320
+ }
4321
+ bytes.push(b);
4322
+ }
4323
+ if (bytes[0] !== HEARTBEAT_SENTINEL || bytes[bytes.length - 1] !== HEARTBEAT_SENTINEL) {
4324
+ return null;
4325
+ }
4326
+ const rawBattery = bytes[11] ?? 0;
4327
+ const mainState = ((bytes[12] ?? 0) & 15) - 1;
4328
+ const subStateRaw = bytes[13] ?? 0;
4329
+ const taskSubState = mainState === HEARTBEAT_MAIN_STATE_MOWING ? MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null : null;
4330
+ return { rawBattery, mainState, subStateRaw, taskSubState };
4331
+ }
4254
4332
 
4255
4333
  // src/models/mower/capabilities.ts
4256
4334
  var FALLBACK2 = {
@@ -4924,6 +5002,19 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
4924
5002
  get fault() {
4925
5003
  return FAULT(this.faultRaw);
4926
5004
  }
5005
+ /**
5006
+ * Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —
5007
+ * idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null
5008
+ * when no mowing session is in progress (or the heartbeat is absent). This is
5009
+ * the donor's "current task" sensor; the heartbeat is push-only (the mower does
5010
+ * not answer live reads), so it tracks the device while it is actively mowing.
5011
+ */
5012
+ get taskSubState() {
5013
+ const hb = parseMowerHeartbeat(
5014
+ this.getProperty(MOWER_PROP.HEARTBEAT.siid, MOWER_PROP.HEARTBEAT.piid)?.value
5015
+ );
5016
+ return hb?.taskSubState ?? null;
5017
+ }
4927
5018
  /** Parsed scheduling task descriptor (2:50), or null. */
4928
5019
  get task() {
4929
5020
  return parseTaskDescriptor(
@@ -5115,6 +5206,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5115
5206
  }
5116
5207
  /** Props worth seeding on start() / polling — exported for the facade. */
5117
5208
  static DEFAULT_PROPS = [
5209
+ MOWER_PROP.HEARTBEAT,
5118
5210
  MOWER_PROP.STATUS,
5119
5211
  MOWER_PROP.DEVICE_CODE,
5120
5212
  MOWER_PROP.BATTERY,
@@ -5743,6 +5835,7 @@ function createClientDumper(client, options) {
5743
5835
  DreameTransportError,
5744
5836
  LIBRARY_NAME,
5745
5837
  MOWER_MODEL_CAPABILITIES,
5838
+ MOWER_TASK_SUBSTATES,
5746
5839
  MiotError,
5747
5840
  MiotState,
5748
5841
  MowerCapabilityResolver,
@@ -5774,6 +5867,7 @@ function createClientDumper(client, options) {
5774
5867
  isDreameConsumableKey,
5775
5868
  mowerConsumableIndex,
5776
5869
  parseMowerConsumables,
5870
+ parseMowerHeartbeat,
5777
5871
  renderMowerSvg,
5778
5872
  renderVacuumPng,
5779
5873
  resolveCapabilities,