@apocaliss92/nodedreame 1.10.0 → 1.11.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.d.cts CHANGED
@@ -1825,6 +1825,30 @@ interface MowerControlState {
1825
1825
  statusCode: number | null;
1826
1826
  zones: number[][];
1827
1827
  }
1828
+ /** Canonical CMS consumable counter keys (blade, brush, robot maintenance). */
1829
+ type MowerConsumableKey = 'blade' | 'brush' | 'maintenance';
1830
+ /** One CMS consumable counter: minutes used + total + remaining %. */
1831
+ interface MowerConsumableReading {
1832
+ readonly key: MowerConsumableKey;
1833
+ /** Minutes of run-time accrued on this counter (counts UP). */
1834
+ readonly usedMinutes: number;
1835
+ /** Counter's full-life duration in minutes. */
1836
+ readonly totalMinutes: number;
1837
+ /** Remaining life percentage (0..100, one decimal). */
1838
+ readonly remainingPercent: number;
1839
+ }
1840
+ /** Map a consumable key (incl. aliases) to its CMS counter index. */
1841
+ declare function mowerConsumableIndex(item: string): number | null;
1842
+ /**
1843
+ * Extract the raw `[blade, brush, robot]` minute counters from a CMS getter
1844
+ * result, or null if the response is malformed. Cast-free, no throw.
1845
+ */
1846
+ declare function extractMowerConsumableValues(result: unknown): number[] | null;
1847
+ /**
1848
+ * Parse a CMS getter result into typed consumable readings (blade/brush/
1849
+ * maintenance) with remaining %. Returns null on a malformed response.
1850
+ */
1851
+ declare function parseMowerConsumables(result: unknown): readonly MowerConsumableReading[] | null;
1828
1852
 
1829
1853
  /**
1830
1854
  * Per-model mower capability records + a resolver adapting the rich record into
@@ -2063,6 +2087,27 @@ declare class MowerDevice extends BaseDevice {
2063
2087
  startMowingEdges(contourIds: number[][]): Promise<unknown>;
2064
2088
  /** Spot mowing (2:50 o:103). */
2065
2089
  startMowingSpots(spotAreaIds: number[]): Promise<unknown>;
2090
+ /**
2091
+ * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),
2092
+ * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This
2093
+ * is a LIVE device action (it wakes the mower); rejects with
2094
+ * {@link DreameDeviceOfflineError} when the mower is unreachable. Returns null
2095
+ * if the response is malformed.
2096
+ */
2097
+ getConsumableValues(): Promise<readonly number[] | null>;
2098
+ /**
2099
+ * Read the CMS consumables as typed readings (blade/brush/maintenance) with
2100
+ * remaining %. LIVE action — see {@link getConsumableValues}. Returns null on
2101
+ * a malformed response.
2102
+ */
2103
+ getConsumables(): Promise<readonly MowerConsumableReading[] | null>;
2104
+ /**
2105
+ * Reset one CMS consumable counter to zero. Reads the current counters, zeroes
2106
+ * the selected one (leaving the others), and writes them back via the
2107
+ * `{m:'s',t:'CMS',d:{value:[…]}}` setter. LIVE action. Throws on an unknown
2108
+ * item or when the current counters cannot be read.
2109
+ */
2110
+ resetConsumable(item: MowerConsumableKey): Promise<void>;
2066
2111
  /**
2067
2112
  * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
2068
2113
  * mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached
@@ -2448,4 +2493,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2448
2493
  */
2449
2494
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2450
2495
 
2451
- 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 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, getMowerCapabilities, getVacuumCapabilities, isDreameConsumableKey, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
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 };
package/dist/index.d.ts CHANGED
@@ -1825,6 +1825,30 @@ interface MowerControlState {
1825
1825
  statusCode: number | null;
1826
1826
  zones: number[][];
1827
1827
  }
1828
+ /** Canonical CMS consumable counter keys (blade, brush, robot maintenance). */
1829
+ type MowerConsumableKey = 'blade' | 'brush' | 'maintenance';
1830
+ /** One CMS consumable counter: minutes used + total + remaining %. */
1831
+ interface MowerConsumableReading {
1832
+ readonly key: MowerConsumableKey;
1833
+ /** Minutes of run-time accrued on this counter (counts UP). */
1834
+ readonly usedMinutes: number;
1835
+ /** Counter's full-life duration in minutes. */
1836
+ readonly totalMinutes: number;
1837
+ /** Remaining life percentage (0..100, one decimal). */
1838
+ readonly remainingPercent: number;
1839
+ }
1840
+ /** Map a consumable key (incl. aliases) to its CMS counter index. */
1841
+ declare function mowerConsumableIndex(item: string): number | null;
1842
+ /**
1843
+ * Extract the raw `[blade, brush, robot]` minute counters from a CMS getter
1844
+ * result, or null if the response is malformed. Cast-free, no throw.
1845
+ */
1846
+ declare function extractMowerConsumableValues(result: unknown): number[] | null;
1847
+ /**
1848
+ * Parse a CMS getter result into typed consumable readings (blade/brush/
1849
+ * maintenance) with remaining %. Returns null on a malformed response.
1850
+ */
1851
+ declare function parseMowerConsumables(result: unknown): readonly MowerConsumableReading[] | null;
1828
1852
 
1829
1853
  /**
1830
1854
  * Per-model mower capability records + a resolver adapting the rich record into
@@ -2063,6 +2087,27 @@ declare class MowerDevice extends BaseDevice {
2063
2087
  startMowingEdges(contourIds: number[][]): Promise<unknown>;
2064
2088
  /** Spot mowing (2:50 o:103). */
2065
2089
  startMowingSpots(spotAreaIds: number[]): Promise<unknown>;
2090
+ /**
2091
+ * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),
2092
+ * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This
2093
+ * is a LIVE device action (it wakes the mower); rejects with
2094
+ * {@link DreameDeviceOfflineError} when the mower is unreachable. Returns null
2095
+ * if the response is malformed.
2096
+ */
2097
+ getConsumableValues(): Promise<readonly number[] | null>;
2098
+ /**
2099
+ * Read the CMS consumables as typed readings (blade/brush/maintenance) with
2100
+ * remaining %. LIVE action — see {@link getConsumableValues}. Returns null on
2101
+ * a malformed response.
2102
+ */
2103
+ getConsumables(): Promise<readonly MowerConsumableReading[] | null>;
2104
+ /**
2105
+ * Reset one CMS consumable counter to zero. Reads the current counters, zeroes
2106
+ * the selected one (leaving the others), and writes them back via the
2107
+ * `{m:'s',t:'CMS',d:{value:[…]}}` setter. LIVE action. Throws on an unknown
2108
+ * item or when the current counters cannot be read.
2109
+ */
2110
+ resetConsumable(item: MowerConsumableKey): Promise<void>;
2066
2111
  /**
2067
2112
  * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
2068
2113
  * mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached
@@ -2448,4 +2493,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
2448
2493
  */
2449
2494
  declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
2450
2495
 
2451
- 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 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, getMowerCapabilities, getVacuumCapabilities, isDreameConsumableKey, renderMowerSvg, renderVacuumPng, resolveCapabilities, supportedAutoSwitchKeys };
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 };
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.10.0";
3
+ var LIBRARY_VERSION = "1.11.1";
4
4
 
5
5
  // src/transport/errors.ts
6
6
  var DreameError = class extends Error {
@@ -616,6 +616,7 @@ var DreamePush = class extends TypedEmitter {
616
616
  #region;
617
617
  #connect;
618
618
  #backoffMs;
619
+ #keepaliveSeconds;
619
620
  #rejectUnauthorized;
620
621
  #topic;
621
622
  #client = null;
@@ -636,6 +637,7 @@ var DreamePush = class extends TypedEmitter {
636
637
  this.#region = input.region;
637
638
  this.#connect = input.connect ?? defaultConnect;
638
639
  this.#backoffMs = input.reconnectBackoffMs ?? 5e3;
640
+ this.#keepaliveSeconds = input.keepaliveSeconds ?? 60;
639
641
  this.#rejectUnauthorized = input.rejectUnauthorized ?? false;
640
642
  this.#topic = buildStatusTopic(this.#device, this.#session.uid, this.#region);
641
643
  this.#onClose = () => {
@@ -693,6 +695,7 @@ var DreamePush = class extends TypedEmitter {
693
695
  reconnectPeriod: 0,
694
696
  // we drive reconnect ourselves
695
697
  connectTimeout: 15e3,
698
+ keepalive: this.#keepaliveSeconds,
696
699
  rejectUnauthorized: this.#rejectUnauthorized,
697
700
  clean: true
698
701
  });
@@ -700,25 +703,37 @@ var DreamePush = class extends TypedEmitter {
700
703
  client.on("message", (_topic, payload) => this.#handleMessage(payload));
701
704
  client.on("error", (err) => this.emit("error", err));
702
705
  client.on("close", this.#onClose);
703
- await new Promise((resolve, reject) => {
704
- const onConnect = () => {
705
- client.removeListener("error", onError);
706
- client.subscribe(this.#topic, { qos: 0 }, (err) => {
707
- if (err) {
708
- reject(new DreameTransportError(`mqtt subscribe failed: ${err.message}`, err));
709
- return;
710
- }
711
- this.emit("connect");
712
- resolve();
713
- });
714
- };
715
- const onError = (err) => {
716
- client.removeListener("connect", onConnect);
717
- reject(new DreameTransportError(`mqtt connect failed: ${err.message}`, err));
718
- };
719
- client.once("connect", onConnect);
720
- client.once("error", onError);
721
- });
706
+ try {
707
+ await new Promise((resolve, reject) => {
708
+ const onConnect = () => {
709
+ client.removeListener("error", onError);
710
+ client.subscribe(this.#topic, { qos: 0 }, (err) => {
711
+ if (err) {
712
+ reject(new DreameTransportError(`mqtt subscribe failed: ${err.message}`, err));
713
+ return;
714
+ }
715
+ this.emit("connect");
716
+ resolve();
717
+ });
718
+ };
719
+ const onError = (err) => {
720
+ client.removeListener("connect", onConnect);
721
+ reject(new DreameTransportError(`mqtt connect failed: ${err.message}`, err));
722
+ };
723
+ client.once("connect", onConnect);
724
+ client.once("error", onError);
725
+ });
726
+ } catch (err) {
727
+ client.removeListener("close", this.#onClose);
728
+ try {
729
+ client.end(true, {}, () => void 0);
730
+ } catch {
731
+ }
732
+ if (this.#client === client) {
733
+ this.#client = null;
734
+ }
735
+ throw err;
736
+ }
722
737
  }
723
738
  #scheduleReconnect() {
724
739
  if (this.#closed || this.#reconnectTimer) {
@@ -3850,6 +3865,19 @@ function buildEdgePayload(contourIds) {
3850
3865
  function buildSpotPayload(spotAreaIds) {
3851
3866
  return { m: "a", p: 0, o: TASK_OPCODE.SPOT, d: { area: [...spotAreaIds] } };
3852
3867
  }
3868
+ function buildGetConsumablePayload() {
3869
+ return { m: "g", t: "CMS" };
3870
+ }
3871
+ function buildSetConsumablePayload(values) {
3872
+ const normalized = values.map((v) => Math.trunc(v));
3873
+ if (normalized.length !== 3) {
3874
+ throw new RangeError(`CMS values must contain exactly 3 counters; got ${normalized.length}`);
3875
+ }
3876
+ if (normalized.some((v) => v < 0)) {
3877
+ throw new RangeError(`CMS values cannot be negative; got ${JSON.stringify(normalized)}`);
3878
+ }
3879
+ return { m: "s", t: "CMS", d: { value: normalized } };
3880
+ }
3853
3881
 
3854
3882
  // src/models/mower/enums.ts
3855
3883
  var MowerStatus = /* @__PURE__ */ ((MowerStatus2) => {
@@ -4041,6 +4069,104 @@ function parseControlStatus(value) {
4041
4069
  zones
4042
4070
  };
4043
4071
  }
4072
+ var MOWER_CONSUMABLES = [
4073
+ { key: "blade", index: 0, totalMinutes: 6e3 },
4074
+ { key: "brush", index: 1, totalMinutes: 3e4 },
4075
+ { key: "maintenance", index: 2, totalMinutes: 3600 }
4076
+ ];
4077
+ function mowerConsumableIndex(item) {
4078
+ switch (item.trim().toLowerCase()) {
4079
+ case "blade":
4080
+ case "blades":
4081
+ return 0;
4082
+ case "brush":
4083
+ case "cleaning_brush":
4084
+ return 1;
4085
+ case "robot":
4086
+ case "maintenance":
4087
+ case "robot_maintenance":
4088
+ return 2;
4089
+ default:
4090
+ return null;
4091
+ }
4092
+ }
4093
+ function toInt(v) {
4094
+ if (typeof v === "number" && Number.isFinite(v)) {
4095
+ return Math.trunc(v);
4096
+ }
4097
+ if (typeof v === "string") {
4098
+ const n = Number(v);
4099
+ return Number.isFinite(n) ? Math.trunc(n) : null;
4100
+ }
4101
+ return null;
4102
+ }
4103
+ function extractCustomActionData(result) {
4104
+ if (!isRecord2(result)) {
4105
+ return null;
4106
+ }
4107
+ if (Array.isArray(result["value"])) {
4108
+ return result;
4109
+ }
4110
+ if (isRecord2(result["d"])) {
4111
+ return result["d"];
4112
+ }
4113
+ const out = result["out"];
4114
+ if (!Array.isArray(out)) {
4115
+ return null;
4116
+ }
4117
+ for (const entry of out) {
4118
+ if (!isRecord2(entry)) {
4119
+ continue;
4120
+ }
4121
+ const r = entry["r"];
4122
+ const code = entry["code"];
4123
+ const rError = r !== void 0 && r !== null && r !== 0;
4124
+ const codeError = code !== void 0 && code !== null && code !== 0;
4125
+ if (rError && codeError) {
4126
+ continue;
4127
+ }
4128
+ if (isRecord2(entry["d"])) {
4129
+ return entry["d"];
4130
+ }
4131
+ }
4132
+ return null;
4133
+ }
4134
+ function extractMowerConsumableValues(result) {
4135
+ const data = extractCustomActionData(result);
4136
+ if (data === null) {
4137
+ return null;
4138
+ }
4139
+ const values = data["value"];
4140
+ if (!Array.isArray(values) || values.length < 3) {
4141
+ return null;
4142
+ }
4143
+ const out = [];
4144
+ for (const v of values.slice(0, 3)) {
4145
+ const n = toInt(v);
4146
+ if (n === null) {
4147
+ return null;
4148
+ }
4149
+ out.push(n);
4150
+ }
4151
+ return out;
4152
+ }
4153
+ function parseMowerConsumables(result) {
4154
+ const values = extractMowerConsumableValues(result);
4155
+ if (values === null) {
4156
+ return null;
4157
+ }
4158
+ return MOWER_CONSUMABLES.map((c) => {
4159
+ const used = values[c.index] ?? 0;
4160
+ const remaining = c.totalMinutes - used;
4161
+ const pct = Math.max(0, Math.min(100, Math.round(remaining / c.totalMinutes * 1e3) / 10));
4162
+ return {
4163
+ key: c.key,
4164
+ usedMinutes: used,
4165
+ totalMinutes: c.totalMinutes,
4166
+ remainingPercent: pct
4167
+ };
4168
+ });
4169
+ }
4044
4170
 
4045
4171
  // src/models/mower/capabilities.ts
4046
4172
  var FALLBACK2 = {
@@ -4806,6 +4932,56 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
4806
4932
  }
4807
4933
  return this.#sendTask(buildSpotPayload(spotAreaIds.map((s) => Math.trunc(s))));
4808
4934
  }
4935
+ // -- CMS consumables ----------------------------------------------------
4936
+ /**
4937
+ * Read the raw CMS consumable counters `[blade, brush, robot]` (minutes used),
4938
+ * via the SCHEDULING_TASK (2:50) custom-action getter `{m:'g',t:'CMS'}`. This
4939
+ * is a LIVE device action (it wakes the mower); rejects with
4940
+ * {@link DreameDeviceOfflineError} when the mower is unreachable. Returns null
4941
+ * if the response is malformed.
4942
+ */
4943
+ async getConsumableValues() {
4944
+ const result = await this.callAction(
4945
+ MOWER_PROP.SCHEDULING_TASK.siid,
4946
+ MOWER_PROP.SCHEDULING_TASK.piid,
4947
+ [buildGetConsumablePayload()]
4948
+ );
4949
+ return extractMowerConsumableValues(result);
4950
+ }
4951
+ /**
4952
+ * Read the CMS consumables as typed readings (blade/brush/maintenance) with
4953
+ * remaining %. LIVE action — see {@link getConsumableValues}. Returns null on
4954
+ * a malformed response.
4955
+ */
4956
+ async getConsumables() {
4957
+ const result = await this.callAction(
4958
+ MOWER_PROP.SCHEDULING_TASK.siid,
4959
+ MOWER_PROP.SCHEDULING_TASK.piid,
4960
+ [buildGetConsumablePayload()]
4961
+ );
4962
+ return parseMowerConsumables(result);
4963
+ }
4964
+ /**
4965
+ * Reset one CMS consumable counter to zero. Reads the current counters, zeroes
4966
+ * the selected one (leaving the others), and writes them back via the
4967
+ * `{m:'s',t:'CMS',d:{value:[…]}}` setter. LIVE action. Throws on an unknown
4968
+ * item or when the current counters cannot be read.
4969
+ */
4970
+ async resetConsumable(item) {
4971
+ const index = mowerConsumableIndex(item);
4972
+ if (index === null) {
4973
+ throw new DreameError(`resetConsumable: unknown consumable item "${item}"`);
4974
+ }
4975
+ const current = await this.getConsumableValues();
4976
+ if (current === null) {
4977
+ throw new DreameError("resetConsumable: failed to read current CMS counters");
4978
+ }
4979
+ const next = [...current];
4980
+ next[index] = 0;
4981
+ await this.callAction(MOWER_PROP.SCHEDULING_TASK.siid, MOWER_PROP.SCHEDULING_TASK.piid, [
4982
+ buildSetConsumablePayload(next)
4983
+ ]);
4984
+ }
4809
4985
  /**
4810
4986
  * Seed the cache from the CLOUD SHADOW (last-known values) WITHOUT waking the
4811
4987
  * mower — reads {@link MowerDevice.DEFAULT_PROPS} from the cloud-cached
@@ -5507,9 +5683,12 @@ export {
5507
5683
  decodeAutoSwitchAll,
5508
5684
  encodeAiFeatureWrite,
5509
5685
  encodeAutoSwitchWrite,
5686
+ extractMowerConsumableValues,
5510
5687
  getMowerCapabilities,
5511
5688
  getVacuumCapabilities,
5512
5689
  isDreameConsumableKey,
5690
+ mowerConsumableIndex,
5691
+ parseMowerConsumables,
5513
5692
  renderMowerSvg,
5514
5693
  renderVacuumPng,
5515
5694
  resolveCapabilities,