@apocaliss92/nodedreame 1.12.4 → 1.12.5

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
@@ -892,6 +892,19 @@ declare function encodeAutoSwitchWrite(key: AutoSwitchKey, value: number): strin
892
892
  */
893
893
  /** Stable consumable keys (kebab-case so a consumer can use them verbatim). */
894
894
  type DreameConsumableKey = 'main-brush' | 'side-brush' | 'filter' | 'sensor' | 'tank-filter' | 'mop-pad' | 'silver-ion' | 'detergent' | 'squeegee' | 'deodorizer' | 'wheel' | 'scale-inhibitor' | 'dust-bag';
895
+ /** The unit a consumable's time-left property counts in. */
896
+ type ConsumableTimeLeftUnit = 'hours' | 'days';
897
+ /** A consumable's time-left property: where it lives and what it counts in. */
898
+ interface ConsumableTimeLeftSpec {
899
+ readonly siid: number;
900
+ readonly piid: number;
901
+ readonly unit: ConsumableTimeLeftUnit;
902
+ }
903
+ /** A consumable's time left as the robot reports it. */
904
+ interface ConsumableTimeLeft {
905
+ readonly value: number;
906
+ readonly unit: ConsumableTimeLeftUnit;
907
+ }
895
908
  /** One consumable's wire coordinates: remaining-life property + optional reset action. */
896
909
  interface ConsumableSpec {
897
910
  readonly key: DreameConsumableKey;
@@ -901,6 +914,11 @@ interface ConsumableSpec {
901
914
  readonly siid: number;
902
915
  readonly piid: number;
903
916
  };
917
+ /**
918
+ * The time-left twin on the same service (the other piid), or null when the
919
+ * consumable has none. A reset rewrites it together with {@link life}.
920
+ */
921
+ readonly timeLeft: ConsumableTimeLeftSpec | null;
904
922
  /** "Mark replaced / reset life" action, or null when the model exposes none. */
905
923
  readonly reset: {
906
924
  readonly siid: number;
@@ -910,7 +928,18 @@ interface ConsumableSpec {
910
928
  /**
911
929
  * Every consumable nodedreame knows. The reset action shares the consumable's
912
930
  * service `siid` with `aiid 1` (Tasshack action map). `dust-bag` has a life
913
- * property but no reset action.
931
+ * property but no reset action and no time-left twin.
932
+ *
933
+ * Time-left twins: the service's other piid. Measured against the Dreame app on
934
+ * an r2538z on 2026-09-26 ("Tempo residuo"): main brush 9/1 = 271 h at 90 %,
935
+ * side brush 10/1 = 171 h at 85 %, filter 11/2 = 121 h at 80 %, sensors 16/2 =
936
+ * 1 h at 4 % — each consistent with its life (300/200/150/30 h). Wheel 30/1 and
937
+ * scale inhibitor 31/1 count DAYS (HA units for that robot). The rest are the
938
+ * mapping the camstack Dreame addon already shipped; not measured on a robot
939
+ * that reports them.
940
+ *
941
+ * Labels `Sensor Dirty` / `Wheel Dirty` are the maintenance the app and HA name
942
+ * (clean the sensors / wheels) — a bare "Sensor at 4 %" read as a broken part.
914
943
  */
915
944
  declare const VACUUM_CONSUMABLES: readonly ConsumableSpec[];
916
945
  /** Lookup a consumable spec by key (undefined for an unknown key). */
@@ -927,6 +956,24 @@ interface ConsumableReading {
927
956
  readonly leftPct: number;
928
957
  readonly resettable: boolean;
929
958
  }
959
+ /** Where a post-reset re-read came from: the robot itself, or the cloud shadow. */
960
+ type ConsumableRefreshSource = 'device' | 'cloud-shadow';
961
+ /**
962
+ * The outcome of a consumable reset. The action has SUCCEEDED whenever this
963
+ * resolves; `refreshedFrom` says whether the values after it were re-read.
964
+ * `null` there means both reads failed — `leftPct`/`timeLeft` are then `null`
965
+ * (unknown, not zero) and the cache still holds the pre-reset values.
966
+ * `refreshError` is why the better source was not used: the live read's error
967
+ * when the shadow answered, the shadow's when neither did, else `null`.
968
+ */
969
+ interface ConsumableResetResult {
970
+ readonly key: DreameConsumableKey;
971
+ readonly actionResult: unknown;
972
+ readonly refreshedFrom: ConsumableRefreshSource | null;
973
+ readonly leftPct: number | null;
974
+ readonly timeLeft: ConsumableTimeLeft | null;
975
+ readonly refreshError: Error | null;
976
+ }
930
977
 
931
978
  /**
932
979
  * Per-model vacuum capability records (ported from malard/node-dreame
@@ -1894,18 +1941,33 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
1894
1941
  */
1895
1942
  get supportedConsumables(): readonly ConsumableReading[];
1896
1943
  /**
1897
- * Seed EVERY consumable life property from the cloud shadow (no robot wake) so
1898
- * {@link supportedConsumables} reflects the full set on a docked robot. The
1899
- * consumable life values are static settings the robot rarely re-pushes over
1900
- * MQTT, so a fresh connect surfaces only the actively-changing ones without this.
1944
+ * Time left before a consumable needs attention, in the unit the robot counts
1945
+ * it in (see {@link ConsumableSpec.timeLeft}), or `null` when the model does
1946
+ * not report it or the key has no time-left twin.
1947
+ */
1948
+ consumableTimeLeft(key: DreameConsumableKey): ConsumableTimeLeft | null;
1949
+ /**
1950
+ * Seed EVERY consumable life property AND its time-left twin from the cloud
1951
+ * shadow (no robot wake), in one call, so {@link supportedConsumables} and
1952
+ * {@link consumableTimeLeft} reflect the full set on a docked robot. The
1953
+ * robot rarely re-pushes these over MQTT, so a consumer that wants them fresh
1954
+ * must call this again — the library does not poll them.
1901
1955
  */
1902
1956
  refreshConsumables(): Promise<void>;
1903
1957
  /**
1904
1958
  * Reset (mark replaced → life back to 100%) one consumable via its MIoT reset
1905
- * action. Rejects when the model exposes no reset action for the key (e.g.
1906
- * `dust-bag`) or the key is unknown.
1959
+ * action, then re-read that consumable's life % and time left ONCE so the
1960
+ * cache — and every getter after this resolves — reflects the reset. The
1961
+ * re-read is live; if the robot does not answer it falls back to the cloud
1962
+ * shadow (which may still hold the pre-reset value until the robot reports),
1963
+ * and `refreshedFrom` says which one answered.
1964
+ *
1965
+ * Rejects when the model exposes no reset action for the key (e.g.
1966
+ * `dust-bag`), the key is unknown, or the action itself fails. A failed
1967
+ * RE-READ does not reject: the reset happened, and the result says the values
1968
+ * are unknown (`refreshedFrom: null`).
1907
1969
  */
1908
- resetConsumable(key: DreameConsumableKey): Promise<unknown>;
1970
+ resetConsumable(key: DreameConsumableKey): Promise<ConsumableResetResult>;
1909
1971
  /**
1910
1972
  * The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON
1911
1973
  * string, depending on firmware — or `null` when not yet observed. Prefer the
@@ -2993,15 +3055,15 @@ declare const DeviceDumpSchema: z.ZodObject<{
2993
3055
  generatedAt: number;
2994
3056
  }>;
2995
3057
  }, "strip", z.ZodTypeAny, {
2996
- schemaVersion: 1;
2997
- library: "nodedreame" | "nodewitt";
2998
- libraryVersion: string;
2999
3058
  device: {
3000
3059
  model: string;
3001
3060
  region?: string | undefined;
3002
3061
  type?: string | undefined;
3003
3062
  firmware?: string | undefined;
3004
3063
  };
3064
+ schemaVersion: 1;
3065
+ library: "nodedreame" | "nodewitt";
3066
+ libraryVersion: string;
3005
3067
  observations: {
3006
3068
  properties: Record<string, {
3007
3069
  values: (string | number | boolean)[];
@@ -3040,15 +3102,15 @@ declare const DeviceDumpSchema: z.ZodObject<{
3040
3102
  generatedAt: number;
3041
3103
  };
3042
3104
  }, {
3043
- schemaVersion: 1;
3044
- library: "nodedreame" | "nodewitt";
3045
- libraryVersion: string;
3046
3105
  device: {
3047
3106
  model: string;
3048
3107
  region?: string | undefined;
3049
3108
  type?: string | undefined;
3050
3109
  firmware?: string | undefined;
3051
3110
  };
3111
+ schemaVersion: 1;
3112
+ library: "nodedreame" | "nodewitt";
3113
+ libraryVersion: string;
3052
3114
  observations: {
3053
3115
  properties: Record<string, {
3054
3116
  values: (string | number | boolean)[];
@@ -3841,4 +3903,4 @@ interface ListDevicesInput {
3841
3903
  /** Enumerate the devices visible to the authenticated account (incl. shared). */
3842
3904
  declare function listDevices(input: ListDevicesInput): Promise<DreameDevice[]>;
3843
3905
 
3844
- export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, ALIYUN_REGION_ID, AUTO_SWITCH_JSON_KEY, type AacConfig, type AiDetectionRaw, type AudioInfoEvent, type AutoSwitchKey, type AutoSwitchRaw, type AvcConfig, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type BrokerEndpoint, type CameraStreamHandle, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DRIVE_DIRECTIONS, DefaultCapabilityResolver, type DetectionBox, type DeviceCapabilities, type DeviceConnectivity, type DeviceDump, type DeviceEvent, type DeviceVideoProfile, type DreameAiFeature, DreameApiError, DreameAuthError, DreameCameraController, type DreameCameraControllerInput, DreameCameraStream, type DreameCameraStreamEvents, type DreameCameraStreamInput, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, DreameVideoSession, type DreameVideoSessionInput, type DriveDirection, type DumperOptions, type FrameSource, type FrameSourceFactory, type IotSession, LIBRARY_NAME, LV_STATUS, type ListDevicesInput, LvRtmpClient, type LvRtmpClientOptions, MONITOR_AIID, MONITOR_PIID, MONITOR_SIID, MONITOR_VENDOR_TOKEN, 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 MonitorActionCaller, 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 OaSession, type ObstacleDetection, type OssFetchInput, type OssFetcherLike, PET_SOUNDS, type PersonFollowDetection, type PetSound, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RelayMinter, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type RenderWifiSignalPngOptions, type StateChangedEvent, type StatusEvent, type StreamInfo, type StreamQueryInput, SuctionLevel, TALK_AUDIO_HEADER_G711A, TaskStatus, VACUUM_ACTIONS, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, VACUUM_MOVE, VACUUM_SIID, type VacuumActionKey, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, type VideoAccessToken, type VideoAccessUnitEvent, type VideoRequestInput, type VideoVendor, WaterVolume, type WifiSignalMap, type WifiSignalMapInput, aacSampleRate, aacToAdts, avccToAnnexB, consumableSpec, createClientDumper, createDumper, createIotSession, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, decodeWifiSignalMap, encodeAiFeatureWrite, encodeAutoSwitchWrite, explainNoCameraChannel, extractMowerConsumableValues, getAliyunAuthCode, getDeviceVideoProfile, getMowerCapabilities, getTencentIdentity, getTencentP2PInfo, getVacuumCapabilities, getVideoAccessToken, getVideoFamilyId, hashAccessCode, isAuthRefusedError, isDreameConsumableKey, listDevices, loginByOauth, makeMonitorSession, mowerConsumableIndex, mowerFaultSeverity, parseAacConfig, parseAvcConfig, parseConnectivity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, parseObstacleData, parsePersonFollow, parseRelayUrl, pcm16ToALaw, pcm16leToALaw, remoteDriveValue, renderMowerSvg, renderVacuumPng, renderWifiSignalPng, resolveCapabilities, signApiGatewayRequest, streamQuery, supportedAutoSwitchKeys, toVideoProfile };
3906
+ export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, ALIYUN_REGION_ID, AUTO_SWITCH_JSON_KEY, type AacConfig, type AiDetectionRaw, type AudioInfoEvent, type AutoSwitchKey, type AutoSwitchRaw, type AvcConfig, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type BrokerEndpoint, type CameraStreamHandle, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableRefreshSource, type ConsumableResetResult, type ConsumableSpec, type ConsumableTimeLeft, type ConsumableTimeLeftSpec, type ConsumableTimeLeftUnit, type CreateDeviceArgs, DRIVE_DIRECTIONS, DefaultCapabilityResolver, type DetectionBox, type DeviceCapabilities, type DeviceConnectivity, type DeviceDump, type DeviceEvent, type DeviceVideoProfile, type DreameAiFeature, DreameApiError, DreameAuthError, DreameCameraController, type DreameCameraControllerInput, DreameCameraStream, type DreameCameraStreamEvents, type DreameCameraStreamInput, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, DreameVideoSession, type DreameVideoSessionInput, type DriveDirection, type DumperOptions, type FrameSource, type FrameSourceFactory, type IotSession, LIBRARY_NAME, LV_STATUS, type ListDevicesInput, LvRtmpClient, type LvRtmpClientOptions, MONITOR_AIID, MONITOR_PIID, MONITOR_SIID, MONITOR_VENDOR_TOKEN, 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 MonitorActionCaller, 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 OaSession, type ObstacleDetection, type OssFetchInput, type OssFetcherLike, PET_SOUNDS, type PersonFollowDetection, type PetSound, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RelayMinter, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type RenderWifiSignalPngOptions, type StateChangedEvent, type StatusEvent, type StreamInfo, type StreamQueryInput, SuctionLevel, TALK_AUDIO_HEADER_G711A, TaskStatus, VACUUM_ACTIONS, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, VACUUM_MOVE, VACUUM_SIID, type VacuumActionKey, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, type VideoAccessToken, type VideoAccessUnitEvent, type VideoRequestInput, type VideoVendor, WaterVolume, type WifiSignalMap, type WifiSignalMapInput, aacSampleRate, aacToAdts, avccToAnnexB, consumableSpec, createClientDumper, createDumper, createIotSession, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, decodeWifiSignalMap, encodeAiFeatureWrite, encodeAutoSwitchWrite, explainNoCameraChannel, extractMowerConsumableValues, getAliyunAuthCode, getDeviceVideoProfile, getMowerCapabilities, getTencentIdentity, getTencentP2PInfo, getVacuumCapabilities, getVideoAccessToken, getVideoFamilyId, hashAccessCode, isAuthRefusedError, isDreameConsumableKey, listDevices, loginByOauth, makeMonitorSession, mowerConsumableIndex, mowerFaultSeverity, parseAacConfig, parseAvcConfig, parseConnectivity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, parseObstacleData, parsePersonFollow, parseRelayUrl, pcm16ToALaw, pcm16leToALaw, remoteDriveValue, renderMowerSvg, renderVacuumPng, renderWifiSignalPng, resolveCapabilities, signApiGatewayRequest, streamQuery, supportedAutoSwitchKeys, toVideoProfile };
package/dist/index.d.ts CHANGED
@@ -892,6 +892,19 @@ declare function encodeAutoSwitchWrite(key: AutoSwitchKey, value: number): strin
892
892
  */
893
893
  /** Stable consumable keys (kebab-case so a consumer can use them verbatim). */
894
894
  type DreameConsumableKey = 'main-brush' | 'side-brush' | 'filter' | 'sensor' | 'tank-filter' | 'mop-pad' | 'silver-ion' | 'detergent' | 'squeegee' | 'deodorizer' | 'wheel' | 'scale-inhibitor' | 'dust-bag';
895
+ /** The unit a consumable's time-left property counts in. */
896
+ type ConsumableTimeLeftUnit = 'hours' | 'days';
897
+ /** A consumable's time-left property: where it lives and what it counts in. */
898
+ interface ConsumableTimeLeftSpec {
899
+ readonly siid: number;
900
+ readonly piid: number;
901
+ readonly unit: ConsumableTimeLeftUnit;
902
+ }
903
+ /** A consumable's time left as the robot reports it. */
904
+ interface ConsumableTimeLeft {
905
+ readonly value: number;
906
+ readonly unit: ConsumableTimeLeftUnit;
907
+ }
895
908
  /** One consumable's wire coordinates: remaining-life property + optional reset action. */
896
909
  interface ConsumableSpec {
897
910
  readonly key: DreameConsumableKey;
@@ -901,6 +914,11 @@ interface ConsumableSpec {
901
914
  readonly siid: number;
902
915
  readonly piid: number;
903
916
  };
917
+ /**
918
+ * The time-left twin on the same service (the other piid), or null when the
919
+ * consumable has none. A reset rewrites it together with {@link life}.
920
+ */
921
+ readonly timeLeft: ConsumableTimeLeftSpec | null;
904
922
  /** "Mark replaced / reset life" action, or null when the model exposes none. */
905
923
  readonly reset: {
906
924
  readonly siid: number;
@@ -910,7 +928,18 @@ interface ConsumableSpec {
910
928
  /**
911
929
  * Every consumable nodedreame knows. The reset action shares the consumable's
912
930
  * service `siid` with `aiid 1` (Tasshack action map). `dust-bag` has a life
913
- * property but no reset action.
931
+ * property but no reset action and no time-left twin.
932
+ *
933
+ * Time-left twins: the service's other piid. Measured against the Dreame app on
934
+ * an r2538z on 2026-09-26 ("Tempo residuo"): main brush 9/1 = 271 h at 90 %,
935
+ * side brush 10/1 = 171 h at 85 %, filter 11/2 = 121 h at 80 %, sensors 16/2 =
936
+ * 1 h at 4 % — each consistent with its life (300/200/150/30 h). Wheel 30/1 and
937
+ * scale inhibitor 31/1 count DAYS (HA units for that robot). The rest are the
938
+ * mapping the camstack Dreame addon already shipped; not measured on a robot
939
+ * that reports them.
940
+ *
941
+ * Labels `Sensor Dirty` / `Wheel Dirty` are the maintenance the app and HA name
942
+ * (clean the sensors / wheels) — a bare "Sensor at 4 %" read as a broken part.
914
943
  */
915
944
  declare const VACUUM_CONSUMABLES: readonly ConsumableSpec[];
916
945
  /** Lookup a consumable spec by key (undefined for an unknown key). */
@@ -927,6 +956,24 @@ interface ConsumableReading {
927
956
  readonly leftPct: number;
928
957
  readonly resettable: boolean;
929
958
  }
959
+ /** Where a post-reset re-read came from: the robot itself, or the cloud shadow. */
960
+ type ConsumableRefreshSource = 'device' | 'cloud-shadow';
961
+ /**
962
+ * The outcome of a consumable reset. The action has SUCCEEDED whenever this
963
+ * resolves; `refreshedFrom` says whether the values after it were re-read.
964
+ * `null` there means both reads failed — `leftPct`/`timeLeft` are then `null`
965
+ * (unknown, not zero) and the cache still holds the pre-reset values.
966
+ * `refreshError` is why the better source was not used: the live read's error
967
+ * when the shadow answered, the shadow's when neither did, else `null`.
968
+ */
969
+ interface ConsumableResetResult {
970
+ readonly key: DreameConsumableKey;
971
+ readonly actionResult: unknown;
972
+ readonly refreshedFrom: ConsumableRefreshSource | null;
973
+ readonly leftPct: number | null;
974
+ readonly timeLeft: ConsumableTimeLeft | null;
975
+ readonly refreshError: Error | null;
976
+ }
930
977
 
931
978
  /**
932
979
  * Per-model vacuum capability records (ported from malard/node-dreame
@@ -1894,18 +1941,33 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
1894
1941
  */
1895
1942
  get supportedConsumables(): readonly ConsumableReading[];
1896
1943
  /**
1897
- * Seed EVERY consumable life property from the cloud shadow (no robot wake) so
1898
- * {@link supportedConsumables} reflects the full set on a docked robot. The
1899
- * consumable life values are static settings the robot rarely re-pushes over
1900
- * MQTT, so a fresh connect surfaces only the actively-changing ones without this.
1944
+ * Time left before a consumable needs attention, in the unit the robot counts
1945
+ * it in (see {@link ConsumableSpec.timeLeft}), or `null` when the model does
1946
+ * not report it or the key has no time-left twin.
1947
+ */
1948
+ consumableTimeLeft(key: DreameConsumableKey): ConsumableTimeLeft | null;
1949
+ /**
1950
+ * Seed EVERY consumable life property AND its time-left twin from the cloud
1951
+ * shadow (no robot wake), in one call, so {@link supportedConsumables} and
1952
+ * {@link consumableTimeLeft} reflect the full set on a docked robot. The
1953
+ * robot rarely re-pushes these over MQTT, so a consumer that wants them fresh
1954
+ * must call this again — the library does not poll them.
1901
1955
  */
1902
1956
  refreshConsumables(): Promise<void>;
1903
1957
  /**
1904
1958
  * Reset (mark replaced → life back to 100%) one consumable via its MIoT reset
1905
- * action. Rejects when the model exposes no reset action for the key (e.g.
1906
- * `dust-bag`) or the key is unknown.
1959
+ * action, then re-read that consumable's life % and time left ONCE so the
1960
+ * cache — and every getter after this resolves — reflects the reset. The
1961
+ * re-read is live; if the robot does not answer it falls back to the cloud
1962
+ * shadow (which may still hold the pre-reset value until the robot reports),
1963
+ * and `refreshedFrom` says which one answered.
1964
+ *
1965
+ * Rejects when the model exposes no reset action for the key (e.g.
1966
+ * `dust-bag`), the key is unknown, or the action itself fails. A failed
1967
+ * RE-READ does not reject: the reset happened, and the result says the values
1968
+ * are unknown (`refreshedFrom: null`).
1907
1969
  */
1908
- resetConsumable(key: DreameConsumableKey): Promise<unknown>;
1970
+ resetConsumable(key: DreameConsumableKey): Promise<ConsumableResetResult>;
1909
1971
  /**
1910
1972
  * The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON
1911
1973
  * string, depending on firmware — or `null` when not yet observed. Prefer the
@@ -2993,15 +3055,15 @@ declare const DeviceDumpSchema: z.ZodObject<{
2993
3055
  generatedAt: number;
2994
3056
  }>;
2995
3057
  }, "strip", z.ZodTypeAny, {
2996
- schemaVersion: 1;
2997
- library: "nodedreame" | "nodewitt";
2998
- libraryVersion: string;
2999
3058
  device: {
3000
3059
  model: string;
3001
3060
  region?: string | undefined;
3002
3061
  type?: string | undefined;
3003
3062
  firmware?: string | undefined;
3004
3063
  };
3064
+ schemaVersion: 1;
3065
+ library: "nodedreame" | "nodewitt";
3066
+ libraryVersion: string;
3005
3067
  observations: {
3006
3068
  properties: Record<string, {
3007
3069
  values: (string | number | boolean)[];
@@ -3040,15 +3102,15 @@ declare const DeviceDumpSchema: z.ZodObject<{
3040
3102
  generatedAt: number;
3041
3103
  };
3042
3104
  }, {
3043
- schemaVersion: 1;
3044
- library: "nodedreame" | "nodewitt";
3045
- libraryVersion: string;
3046
3105
  device: {
3047
3106
  model: string;
3048
3107
  region?: string | undefined;
3049
3108
  type?: string | undefined;
3050
3109
  firmware?: string | undefined;
3051
3110
  };
3111
+ schemaVersion: 1;
3112
+ library: "nodedreame" | "nodewitt";
3113
+ libraryVersion: string;
3052
3114
  observations: {
3053
3115
  properties: Record<string, {
3054
3116
  values: (string | number | boolean)[];
@@ -3841,4 +3903,4 @@ interface ListDevicesInput {
3841
3903
  /** Enumerate the devices visible to the authenticated account (incl. shared). */
3842
3904
  declare function listDevices(input: ListDevicesInput): Promise<DreameDevice[]>;
3843
3905
 
3844
- export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, ALIYUN_REGION_ID, AUTO_SWITCH_JSON_KEY, type AacConfig, type AiDetectionRaw, type AudioInfoEvent, type AutoSwitchKey, type AutoSwitchRaw, type AvcConfig, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type BrokerEndpoint, type CameraStreamHandle, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableSpec, type CreateDeviceArgs, DRIVE_DIRECTIONS, DefaultCapabilityResolver, type DetectionBox, type DeviceCapabilities, type DeviceConnectivity, type DeviceDump, type DeviceEvent, type DeviceVideoProfile, type DreameAiFeature, DreameApiError, DreameAuthError, DreameCameraController, type DreameCameraControllerInput, DreameCameraStream, type DreameCameraStreamEvents, type DreameCameraStreamInput, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, DreameVideoSession, type DreameVideoSessionInput, type DriveDirection, type DumperOptions, type FrameSource, type FrameSourceFactory, type IotSession, LIBRARY_NAME, LV_STATUS, type ListDevicesInput, LvRtmpClient, type LvRtmpClientOptions, MONITOR_AIID, MONITOR_PIID, MONITOR_SIID, MONITOR_VENDOR_TOKEN, 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 MonitorActionCaller, 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 OaSession, type ObstacleDetection, type OssFetchInput, type OssFetcherLike, PET_SOUNDS, type PersonFollowDetection, type PetSound, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RelayMinter, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type RenderWifiSignalPngOptions, type StateChangedEvent, type StatusEvent, type StreamInfo, type StreamQueryInput, SuctionLevel, TALK_AUDIO_HEADER_G711A, TaskStatus, VACUUM_ACTIONS, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, VACUUM_MOVE, VACUUM_SIID, type VacuumActionKey, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, type VideoAccessToken, type VideoAccessUnitEvent, type VideoRequestInput, type VideoVendor, WaterVolume, type WifiSignalMap, type WifiSignalMapInput, aacSampleRate, aacToAdts, avccToAnnexB, consumableSpec, createClientDumper, createDumper, createIotSession, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, decodeWifiSignalMap, encodeAiFeatureWrite, encodeAutoSwitchWrite, explainNoCameraChannel, extractMowerConsumableValues, getAliyunAuthCode, getDeviceVideoProfile, getMowerCapabilities, getTencentIdentity, getTencentP2PInfo, getVacuumCapabilities, getVideoAccessToken, getVideoFamilyId, hashAccessCode, isAuthRefusedError, isDreameConsumableKey, listDevices, loginByOauth, makeMonitorSession, mowerConsumableIndex, mowerFaultSeverity, parseAacConfig, parseAvcConfig, parseConnectivity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, parseObstacleData, parsePersonFollow, parseRelayUrl, pcm16ToALaw, pcm16leToALaw, remoteDriveValue, renderMowerSvg, renderVacuumPng, renderWifiSignalPng, resolveCapabilities, signApiGatewayRequest, streamQuery, supportedAutoSwitchKeys, toVideoProfile };
3906
+ export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, ALIYUN_REGION_ID, AUTO_SWITCH_JSON_KEY, type AacConfig, type AiDetectionRaw, type AudioInfoEvent, type AutoSwitchKey, type AutoSwitchRaw, type AvcConfig, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type BrokerEndpoint, type CameraStreamHandle, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type ConsumableReading, type ConsumableRefreshSource, type ConsumableResetResult, type ConsumableSpec, type ConsumableTimeLeft, type ConsumableTimeLeftSpec, type ConsumableTimeLeftUnit, type CreateDeviceArgs, DRIVE_DIRECTIONS, DefaultCapabilityResolver, type DetectionBox, type DeviceCapabilities, type DeviceConnectivity, type DeviceDump, type DeviceEvent, type DeviceVideoProfile, type DreameAiFeature, DreameApiError, DreameAuthError, DreameCameraController, type DreameCameraControllerInput, DreameCameraStream, type DreameCameraStreamEvents, type DreameCameraStreamInput, type DreameCloudState, type DreameConsumableKey, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, DreameVideoSession, type DreameVideoSessionInput, type DriveDirection, type DumperOptions, type FrameSource, type FrameSourceFactory, type IotSession, LIBRARY_NAME, LV_STATUS, type ListDevicesInput, LvRtmpClient, type LvRtmpClientOptions, MONITOR_AIID, MONITOR_PIID, MONITOR_SIID, MONITOR_VENDOR_TOKEN, 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 MonitorActionCaller, 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 OaSession, type ObstacleDetection, type OssFetchInput, type OssFetcherLike, PET_SOUNDS, type PersonFollowDetection, type PetSound, type PropertyChangedEvent, type PropertyResult, type PropertyState, type PropertyWrite, type RelayMinter, type RenderMowerSvgOptions, type RenderVacuumPngOptions, type RenderWifiSignalPngOptions, type StateChangedEvent, type StatusEvent, type StreamInfo, type StreamQueryInput, SuctionLevel, TALK_AUDIO_HEADER_G711A, TaskStatus, VACUUM_ACTIONS, VACUUM_CONSUMABLES, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, VACUUM_MOVE, VACUUM_SIID, type VacuumActionKey, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, type VideoAccessToken, type VideoAccessUnitEvent, type VideoRequestInput, type VideoVendor, WaterVolume, type WifiSignalMap, type WifiSignalMapInput, aacSampleRate, aacToAdts, avccToAnnexB, consumableSpec, createClientDumper, createDumper, createIotSession, decodeAiFeature, decodeAutoSwitch, decodeAutoSwitchAll, decodeWifiSignalMap, encodeAiFeatureWrite, encodeAutoSwitchWrite, explainNoCameraChannel, extractMowerConsumableValues, getAliyunAuthCode, getDeviceVideoProfile, getMowerCapabilities, getTencentIdentity, getTencentP2PInfo, getVacuumCapabilities, getVideoAccessToken, getVideoFamilyId, hashAccessCode, isAuthRefusedError, isDreameConsumableKey, listDevices, loginByOauth, makeMonitorSession, mowerConsumableIndex, mowerFaultSeverity, parseAacConfig, parseAvcConfig, parseConnectivity, parseMowerConsumables, parseMowerHeartbeat, parseMowingProgress, parseObstacleData, parsePersonFollow, parseRelayUrl, pcm16ToALaw, pcm16leToALaw, remoteDriveValue, renderMowerSvg, renderVacuumPng, renderWifiSignalPng, resolveCapabilities, signApiGatewayRequest, streamQuery, supportedAutoSwitchKeys, toVideoProfile };
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.12.4";
3
+ var LIBRARY_VERSION = "1.12.5";
4
4
 
5
5
  // src/transport/errors.ts
6
6
  var DreameError = class extends Error {
@@ -1846,50 +1846,93 @@ var VACUUM_CONSUMABLES = [
1846
1846
  key: "main-brush",
1847
1847
  label: "Main Brush",
1848
1848
  life: { siid: 9, piid: 2 },
1849
+ timeLeft: { siid: 9, piid: 1, unit: "hours" },
1849
1850
  reset: { siid: 9, aiid: 1 }
1850
1851
  },
1851
1852
  {
1852
1853
  key: "side-brush",
1853
1854
  label: "Side Brush",
1854
1855
  life: { siid: 10, piid: 2 },
1856
+ timeLeft: { siid: 10, piid: 1, unit: "hours" },
1855
1857
  reset: { siid: 10, aiid: 1 }
1856
1858
  },
1857
- { key: "filter", label: "Filter", life: { siid: 11, piid: 1 }, reset: { siid: 11, aiid: 1 } },
1858
- { key: "sensor", label: "Sensor", life: { siid: 16, piid: 1 }, reset: { siid: 16, aiid: 1 } },
1859
+ {
1860
+ key: "filter",
1861
+ label: "Filter",
1862
+ life: { siid: 11, piid: 1 },
1863
+ timeLeft: { siid: 11, piid: 2, unit: "hours" },
1864
+ reset: { siid: 11, aiid: 1 }
1865
+ },
1866
+ {
1867
+ key: "sensor",
1868
+ label: "Sensor Dirty",
1869
+ life: { siid: 16, piid: 1 },
1870
+ timeLeft: { siid: 16, piid: 2, unit: "hours" },
1871
+ reset: { siid: 16, aiid: 1 }
1872
+ },
1859
1873
  {
1860
1874
  key: "tank-filter",
1861
1875
  label: "Tank Filter",
1862
1876
  life: { siid: 17, piid: 1 },
1877
+ timeLeft: { siid: 17, piid: 2, unit: "hours" },
1863
1878
  reset: { siid: 17, aiid: 1 }
1864
1879
  },
1865
- { key: "mop-pad", label: "Mop Pad", life: { siid: 18, piid: 1 }, reset: { siid: 18, aiid: 1 } },
1880
+ {
1881
+ key: "mop-pad",
1882
+ label: "Mop Pad",
1883
+ life: { siid: 18, piid: 1 },
1884
+ timeLeft: { siid: 18, piid: 2, unit: "hours" },
1885
+ reset: { siid: 18, aiid: 1 }
1886
+ },
1866
1887
  {
1867
1888
  key: "silver-ion",
1868
1889
  label: "Silver-ion",
1869
1890
  life: { siid: 19, piid: 2 },
1891
+ timeLeft: { siid: 19, piid: 1, unit: "days" },
1870
1892
  reset: { siid: 19, aiid: 1 }
1871
1893
  },
1872
1894
  {
1873
1895
  key: "detergent",
1874
1896
  label: "Detergent",
1875
1897
  life: { siid: 20, piid: 1 },
1898
+ timeLeft: { siid: 20, piid: 2, unit: "days" },
1876
1899
  reset: { siid: 20, aiid: 1 }
1877
1900
  },
1878
- { key: "squeegee", label: "Squeegee", life: { siid: 24, piid: 1 }, reset: { siid: 24, aiid: 1 } },
1901
+ {
1902
+ key: "squeegee",
1903
+ label: "Squeegee",
1904
+ life: { siid: 24, piid: 1 },
1905
+ timeLeft: { siid: 24, piid: 2, unit: "days" },
1906
+ reset: { siid: 24, aiid: 1 }
1907
+ },
1879
1908
  {
1880
1909
  key: "deodorizer",
1881
1910
  label: "Deodorizer",
1882
1911
  life: { siid: 29, piid: 2 },
1912
+ timeLeft: { siid: 29, piid: 1, unit: "days" },
1883
1913
  reset: { siid: 29, aiid: 1 }
1884
1914
  },
1885
- { key: "wheel", label: "Wheel", life: { siid: 30, piid: 2 }, reset: { siid: 30, aiid: 1 } },
1915
+ {
1916
+ key: "wheel",
1917
+ label: "Wheel Dirty",
1918
+ life: { siid: 30, piid: 2 },
1919
+ timeLeft: { siid: 30, piid: 1, unit: "days" },
1920
+ reset: { siid: 30, aiid: 1 }
1921
+ },
1886
1922
  {
1887
1923
  key: "scale-inhibitor",
1888
1924
  label: "Scale Inhibitor",
1889
1925
  life: { siid: 31, piid: 2 },
1926
+ timeLeft: { siid: 31, piid: 1, unit: "days" },
1890
1927
  reset: { siid: 31, aiid: 1 }
1891
1928
  },
1892
- { key: "dust-bag", label: "Dust Bag", life: { siid: 27, piid: 17 }, reset: null }
1929
+ {
1930
+ key: "dust-bag",
1931
+ label: "Dust Bag",
1932
+ life: { siid: 27, piid: 17 },
1933
+ timeLeft: null,
1934
+ reset: null
1935
+ }
1893
1936
  ];
1894
1937
  function consumableSpec(key2) {
1895
1938
  return VACUUM_CONSUMABLES.find((c) => c.key === key2);
@@ -4761,6 +4804,12 @@ var OssFetcher = class {
4761
4804
  };
4762
4805
 
4763
4806
  // src/models/vacuum/vacuum-device.ts
4807
+ function consumableProps(spec) {
4808
+ return spec.timeLeft === null ? [spec.life] : [spec.life, { siid: spec.timeLeft.siid, piid: spec.timeLeft.piid }];
4809
+ }
4810
+ function asError(err) {
4811
+ return err instanceof Error ? err : new Error(String(err));
4812
+ }
4764
4813
  var SUCTION = enumLookup([
4765
4814
  0 /* Quiet */,
4766
4815
  1 /* Standard */,
@@ -4918,25 +4967,72 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
4918
4967
  });
4919
4968
  }
4920
4969
  /**
4921
- * Seed EVERY consumable life property from the cloud shadow (no robot wake) so
4922
- * {@link supportedConsumables} reflects the full set on a docked robot. The
4923
- * consumable life values are static settings the robot rarely re-pushes over
4924
- * MQTT, so a fresh connect surfaces only the actively-changing ones without this.
4970
+ * Time left before a consumable needs attention, in the unit the robot counts
4971
+ * it in (see {@link ConsumableSpec.timeLeft}), or `null` when the model does
4972
+ * not report it or the key has no time-left twin.
4973
+ */
4974
+ consumableTimeLeft(key2) {
4975
+ const spec = consumableSpec(key2);
4976
+ if (spec === void 0 || spec.timeLeft === null) return null;
4977
+ const value = this.#num(spec.timeLeft.siid, spec.timeLeft.piid);
4978
+ return value === null ? null : { value, unit: spec.timeLeft.unit };
4979
+ }
4980
+ /**
4981
+ * Seed EVERY consumable life property AND its time-left twin from the cloud
4982
+ * shadow (no robot wake), in one call, so {@link supportedConsumables} and
4983
+ * {@link consumableTimeLeft} reflect the full set on a docked robot. The
4984
+ * robot rarely re-pushes these over MQTT, so a consumer that wants them fresh
4985
+ * must call this again — the library does not poll them.
4925
4986
  */
4926
4987
  async refreshConsumables() {
4927
- await this.refreshCachedProperties(VACUUM_CONSUMABLES.map((c) => c.life));
4988
+ await this.refreshCachedProperties(VACUUM_CONSUMABLES.flatMap(consumableProps));
4928
4989
  }
4929
4990
  /**
4930
4991
  * Reset (mark replaced → life back to 100%) one consumable via its MIoT reset
4931
- * action. Rejects when the model exposes no reset action for the key (e.g.
4932
- * `dust-bag`) or the key is unknown.
4992
+ * action, then re-read that consumable's life % and time left ONCE so the
4993
+ * cache — and every getter after this resolves — reflects the reset. The
4994
+ * re-read is live; if the robot does not answer it falls back to the cloud
4995
+ * shadow (which may still hold the pre-reset value until the robot reports),
4996
+ * and `refreshedFrom` says which one answered.
4997
+ *
4998
+ * Rejects when the model exposes no reset action for the key (e.g.
4999
+ * `dust-bag`), the key is unknown, or the action itself fails. A failed
5000
+ * RE-READ does not reject: the reset happened, and the result says the values
5001
+ * are unknown (`refreshedFrom: null`).
4933
5002
  */
4934
5003
  async resetConsumable(key2) {
4935
5004
  const spec = consumableSpec(key2);
4936
5005
  if (spec === void 0 || spec.reset === null) {
4937
5006
  throw new DreameError(`resetConsumable: no reset action for consumable "${key2}"`);
4938
5007
  }
4939
- return this.callAction(spec.reset.siid, spec.reset.aiid, []);
5008
+ const actionResult = await this.callAction(spec.reset.siid, spec.reset.aiid, []);
5009
+ const refresh2 = await this.#rereadConsumable(spec);
5010
+ const fresh = refresh2.refreshedFrom !== null;
5011
+ return {
5012
+ key: key2,
5013
+ actionResult,
5014
+ refreshedFrom: refresh2.refreshedFrom,
5015
+ leftPct: fresh ? this.consumableLeftPct(key2) : null,
5016
+ timeLeft: fresh ? this.consumableTimeLeft(key2) : null,
5017
+ refreshError: refresh2.error
5018
+ };
5019
+ }
5020
+ /** One targeted re-read of a consumable's properties: live, else the cloud shadow. */
5021
+ async #rereadConsumable(spec) {
5022
+ const props = consumableProps(spec);
5023
+ let liveError;
5024
+ try {
5025
+ await this.refreshProperties(props);
5026
+ return { refreshedFrom: "device", error: null };
5027
+ } catch (err) {
5028
+ liveError = asError(err);
5029
+ }
5030
+ try {
5031
+ await this.refreshCachedProperties(props);
5032
+ return { refreshedFrom: "cloud-shadow", error: liveError };
5033
+ } catch (err) {
5034
+ return { refreshedFrom: null, error: asError(err) };
5035
+ }
4940
5036
  }
4941
5037
  // -- AI obstacle-detection ----------------------------------------------
4942
5038
  /**
@@ -5304,7 +5400,9 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
5304
5400
  break;
5305
5401
  }
5306
5402
  if (Date.now() + pollMs >= deadline) {
5307
- throw new DreameError("wifi signal map not available (none stored yet, or request timed out)");
5403
+ throw new DreameError(
5404
+ "wifi signal map not available (none stored yet, or request timed out)"
5405
+ );
5308
5406
  }
5309
5407
  await new Promise((r) => setTimeout(r, pollMs));
5310
5408
  }