@apocaliss92/nodedreame 1.11.1 → 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.d.cts CHANGED
@@ -1849,6 +1849,31 @@ declare function extractMowerConsumableValues(result: unknown): number[] | null;
1849
1849
  * maintenance) with remaining %. Returns null on a malformed response.
1850
1850
  */
1851
1851
  declare function parseMowerConsumables(result: unknown): readonly MowerConsumableReading[] | null;
1852
+ /** Decoded mowing task sub-state (heartbeat byte 13), or null when no mowing
1853
+ * session is in progress. Ported from antondaubert/dreame-mower
1854
+ * `property_misc.py` Property11Handler. */
1855
+ type MowerTaskSubState = 'idle' | 'starting' | 'mowing' | 'paused' | 'finished' | 'failed' | 'exit' | 'returning-to-dock';
1856
+ /** Ordered sub-state table; the heartbeat encodes `subState = base + index`. */
1857
+ declare const MOWER_TASK_SUBSTATES: readonly MowerTaskSubState[];
1858
+ /** Typed heartbeat decode: battery byte, main-state, and (when mowing) the task
1859
+ * sub-state. */
1860
+ interface MowerHeartbeat {
1861
+ /** Raw battery byte (byte 11) — percent with a charging flag in the high bits. */
1862
+ readonly rawBattery: number;
1863
+ /** `(byte12 & 0x0F) - 1`; 4 = mowing. */
1864
+ readonly mainState: number;
1865
+ /** Raw sub-state byte (byte 13). */
1866
+ readonly subStateRaw: number;
1867
+ /** Decoded sub-state when a mowing session is active, else null. */
1868
+ readonly taskSubState: MowerTaskSubState | null;
1869
+ }
1870
+ /**
1871
+ * Decode the 20-byte HEARTBEAT (1:1) blob. Returns null when the payload is not a
1872
+ * well-formed heartbeat (wrong length / missing 0xCE sentinels). The task
1873
+ * sub-state is only meaningful while `mainState === 4` (mowing); in any other
1874
+ * main state there is no active mowing task (returns `taskSubState: null`).
1875
+ */
1876
+ declare function parseMowerHeartbeat(value: unknown): MowerHeartbeat | null;
1852
1877
 
1853
1878
  /**
1854
1879
  * Per-model mower capability records + a resolver adapting the rich record into
@@ -2053,6 +2078,14 @@ declare class MowerDevice extends BaseDevice {
2053
2078
  * code — which still surfaces as the raw number via {@link faultRaw}.
2054
2079
  */
2055
2080
  get fault(): MowerFault | null;
2081
+ /**
2082
+ * Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —
2083
+ * idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null
2084
+ * when no mowing session is in progress (or the heartbeat is absent). This is
2085
+ * the donor's "current task" sensor; the heartbeat is push-only (the mower does
2086
+ * not answer live reads), so it tracks the device while it is actively mowing.
2087
+ */
2088
+ get taskSubState(): MowerTaskSubState | null;
2056
2089
  /** Parsed scheduling task descriptor (2:50), or null. */
2057
2090
  get task(): MowerTaskDescriptor | null;
2058
2091
  /**
@@ -2135,6 +2168,9 @@ declare class MowerDevice extends BaseDevice {
2135
2168
  mapSvg(opts?: RenderMowerSvgOptions): Promise<string>;
2136
2169
  /** Props worth seeding on start() / polling — exported for the facade. */
2137
2170
  static readonly DEFAULT_PROPS: readonly [{
2171
+ readonly siid: 1;
2172
+ readonly piid: 1;
2173
+ }, {
2138
2174
  readonly siid: 2;
2139
2175
  readonly piid: 1;
2140
2176
  }, {
@@ -2493,4 +2529,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2493
2529
  */
2494
2530
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2495
2531
 
2496
- export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, AUTO_SWITCH_JSON_KEY, type AiDetectionRaw, type AutoSwitchKey, type AutoSwitchRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, type MapBoundingBox, type MapCleanedAreaOverlay, type MapColorScheme, type MapDimensions, type MapFrameType, type MapLayer, type MapLayerType, type MapLowLyingArea, type MapObstacle, type MapPath, type MapPathType, type MapPoint, type MapPose, type MapRestrictedArea, type MapRoom, type MapRoomWall, type MapRun, type MapSegment, type MapStorey, type MapVirtualWall, type MapWallsInfo, type MiotAction, MiotError, type MiotProp, MiotState, type MowerAvailableMap, type MowerCapabilities, MowerCapabilityResolver, MowerChargingStatus, type MowerConsumableKey, type MowerConsumableReading, type MowerContour, MowerControlAction, type MowerControlState, MowerDevice, type MowerDeviceInput, MowerFault, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerPathEntry, type MowerPoint, type MowerSpotArea, MowerStatus, type MowerTaskDescriptor, MowerTaskStatus, type MowerZone, Nodreame, type NodreameDeps, type NodreameEvents, type NodreameOptions, type OssFetchInput, type OssFetcherLike, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type StateChangedEvent, SuctionLevel, TaskStatus, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, consumableSpec, createClientDumper, createDumper, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, encodeAiFeatureWrite, encodeAutoSwitchWrite, extractMowerConsumableValues, getMowerCapabilities, getVacuumCapabilities, isDreameConsumableKey, mowerConsumableIndex, parseMowerConsumables, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
2532
+ export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, AUTO_SWITCH_JSON_KEY, type AiDetectionRaw, type AutoSwitchKey, type AutoSwitchRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, MOWER_TASK_SUBSTATES, type MapBoundingBox, type MapCleanedAreaOverlay, type MapColorScheme, type MapDimensions, type MapFrameType, type MapLayer, type MapLayerType, type MapLowLyingArea, type MapObstacle, type MapPath, type MapPathType, type MapPoint, type MapPose, type MapRestrictedArea, type MapRoom, type MapRoomWall, type MapRun, type MapSegment, type MapStorey, type MapVirtualWall, type MapWallsInfo, type MiotAction, MiotError, type MiotProp, MiotState, type MowerAvailableMap, type MowerCapabilities, MowerCapabilityResolver, MowerChargingStatus, type MowerConsumableKey, type MowerConsumableReading, type MowerContour, MowerControlAction, type MowerControlState, MowerDevice, type MowerDeviceInput, MowerFault, type MowerHeartbeat, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerPathEntry, type MowerPoint, type MowerSpotArea, MowerStatus, type MowerTaskDescriptor, MowerTaskStatus, type MowerTaskSubState, type MowerZone, Nodreame, type NodreameDeps, type NodreameEvents, type NodreameOptions, type OssFetchInput, type OssFetcherLike, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type StateChangedEvent, SuctionLevel, TaskStatus, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, consumableSpec, createClientDumper, createDumper, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, encodeAiFeatureWrite, encodeAutoSwitchWrite, extractMowerConsumableValues, getMowerCapabilities, getVacuumCapabilities, isDreameConsumableKey, mowerConsumableIndex, parseMowerConsumables, parseMowerHeartbeat, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
package/dist/index.d.ts CHANGED
@@ -1849,6 +1849,31 @@ declare function extractMowerConsumableValues(result: unknown): number[] | null;
1849
1849
  * maintenance) with remaining %. Returns null on a malformed response.
1850
1850
  */
1851
1851
  declare function parseMowerConsumables(result: unknown): readonly MowerConsumableReading[] | null;
1852
+ /** Decoded mowing task sub-state (heartbeat byte 13), or null when no mowing
1853
+ * session is in progress. Ported from antondaubert/dreame-mower
1854
+ * `property_misc.py` Property11Handler. */
1855
+ type MowerTaskSubState = 'idle' | 'starting' | 'mowing' | 'paused' | 'finished' | 'failed' | 'exit' | 'returning-to-dock';
1856
+ /** Ordered sub-state table; the heartbeat encodes `subState = base + index`. */
1857
+ declare const MOWER_TASK_SUBSTATES: readonly MowerTaskSubState[];
1858
+ /** Typed heartbeat decode: battery byte, main-state, and (when mowing) the task
1859
+ * sub-state. */
1860
+ interface MowerHeartbeat {
1861
+ /** Raw battery byte (byte 11) — percent with a charging flag in the high bits. */
1862
+ readonly rawBattery: number;
1863
+ /** `(byte12 & 0x0F) - 1`; 4 = mowing. */
1864
+ readonly mainState: number;
1865
+ /** Raw sub-state byte (byte 13). */
1866
+ readonly subStateRaw: number;
1867
+ /** Decoded sub-state when a mowing session is active, else null. */
1868
+ readonly taskSubState: MowerTaskSubState | null;
1869
+ }
1870
+ /**
1871
+ * Decode the 20-byte HEARTBEAT (1:1) blob. Returns null when the payload is not a
1872
+ * well-formed heartbeat (wrong length / missing 0xCE sentinels). The task
1873
+ * sub-state is only meaningful while `mainState === 4` (mowing); in any other
1874
+ * main state there is no active mowing task (returns `taskSubState: null`).
1875
+ */
1876
+ declare function parseMowerHeartbeat(value: unknown): MowerHeartbeat | null;
1852
1877
 
1853
1878
  /**
1854
1879
  * Per-model mower capability records + a resolver adapting the rich record into
@@ -2053,6 +2078,14 @@ declare class MowerDevice extends BaseDevice {
2053
2078
  * code — which still surfaces as the raw number via {@link faultRaw}.
2054
2079
  */
2055
2080
  get fault(): MowerFault | null;
2081
+ /**
2082
+ * Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —
2083
+ * idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null
2084
+ * when no mowing session is in progress (or the heartbeat is absent). This is
2085
+ * the donor's "current task" sensor; the heartbeat is push-only (the mower does
2086
+ * not answer live reads), so it tracks the device while it is actively mowing.
2087
+ */
2088
+ get taskSubState(): MowerTaskSubState | null;
2056
2089
  /** Parsed scheduling task descriptor (2:50), or null. */
2057
2090
  get task(): MowerTaskDescriptor | null;
2058
2091
  /**
@@ -2135,6 +2168,9 @@ declare class MowerDevice extends BaseDevice {
2135
2168
  mapSvg(opts?: RenderMowerSvgOptions): Promise<string>;
2136
2169
  /** Props worth seeding on start() / polling — exported for the facade. */
2137
2170
  static readonly DEFAULT_PROPS: readonly [{
2171
+ readonly siid: 1;
2172
+ readonly piid: 1;
2173
+ }, {
2138
2174
  readonly siid: 2;
2139
2175
  readonly piid: 1;
2140
2176
  }, {
@@ -2493,4 +2529,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2493
2529
  */
2494
2530
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2495
2531
 
2496
- export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, AUTO_SWITCH_JSON_KEY, type AiDetectionRaw, type AutoSwitchKey, type AutoSwitchRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, type MapBoundingBox, type MapCleanedAreaOverlay, type MapColorScheme, type MapDimensions, type MapFrameType, type MapLayer, type MapLayerType, type MapLowLyingArea, type MapObstacle, type MapPath, type MapPathType, type MapPoint, type MapPose, type MapRestrictedArea, type MapRoom, type MapRoomWall, type MapRun, type MapSegment, type MapStorey, type MapVirtualWall, type MapWallsInfo, type MiotAction, MiotError, type MiotProp, MiotState, type MowerAvailableMap, type MowerCapabilities, MowerCapabilityResolver, MowerChargingStatus, type MowerConsumableKey, type MowerConsumableReading, type MowerContour, MowerControlAction, type MowerControlState, MowerDevice, type MowerDeviceInput, MowerFault, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerPathEntry, type MowerPoint, type MowerSpotArea, MowerStatus, type MowerTaskDescriptor, MowerTaskStatus, type MowerZone, Nodreame, type NodreameDeps, type NodreameEvents, type NodreameOptions, type OssFetchInput, type OssFetcherLike, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type StateChangedEvent, SuctionLevel, TaskStatus, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, consumableSpec, createClientDumper, createDumper, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, encodeAiFeatureWrite, encodeAutoSwitchWrite, extractMowerConsumableValues, getMowerCapabilities, getVacuumCapabilities, isDreameConsumableKey, mowerConsumableIndex, parseMowerConsumables, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
2532
+ export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, AUTO_SWITCH_JSON_KEY, type AiDetectionRaw, type AutoSwitchKey, type AutoSwitchRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, MOWER_TASK_SUBSTATES, type MapBoundingBox, type MapCleanedAreaOverlay, type MapColorScheme, type MapDimensions, type MapFrameType, type MapLayer, type MapLayerType, type MapLowLyingArea, type MapObstacle, type MapPath, type MapPathType, type MapPoint, type MapPose, type MapRestrictedArea, type MapRoom, type MapRoomWall, type MapRun, type MapSegment, type MapStorey, type MapVirtualWall, type MapWallsInfo, type MiotAction, MiotError, type MiotProp, MiotState, type MowerAvailableMap, type MowerCapabilities, MowerCapabilityResolver, MowerChargingStatus, type MowerConsumableKey, type MowerConsumableReading, type MowerContour, MowerControlAction, type MowerControlState, MowerDevice, type MowerDeviceInput, MowerFault, type MowerHeartbeat, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerPathEntry, type MowerPoint, type MowerSpotArea, MowerStatus, type MowerTaskDescriptor, MowerTaskStatus, type MowerTaskSubState, type MowerZone, Nodreame, type NodreameDeps, type NodreameEvents, type NodreameOptions, type OssFetchInput, type OssFetcherLike, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type StateChangedEvent, SuctionLevel, TaskStatus, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, consumableSpec, createClientDumper, createDumper, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, encodeAiFeatureWrite, encodeAutoSwitchWrite, extractMowerConsumableValues, getMowerCapabilities, getVacuumCapabilities, isDreameConsumableKey, mowerConsumableIndex, parseMowerConsumables, parseMowerHeartbeat, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/support/version.ts
2
2
  var LIBRARY_NAME = "nodedreame";
3
- var LIBRARY_VERSION = "1.11.1";
3
+ var LIBRARY_VERSION = "1.11.2";
4
4
 
5
5
  // src/transport/errors.ts
6
6
  var DreameError = class extends Error {
@@ -1575,7 +1575,9 @@ function encodeAiFeatureWrite(raw, feature, value) {
1575
1575
  if (typeof raw === "number") {
1576
1576
  const bit = AI_FEATURE_BIT[feature];
1577
1577
  if (bit === void 0) {
1578
- throw new Error(`encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`);
1578
+ throw new Error(
1579
+ `encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`
1580
+ );
1579
1581
  }
1580
1582
  return value ? raw | bit : raw & ~bit;
1581
1583
  }
@@ -1692,18 +1694,53 @@ function encodeAutoSwitchWrite(key2, value) {
1692
1694
 
1693
1695
  // src/models/vacuum/consumables.ts
1694
1696
  var VACUUM_CONSUMABLES = [
1695
- { key: "main-brush", label: "Main Brush", life: { siid: 9, piid: 2 }, reset: { siid: 9, aiid: 1 } },
1696
- { key: "side-brush", label: "Side Brush", life: { siid: 10, piid: 2 }, reset: { siid: 10, aiid: 1 } },
1697
+ {
1698
+ key: "main-brush",
1699
+ label: "Main Brush",
1700
+ life: { siid: 9, piid: 2 },
1701
+ reset: { siid: 9, aiid: 1 }
1702
+ },
1703
+ {
1704
+ key: "side-brush",
1705
+ label: "Side Brush",
1706
+ life: { siid: 10, piid: 2 },
1707
+ reset: { siid: 10, aiid: 1 }
1708
+ },
1697
1709
  { key: "filter", label: "Filter", life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },
1698
1710
  { key: "sensor", label: "Sensor", life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },
1699
- { key: "tank-filter", label: "Tank Filter", life: { siid: 17, piid: 1 }, reset: { siid: 17, aiid: 1 } },
1711
+ {
1712
+ key: "tank-filter",
1713
+ label: "Tank Filter",
1714
+ life: { siid: 17, piid: 1 },
1715
+ reset: { siid: 17, aiid: 1 }
1716
+ },
1700
1717
  { key: "mop-pad", label: "Mop Pad", life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },
1701
- { key: "silver-ion", label: "Silver-ion", life: { siid: 19, piid: 2 }, reset: { siid: 19, aiid: 1 } },
1702
- { key: "detergent", label: "Detergent", life: { siid: 20, piid: 1 }, reset: { siid: 20, aiid: 1 } },
1718
+ {
1719
+ key: "silver-ion",
1720
+ label: "Silver-ion",
1721
+ life: { siid: 19, piid: 2 },
1722
+ reset: { siid: 19, aiid: 1 }
1723
+ },
1724
+ {
1725
+ key: "detergent",
1726
+ label: "Detergent",
1727
+ life: { siid: 20, piid: 1 },
1728
+ reset: { siid: 20, aiid: 1 }
1729
+ },
1703
1730
  { key: "squeegee", label: "Squeegee", life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },
1704
- { key: "deodorizer", label: "Deodorizer", life: { siid: 29, piid: 2 }, reset: { siid: 29, aiid: 1 } },
1731
+ {
1732
+ key: "deodorizer",
1733
+ label: "Deodorizer",
1734
+ life: { siid: 29, piid: 2 },
1735
+ reset: { siid: 29, aiid: 1 }
1736
+ },
1705
1737
  { key: "wheel", label: "Wheel", life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },
1706
- { key: "scale-inhibitor", label: "Scale Inhibitor", life: { siid: 31, piid: 2 }, reset: { siid: 31, aiid: 1 } },
1738
+ {
1739
+ key: "scale-inhibitor",
1740
+ label: "Scale Inhibitor",
1741
+ life: { siid: 31, piid: 2 },
1742
+ reset: { siid: 31, aiid: 1 }
1743
+ },
1707
1744
  { key: "dust-bag", label: "Dust Bag", life: { siid: 27, piid: 17 }, reset: null }
1708
1745
  ];
1709
1746
  function consumableSpec(key2) {
@@ -3818,6 +3855,9 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
3818
3855
 
3819
3856
  // src/models/mower/properties.ts
3820
3857
  var MOWER_PROP = {
3858
+ /** VERIFIED-by-donor — 20-byte heartbeat (sentinel 0xCE at [0]/[19]); byte 11
3859
+ * battery, byte 12 main-state, byte 13 task sub-state. See parseHeartbeat. */
3860
+ HEARTBEAT: { siid: 1, piid: 1 },
3821
3861
  /** VERIFIED-by-donor — pose + coverage telemetry (binary). P4 uses progress % only. */
3822
3862
  POSE_COVERAGE: { siid: 1, piid: 4 },
3823
3863
  /** VERIFIED-by-donor — MowerStatus (DeviceStatus). */
@@ -4167,6 +4207,39 @@ function parseMowerConsumables(result) {
4167
4207
  };
4168
4208
  });
4169
4209
  }
4210
+ var MOWER_TASK_SUBSTATES = [
4211
+ "idle",
4212
+ "starting",
4213
+ "mowing",
4214
+ "paused",
4215
+ "finished",
4216
+ "failed",
4217
+ "exit",
4218
+ "returning-to-dock"
4219
+ ];
4220
+ var HEARTBEAT_SENTINEL = 206;
4221
+ var HEARTBEAT_SUBSTATE_BASE = 33;
4222
+ var HEARTBEAT_MAIN_STATE_MOWING = 4;
4223
+ function parseMowerHeartbeat(value) {
4224
+ if (!Array.isArray(value) || value.length < 14) {
4225
+ return null;
4226
+ }
4227
+ const bytes = [];
4228
+ for (const b of value) {
4229
+ if (typeof b !== "number") {
4230
+ return null;
4231
+ }
4232
+ bytes.push(b);
4233
+ }
4234
+ if (bytes[0] !== HEARTBEAT_SENTINEL || bytes[bytes.length - 1] !== HEARTBEAT_SENTINEL) {
4235
+ return null;
4236
+ }
4237
+ const rawBattery = bytes[11] ?? 0;
4238
+ const mainState = ((bytes[12] ?? 0) & 15) - 1;
4239
+ const subStateRaw = bytes[13] ?? 0;
4240
+ const taskSubState = mainState === HEARTBEAT_MAIN_STATE_MOWING ? MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null : null;
4241
+ return { rawBattery, mainState, subStateRaw, taskSubState };
4242
+ }
4170
4243
 
4171
4244
  // src/models/mower/capabilities.ts
4172
4245
  var FALLBACK2 = {
@@ -4840,6 +4913,19 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
4840
4913
  get fault() {
4841
4914
  return FAULT(this.faultRaw);
4842
4915
  }
4916
+ /**
4917
+ * Live mowing task sub-state decoded from the HEARTBEAT (1:1) blob —
4918
+ * idle/starting/mowing/paused/finished/failed/exit/returning-to-dock — or null
4919
+ * when no mowing session is in progress (or the heartbeat is absent). This is
4920
+ * the donor's "current task" sensor; the heartbeat is push-only (the mower does
4921
+ * not answer live reads), so it tracks the device while it is actively mowing.
4922
+ */
4923
+ get taskSubState() {
4924
+ const hb = parseMowerHeartbeat(
4925
+ this.getProperty(MOWER_PROP.HEARTBEAT.siid, MOWER_PROP.HEARTBEAT.piid)?.value
4926
+ );
4927
+ return hb?.taskSubState ?? null;
4928
+ }
4843
4929
  /** Parsed scheduling task descriptor (2:50), or null. */
4844
4930
  get task() {
4845
4931
  return parseTaskDescriptor(
@@ -5031,6 +5117,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5031
5117
  }
5032
5118
  /** Props worth seeding on start() / polling — exported for the facade. */
5033
5119
  static DEFAULT_PROPS = [
5120
+ MOWER_PROP.HEARTBEAT,
5034
5121
  MOWER_PROP.STATUS,
5035
5122
  MOWER_PROP.DEVICE_CODE,
5036
5123
  MOWER_PROP.BATTERY,
@@ -5658,6 +5745,7 @@ export {
5658
5745
  DreameTransportError,
5659
5746
  LIBRARY_NAME,
5660
5747
  MODEL_CAPABILITIES2 as MOWER_MODEL_CAPABILITIES,
5748
+ MOWER_TASK_SUBSTATES,
5661
5749
  MiotError,
5662
5750
  MiotState,
5663
5751
  MowerCapabilityResolver,
@@ -5689,6 +5777,7 @@ export {
5689
5777
  isDreameConsumableKey,
5690
5778
  mowerConsumableIndex,
5691
5779
  parseMowerConsumables,
5780
+ parseMowerHeartbeat,
5692
5781
  renderMowerSvg,
5693
5782
  renderVacuumPng,
5694
5783
  resolveCapabilities,