@apocaliss92/nodedreame 1.11.5 → 1.11.6

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
@@ -1815,6 +1815,14 @@ declare enum MowerFault {
1815
1815
  PauseTimeoutReturning = 72,// INFO
1816
1816
  TopCoverOpen = 73
1817
1817
  }
1818
+ /** Severity class of a mower DEVICE_CODE (2:2 / {@link MowerFault}). */
1819
+ type MowerFaultSeverity = 'info' | 'warning' | 'error';
1820
+ /**
1821
+ * Classify a mower device-code by severity, matching the INFO/WARNING/ERROR
1822
+ * annotations on {@link MowerFault}. Most codes are status/INFO (e.g. 50
1823
+ * TaskStart); only a subset are actionable. Unknown codes default to `info`.
1824
+ */
1825
+ declare function mowerFaultSeverity(code: number | null): MowerFaultSeverity;
1818
1826
 
1819
1827
  /**
1820
1828
  * Cast-free mower decoders. Re-exports the shared numeric primitives, and adds
@@ -1891,6 +1899,28 @@ interface MowerHeartbeat {
1891
1899
  * main state there is no active mowing task (returns `taskSubState: null`).
1892
1900
  */
1893
1901
  declare function parseMowerHeartbeat(value: unknown): MowerHeartbeat | null;
1902
+ /** Decoded mowing-progress signal from the POSE_COVERAGE (1:4) `task` block. */
1903
+ interface MowerMowingProgress {
1904
+ /** 0..100 — overall mowing completion. */
1905
+ readonly progressPercent: number;
1906
+ /** Already-finished area in m². */
1907
+ readonly currentAreaSqm: number;
1908
+ /** Total target area in m². */
1909
+ readonly totalAreaSqm: number;
1910
+ }
1911
+ /**
1912
+ * Locate the 10-byte `task` block inside a POSE_COVERAGE (1:4) frame and decode
1913
+ * the mowing progress. Frame shapes (ported from antondaubert/dreame-mower
1914
+ * `pose_coverage.py`):
1915
+ * - sentinel-framed `[CE] pose(6) trace(15) task(10) [CE]` (33B) / `…task(10) trace2(11) [CE]` (44B)
1916
+ * → task at offset 22;
1917
+ * - alt `task(10) [CE] [+trace]` (no leading sentinel, ≥11B) → task at offset 0.
1918
+ * The 22B `[CE] pose trace` and 13B `[CE] pose extra` shapes carry NO task → null.
1919
+ *
1920
+ * `task` layout: `[2:4] percent u16LE (value×100) [4:7] total u24LE (centi-m²)
1921
+ * [7:10] finished u24LE (centi-m²)`.
1922
+ */
1923
+ declare function parseMowingProgress(value: unknown): MowerMowingProgress | null;
1894
1924
 
1895
1925
  /**
1896
1926
  * Per-model mower capability records + a resolver adapting the rich record into
@@ -2110,6 +2140,17 @@ declare class MowerDevice extends BaseDevice {
2110
2140
  * This is the P4 progress surface; the byte-accurate pose-track % is P5.
2111
2141
  */
2112
2142
  get coverageTargetPct(): number | null;
2143
+ /**
2144
+ * Live mowing PROGRESS decoded from the POSE_COVERAGE (1:4) task block —
2145
+ * `{progressPercent, currentAreaSqm, totalAreaSqm}` — or null when 1:4 carries
2146
+ * no task block (idle / pose-only frame). This is the byte-accurate progress
2147
+ * the Dreamehome app shows (vs {@link coverageTargetPct}, the task TARGET).
2148
+ */
2149
+ get mowingProgress(): MowerMowingProgress | null;
2150
+ /** Mowing completion percent (0..100) from {@link mowingProgress}, or null. */
2151
+ get mowingProgressPct(): number | null;
2152
+ /** Severity of the current DEVICE_CODE (2:2) — info / warning / error. */
2153
+ get faultSeverity(): MowerFaultSeverity;
2113
2154
  /** Parsed per-zone control status (2:56), or null. */
2114
2155
  get controlStatus(): MowerControlState | null;
2115
2156
  get controlAction(): MowerControlAction | null;
@@ -2187,6 +2228,9 @@ declare class MowerDevice extends BaseDevice {
2187
2228
  static readonly DEFAULT_PROPS: readonly [{
2188
2229
  readonly siid: 1;
2189
2230
  readonly piid: 1;
2231
+ }, {
2232
+ readonly siid: 1;
2233
+ readonly piid: 4;
2190
2234
  }, {
2191
2235
  readonly siid: 2;
2192
2236
  readonly piid: 1;
@@ -2546,4 +2590,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2546
2590
  */
2547
2591
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2548
2592
 
2549
- 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 };
2593
+ 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 MowerFaultSeverity, type MowerHeartbeat, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerMowingProgress, 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, mowerFaultSeverity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
package/dist/index.d.ts CHANGED
@@ -1815,6 +1815,14 @@ declare enum MowerFault {
1815
1815
  PauseTimeoutReturning = 72,// INFO
1816
1816
  TopCoverOpen = 73
1817
1817
  }
1818
+ /** Severity class of a mower DEVICE_CODE (2:2 / {@link MowerFault}). */
1819
+ type MowerFaultSeverity = 'info' | 'warning' | 'error';
1820
+ /**
1821
+ * Classify a mower device-code by severity, matching the INFO/WARNING/ERROR
1822
+ * annotations on {@link MowerFault}. Most codes are status/INFO (e.g. 50
1823
+ * TaskStart); only a subset are actionable. Unknown codes default to `info`.
1824
+ */
1825
+ declare function mowerFaultSeverity(code: number | null): MowerFaultSeverity;
1818
1826
 
1819
1827
  /**
1820
1828
  * Cast-free mower decoders. Re-exports the shared numeric primitives, and adds
@@ -1891,6 +1899,28 @@ interface MowerHeartbeat {
1891
1899
  * main state there is no active mowing task (returns `taskSubState: null`).
1892
1900
  */
1893
1901
  declare function parseMowerHeartbeat(value: unknown): MowerHeartbeat | null;
1902
+ /** Decoded mowing-progress signal from the POSE_COVERAGE (1:4) `task` block. */
1903
+ interface MowerMowingProgress {
1904
+ /** 0..100 — overall mowing completion. */
1905
+ readonly progressPercent: number;
1906
+ /** Already-finished area in m². */
1907
+ readonly currentAreaSqm: number;
1908
+ /** Total target area in m². */
1909
+ readonly totalAreaSqm: number;
1910
+ }
1911
+ /**
1912
+ * Locate the 10-byte `task` block inside a POSE_COVERAGE (1:4) frame and decode
1913
+ * the mowing progress. Frame shapes (ported from antondaubert/dreame-mower
1914
+ * `pose_coverage.py`):
1915
+ * - sentinel-framed `[CE] pose(6) trace(15) task(10) [CE]` (33B) / `…task(10) trace2(11) [CE]` (44B)
1916
+ * → task at offset 22;
1917
+ * - alt `task(10) [CE] [+trace]` (no leading sentinel, ≥11B) → task at offset 0.
1918
+ * The 22B `[CE] pose trace` and 13B `[CE] pose extra` shapes carry NO task → null.
1919
+ *
1920
+ * `task` layout: `[2:4] percent u16LE (value×100) [4:7] total u24LE (centi-m²)
1921
+ * [7:10] finished u24LE (centi-m²)`.
1922
+ */
1923
+ declare function parseMowingProgress(value: unknown): MowerMowingProgress | null;
1894
1924
 
1895
1925
  /**
1896
1926
  * Per-model mower capability records + a resolver adapting the rich record into
@@ -2110,6 +2140,17 @@ declare class MowerDevice extends BaseDevice {
2110
2140
  * This is the P4 progress surface; the byte-accurate pose-track % is P5.
2111
2141
  */
2112
2142
  get coverageTargetPct(): number | null;
2143
+ /**
2144
+ * Live mowing PROGRESS decoded from the POSE_COVERAGE (1:4) task block —
2145
+ * `{progressPercent, currentAreaSqm, totalAreaSqm}` — or null when 1:4 carries
2146
+ * no task block (idle / pose-only frame). This is the byte-accurate progress
2147
+ * the Dreamehome app shows (vs {@link coverageTargetPct}, the task TARGET).
2148
+ */
2149
+ get mowingProgress(): MowerMowingProgress | null;
2150
+ /** Mowing completion percent (0..100) from {@link mowingProgress}, or null. */
2151
+ get mowingProgressPct(): number | null;
2152
+ /** Severity of the current DEVICE_CODE (2:2) — info / warning / error. */
2153
+ get faultSeverity(): MowerFaultSeverity;
2113
2154
  /** Parsed per-zone control status (2:56), or null. */
2114
2155
  get controlStatus(): MowerControlState | null;
2115
2156
  get controlAction(): MowerControlAction | null;
@@ -2187,6 +2228,9 @@ declare class MowerDevice extends BaseDevice {
2187
2228
  static readonly DEFAULT_PROPS: readonly [{
2188
2229
  readonly siid: 1;
2189
2230
  readonly piid: 1;
2231
+ }, {
2232
+ readonly siid: 1;
2233
+ readonly piid: 4;
2190
2234
  }, {
2191
2235
  readonly siid: 2;
2192
2236
  readonly piid: 1;
@@ -2546,4 +2590,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2546
2590
  */
2547
2591
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2548
2592
 
2549
- 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 };
2593
+ 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 MowerFaultSeverity, type MowerHeartbeat, type MowerMap, type MowerMapBoundary, type MowerMowPath, type MowerMowingProgress, 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, mowerFaultSeverity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, 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.5";
3
+ var LIBRARY_VERSION = "1.11.6";
4
4
 
5
5
  // src/transport/errors.ts
6
6
  var DreameError = class extends Error {
@@ -2771,18 +2771,27 @@ function decodeVacuumMap(input, opts = {}) {
2771
2771
  const pixelGrid = inflated.subarray(pixelStart, pixelEnd);
2772
2772
  const canDecodePixels = header.frameType === "I" && pixelGrid.length === header.width * header.height;
2773
2773
  const layers = canDecodePixels ? decodePixelGridFsm1(pixelGrid, header.width, header.height) : [];
2774
- const segments = canDecodePixels ? collectSegments(layers, dimensions, tail) : [];
2775
2774
  const paths = parsePathTr(tail.tr ?? "");
2776
2775
  const obstacles = parseObstacles(tail.ai_obstacle ?? []);
2777
2776
  let geometry = parseTailGeometry(tail);
2778
- if (!isGeometryComplete(geometry) && typeof tail.rism === "string" && tail.rism.length > 0) {
2777
+ let segTail = tail;
2778
+ const outerHasNames = Object.values(tail.seg_inf ?? {}).some(
2779
+ (m) => typeof m?.name === "string" && m.name.length > 0
2780
+ );
2781
+ if ((!isGeometryComplete(geometry) || !outerHasNames) && typeof tail.rism === "string" && tail.rism.length > 0) {
2779
2782
  try {
2780
2783
  const innerInflated = unwrapEnvelope(tail.rism);
2781
2784
  const { tail: innerTail } = parseFrame(innerInflated);
2782
- geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));
2785
+ if (!isGeometryComplete(geometry)) {
2786
+ geometry = coalesceGeometry(geometry, parseTailGeometry(innerTail));
2787
+ }
2788
+ if (!outerHasNames && innerTail.seg_inf) {
2789
+ segTail = mergeSegInfNames(tail, innerTail.seg_inf);
2790
+ }
2783
2791
  } catch {
2784
2792
  }
2785
2793
  }
2794
+ const segments = canDecodePixels ? collectSegments(layers, dimensions, segTail) : [];
2786
2795
  const cleanedArea = typeof tail.decmap === "string" ? parseCleanedAreaOverlay(tail.decmap) : null;
2787
2796
  return {
2788
2797
  mapId: header.mapId,
@@ -2806,6 +2815,18 @@ function applyVacuumPFrame(prev, pframe, opts = {}) {
2806
2815
  const merged = typeof prev === "string" || typeof pframe === "string" ? mergePFrameEnvelope(prev, pframe, opts.prev, opts.pframe) : mergePFrame(prev, pframe);
2807
2816
  return { buffer: merged, data: decodeVacuumMap(merged) };
2808
2817
  }
2818
+ function mergeSegInfNames(tail, innerSegInf) {
2819
+ const outer = tail.seg_inf ?? {};
2820
+ const ids = /* @__PURE__ */ new Set([...Object.keys(outer), ...Object.keys(innerSegInf)]);
2821
+ const merged = {};
2822
+ for (const id of ids) {
2823
+ const o = outer[id] ?? {};
2824
+ const inner = innerSegInf[id] ?? {};
2825
+ const name = typeof o.name === "string" && o.name.length > 0 ? o.name : inner.name;
2826
+ merged[id] = { ...inner, ...o, ...name !== void 0 ? { name } : {} };
2827
+ }
2828
+ return { ...tail, seg_inf: merged };
2829
+ }
2809
2830
  function mergeDimensions(header, tail) {
2810
2831
  const left = tail.origin?.[0] ?? header.left;
2811
2832
  const top = tail.origin?.[1] ?? header.top;
@@ -2966,8 +2987,12 @@ function renderVacuumPng(map, opts = {}) {
2966
2987
  const byType = opts.colorObstaclesByType !== false;
2967
2988
  for (const ob of map.obstacles) {
2968
2989
  const p = worldToPx(ob.x, ob.y, dim, scale);
2969
- const color = byType ? obstacleColor(ob.type) : pal.obstacle;
2970
- drawDiamond(png, p.x, p.y, Math.max(2, scale * 2), color);
2990
+ const r = Math.max(2, scale * 2);
2991
+ if (byType) {
2992
+ drawObstacleShape(png, p.x, p.y, r, ob.type, obstacleColor(ob.type));
2993
+ } else {
2994
+ drawDiamond(png, p.x, p.y, r, pal.obstacle);
2995
+ }
2971
2996
  }
2972
2997
  }
2973
2998
  if (opts.showCharger !== false && map.dock !== null) {
@@ -3128,6 +3153,32 @@ function obstacleColor(type) {
3128
3153
  const hue = Math.abs(Math.trunc(type)) * 47 % 360;
3129
3154
  return hsvToRgba(hue, 0.85, 0.95);
3130
3155
  }
3156
+ function drawSquare(png, cx, cy, r, color) {
3157
+ fillRect(png, cx - r, cy - r, cx + r, cy + r, color);
3158
+ }
3159
+ function drawTriangle(png, cx, cy, r, color) {
3160
+ for (let dy = -r; dy <= r; dy += 1) {
3161
+ const w = Math.round((dy + r) / (2 * r) * r);
3162
+ for (let dx = -w; dx <= w; dx += 1) {
3163
+ setPixel(png, cx + dx, cy + dy, color);
3164
+ }
3165
+ }
3166
+ }
3167
+ function drawObstacleShape(png, cx, cy, r, type, color) {
3168
+ switch (Math.abs(Math.trunc(type)) % 4) {
3169
+ case 0:
3170
+ drawDiamond(png, cx, cy, r, color);
3171
+ return;
3172
+ case 1:
3173
+ drawSquare(png, cx, cy, r, color);
3174
+ return;
3175
+ case 2:
3176
+ drawTriangle(png, cx, cy, r, color);
3177
+ return;
3178
+ default:
3179
+ drawDisk(png, cx, cy, r, color);
3180
+ }
3181
+ }
3131
3182
  var FURNITURE_LINE = [150, 110, 200, 235];
3132
3183
  function paintFurnitureZone(png, points, dim, scale) {
3133
3184
  if (points.length < 2) return;
@@ -4112,6 +4163,12 @@ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
4112
4163
  MowerFault2[MowerFault2["TopCoverOpen"] = 73] = "TopCoverOpen";
4113
4164
  return MowerFault2;
4114
4165
  })(MowerFault || {});
4166
+ function mowerFaultSeverity(code) {
4167
+ if (code === null) return "info";
4168
+ if (code >= 1 && code <= 30 || code === 37 || code === 73) return "error";
4169
+ if (code >= 31 && code <= 36 || code >= 38 && code <= 45) return "warning";
4170
+ return "info";
4171
+ }
4115
4172
 
4116
4173
  // src/models/mower/decode.ts
4117
4174
  function isRecord2(v) {
@@ -4322,6 +4379,37 @@ function parseMowerHeartbeat(value) {
4322
4379
  const taskSubState = mainState === HEARTBEAT_MAIN_STATE_MOWING ? MOWER_TASK_SUBSTATES[subStateRaw - HEARTBEAT_SUBSTATE_BASE] ?? null : null;
4323
4380
  return { rawBattery, mainState, subStateRaw, taskSubState };
4324
4381
  }
4382
+ var POSE_SENTINEL = 206;
4383
+ function poseBytes(value) {
4384
+ if (!Array.isArray(value)) return null;
4385
+ const bytes = [];
4386
+ for (const b of value) {
4387
+ if (typeof b !== "number") return null;
4388
+ bytes.push(b);
4389
+ }
4390
+ return bytes;
4391
+ }
4392
+ function parseMowingProgress(value) {
4393
+ const bytes = poseBytes(value);
4394
+ if (bytes === null || bytes.length < 11) return null;
4395
+ const last = bytes[bytes.length - 1];
4396
+ const head = bytes[0];
4397
+ let offset = null;
4398
+ if (head !== POSE_SENTINEL && last === POSE_SENTINEL) {
4399
+ offset = 0;
4400
+ } else if (head === POSE_SENTINEL && last === POSE_SENTINEL && bytes.length >= 33) {
4401
+ offset = 22;
4402
+ }
4403
+ if (offset === null || offset + 10 > bytes.length) return null;
4404
+ const rawPercent = (bytes[offset + 2] ?? 0) | (bytes[offset + 3] ?? 0) << 8;
4405
+ const total = (bytes[offset + 4] ?? 0) | (bytes[offset + 5] ?? 0) << 8 | (bytes[offset + 6] ?? 0) << 16;
4406
+ const finish = (bytes[offset + 7] ?? 0) | (bytes[offset + 8] ?? 0) << 8 | (bytes[offset + 9] ?? 0) << 16;
4407
+ return {
4408
+ progressPercent: rawPercent ? Math.min(100, rawPercent / 100) : 0,
4409
+ currentAreaSqm: finish / 100,
4410
+ totalAreaSqm: total / 100
4411
+ };
4412
+ }
4325
4413
 
4326
4414
  // src/models/mower/capabilities.ts
4327
4415
  var FALLBACK2 = {
@@ -5021,6 +5109,25 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5021
5109
  get coverageTargetPct() {
5022
5110
  return this.task?.coverageTarget ?? null;
5023
5111
  }
5112
+ /**
5113
+ * Live mowing PROGRESS decoded from the POSE_COVERAGE (1:4) task block —
5114
+ * `{progressPercent, currentAreaSqm, totalAreaSqm}` — or null when 1:4 carries
5115
+ * no task block (idle / pose-only frame). This is the byte-accurate progress
5116
+ * the Dreamehome app shows (vs {@link coverageTargetPct}, the task TARGET).
5117
+ */
5118
+ get mowingProgress() {
5119
+ return parseMowingProgress(
5120
+ this.getProperty(MOWER_PROP.POSE_COVERAGE.siid, MOWER_PROP.POSE_COVERAGE.piid)?.value
5121
+ );
5122
+ }
5123
+ /** Mowing completion percent (0..100) from {@link mowingProgress}, or null. */
5124
+ get mowingProgressPct() {
5125
+ return this.mowingProgress?.progressPercent ?? null;
5126
+ }
5127
+ /** Severity of the current DEVICE_CODE (2:2) — info / warning / error. */
5128
+ get faultSeverity() {
5129
+ return mowerFaultSeverity(this.faultRaw);
5130
+ }
5024
5131
  /** Parsed per-zone control status (2:56), or null. */
5025
5132
  get controlStatus() {
5026
5133
  return parseControlStatus(
@@ -5200,6 +5307,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
5200
5307
  /** Props worth seeding on start() / polling — exported for the facade. */
5201
5308
  static DEFAULT_PROPS = [
5202
5309
  MOWER_PROP.HEARTBEAT,
5310
+ MOWER_PROP.POSE_COVERAGE,
5203
5311
  MOWER_PROP.STATUS,
5204
5312
  MOWER_PROP.DEVICE_CODE,
5205
5313
  MOWER_PROP.BATTERY,
@@ -5858,8 +5966,10 @@ export {
5858
5966
  getVacuumCapabilities,
5859
5967
  isDreameConsumableKey,
5860
5968
  mowerConsumableIndex,
5969
+ mowerFaultSeverity,
5861
5970
  parseMowerConsumables,
5862
5971
  parseMowerHeartbeat,
5972
+ parseMowingProgress,
5863
5973
  renderMowerSvg,
5864
5974
  renderVacuumPng,
5865
5975
  resolveCapabilities,