@apocaliss92/nodedreame 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +167 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -1
- package/dist/index.d.ts +89 -1
- package/dist/index.js +163 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -710,6 +710,51 @@ declare enum TaskStatus {
|
|
|
710
710
|
CustomCleaningWashingPaused = 33
|
|
711
711
|
}
|
|
712
712
|
|
|
713
|
+
/**
|
|
714
|
+
* AI obstacle-detection codec. The robot packs ALL AI-obstacle toggles into a
|
|
715
|
+
* SINGLE MIoT property — `AI_DETECTION` (siid 4 piid 22) — whose value is EITHER
|
|
716
|
+
* an `int` bitmask OR a `str` JSON object, depending on firmware generation.
|
|
717
|
+
* Ported from the Tasshack `dreame-vacuum` HA integration (v2.0.0b25):
|
|
718
|
+
* - INT ←→ `DreameVacuumAIProperty` (bitwise flags)
|
|
719
|
+
* - JSON ←→ `DreameVacuumStrAIProperty` ({ "<key>": bool })
|
|
720
|
+
*
|
|
721
|
+
* This module exposes per-feature decode + a read-modify-write encode that
|
|
722
|
+
* preserves the other features, so a consumer can present each AI toggle as an
|
|
723
|
+
* independent boolean without knowing which on-wire encoding the model uses.
|
|
724
|
+
*/
|
|
725
|
+
/** Canonical AI-obstacle feature keys. A given model exposes a subset. */
|
|
726
|
+
type DreameAiFeature = 'furnitureDetection' | 'obstacleDetection' | 'obstaclePicture' | 'fluidDetection' | 'petDetection' | 'obstacleImageUpload' | 'petAvoidance' | 'fuzzyObstacleDetection' | 'petPicture' | 'petFocusedDetection' | 'largeParticlesBoost' | 'humanDetection';
|
|
727
|
+
/**
|
|
728
|
+
* Bit value in the INT-encoded `AI_DETECTION` (Tasshack `DreameVacuumAIProperty`).
|
|
729
|
+
* A feature absent here has no int representation (JSON-only).
|
|
730
|
+
*/
|
|
731
|
+
declare const AI_FEATURE_BIT: Readonly<Partial<Record<DreameAiFeature, number>>>;
|
|
732
|
+
/**
|
|
733
|
+
* Key in the JSON-string-encoded `AI_DETECTION` (Tasshack
|
|
734
|
+
* `DreameVacuumStrAIProperty`). A feature absent here has no JSON representation.
|
|
735
|
+
*/
|
|
736
|
+
declare const AI_FEATURE_JSON_KEY: Readonly<Partial<Record<DreameAiFeature, string>>>;
|
|
737
|
+
/** The raw on-wire `AI_DETECTION` value: an int bitmask, a JSON string, or absent. */
|
|
738
|
+
type AiDetectionRaw = number | string | null;
|
|
739
|
+
/**
|
|
740
|
+
* Decode ONE AI feature's on/off state from the raw `AI_DETECTION` value,
|
|
741
|
+
* transparently handling both the int-bitmask and JSON-string encodings.
|
|
742
|
+
* Returns `null` when the value is absent, the encoding cannot represent the
|
|
743
|
+
* feature, or the JSON payload omits the key.
|
|
744
|
+
*/
|
|
745
|
+
declare function decodeAiFeature(raw: AiDetectionRaw, feature: DreameAiFeature): boolean | null;
|
|
746
|
+
/**
|
|
747
|
+
* Compute the value to WRITE back to `AI_DETECTION` after toggling ONE feature,
|
|
748
|
+
* preserving the on-wire encoding and the other features. Mirrors Tasshack
|
|
749
|
+
* `set_ai_detection`: an int payload is the full new bitmask; a JSON payload
|
|
750
|
+
* carries ONLY the changed key (the device merges it server-side).
|
|
751
|
+
*
|
|
752
|
+
* Throws when the current value/encoding is unknown (a blind read-modify-write
|
|
753
|
+
* could clobber the other features) or the feature has no representation in the
|
|
754
|
+
* active encoding.
|
|
755
|
+
*/
|
|
756
|
+
declare function encodeAiFeatureWrite(raw: AiDetectionRaw, feature: DreameAiFeature, value: boolean): number | string;
|
|
757
|
+
|
|
713
758
|
/**
|
|
714
759
|
* Per-model vacuum capability records (ported from malard/node-dreame
|
|
715
760
|
* src/capabilities.ts, MIT — attribution retained) plus a resolver that
|
|
@@ -743,6 +788,12 @@ interface VacuumCapabilities {
|
|
|
743
788
|
canMap: boolean;
|
|
744
789
|
supportedSuctionLevels: readonly SuctionLevel[];
|
|
745
790
|
supportedWaterVolumes: readonly WaterVolume[];
|
|
791
|
+
/**
|
|
792
|
+
* The AI-obstacle toggles this model exposes (subset of {@link DreameAiFeature}),
|
|
793
|
+
* each surfaced as an independent boolean from the packed `AI_DETECTION` property.
|
|
794
|
+
* Empty when `hasAiObstacleDetection` is false.
|
|
795
|
+
*/
|
|
796
|
+
supportedAiFeatures: readonly DreameAiFeature[];
|
|
746
797
|
}
|
|
747
798
|
declare const MODEL_CAPABILITIES$1: Readonly<Record<string, VacuumCapabilities>>;
|
|
748
799
|
/** Resolve a model to its rich vacuum capability record (frozen / fallback). */
|
|
@@ -1215,6 +1266,27 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1215
1266
|
get sideBrushLeftPct(): number | null;
|
|
1216
1267
|
get filterLeftPct(): number | null;
|
|
1217
1268
|
get volume(): number | null;
|
|
1269
|
+
/**
|
|
1270
|
+
* The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON
|
|
1271
|
+
* string, depending on firmware — or `null` when not yet observed. Prefer the
|
|
1272
|
+
* decoded {@link aiFeature} / {@link supportedAiFeatures} surface.
|
|
1273
|
+
*/
|
|
1274
|
+
get aiDetectionRaw(): AiDetectionRaw;
|
|
1275
|
+
/** The AI-obstacle toggles this model exposes (from its capability record). */
|
|
1276
|
+
get supportedAiFeatures(): readonly DreameAiFeature[];
|
|
1277
|
+
/**
|
|
1278
|
+
* Read ONE AI-obstacle toggle's on/off state, decoded from the packed
|
|
1279
|
+
* `AI_DETECTION` value (transparent across the int / JSON encodings). `null`
|
|
1280
|
+
* when the value is unobserved or the feature is not representable.
|
|
1281
|
+
*/
|
|
1282
|
+
aiFeature(feature: DreameAiFeature): boolean | null;
|
|
1283
|
+
/**
|
|
1284
|
+
* Toggle ONE AI-obstacle feature, preserving the others via a read-modify-write
|
|
1285
|
+
* of the packed `AI_DETECTION` property (mirrors Tasshack `set_ai_detection`).
|
|
1286
|
+
* Capability-gated on `hasAiObstacleDetection`; rejects when the current value
|
|
1287
|
+
* is unknown (a blind write would clobber the other toggles).
|
|
1288
|
+
*/
|
|
1289
|
+
setAiFeature(feature: DreameAiFeature, value: boolean): Promise<unknown>;
|
|
1218
1290
|
/**
|
|
1219
1291
|
* Start cleaning (MIoT action siid 2 aiid 1). Named `startCleaning` — NOT
|
|
1220
1292
|
* `start` — because `BaseDevice.start()` is the lifecycle method that opens
|
|
@@ -1289,6 +1361,19 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1289
1361
|
* forwarded verbatim.
|
|
1290
1362
|
*/
|
|
1291
1363
|
fetchLatestMap(opts?: Omit<VacuumGetMapInput, 'filename'>): Promise<VacuumMap | null>;
|
|
1364
|
+
/**
|
|
1365
|
+
* Seed {@link mapFilename} from the CLOUD SHADOW (the last value the robot
|
|
1366
|
+
* pushed for siid 6 piid 3) WITHOUT waking it — so a docked/idle robot can
|
|
1367
|
+
* surface its LAST cleaning map without a fresh clean. Emits the usual
|
|
1368
|
+
* `propertyChanged`/`stateChanged` (so a map-watching consumer re-renders) and
|
|
1369
|
+
* returns the resolved {@link mapFilename} (null when the model has no map or
|
|
1370
|
+
* the shadow carries no map path yet).
|
|
1371
|
+
*
|
|
1372
|
+
* NOTE: the shadow holds the last LIVE-PATH filename; its OSS blob may have
|
|
1373
|
+
* expired, in which case a follow-up {@link fetchLatestMap} rejects — treat
|
|
1374
|
+
* that as "no saved map available".
|
|
1375
|
+
*/
|
|
1376
|
+
refreshSavedMapFilename(): Promise<string | null>;
|
|
1292
1377
|
/**
|
|
1293
1378
|
* The current room/segment id, derived from the most-recently-decoded map's
|
|
1294
1379
|
* active-segment set (`sa`). `null` when no map has been fetched or no
|
|
@@ -1334,6 +1419,9 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1334
1419
|
}, {
|
|
1335
1420
|
readonly siid: 4;
|
|
1336
1421
|
readonly piid: 63;
|
|
1422
|
+
}, {
|
|
1423
|
+
readonly siid: 4;
|
|
1424
|
+
readonly piid: 22;
|
|
1337
1425
|
}, {
|
|
1338
1426
|
readonly siid: 3;
|
|
1339
1427
|
readonly piid: 1;
|
|
@@ -2147,4 +2235,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
|
|
|
2147
2235
|
*/
|
|
2148
2236
|
declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
|
|
2149
2237
|
|
|
2150
|
-
export { BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, DreameApiError, DreameAuthError, type DreameCloudState, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, type MapBoundingBox, type MapCleanedAreaOverlay, 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, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, createClientDumper, createDumper, getMowerCapabilities, getVacuumCapabilities, renderMowerSvg, renderVacuumPng, resolveCapabilities };
|
|
2238
|
+
export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, type AiDetectionRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, type MapBoundingBox, type MapCleanedAreaOverlay, 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, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, createClientDumper, createDumper, decodeAiFeature, encodeAiFeatureWrite, getMowerCapabilities, getVacuumCapabilities, renderMowerSvg, renderVacuumPng, resolveCapabilities };
|
package/dist/index.d.ts
CHANGED
|
@@ -710,6 +710,51 @@ declare enum TaskStatus {
|
|
|
710
710
|
CustomCleaningWashingPaused = 33
|
|
711
711
|
}
|
|
712
712
|
|
|
713
|
+
/**
|
|
714
|
+
* AI obstacle-detection codec. The robot packs ALL AI-obstacle toggles into a
|
|
715
|
+
* SINGLE MIoT property — `AI_DETECTION` (siid 4 piid 22) — whose value is EITHER
|
|
716
|
+
* an `int` bitmask OR a `str` JSON object, depending on firmware generation.
|
|
717
|
+
* Ported from the Tasshack `dreame-vacuum` HA integration (v2.0.0b25):
|
|
718
|
+
* - INT ←→ `DreameVacuumAIProperty` (bitwise flags)
|
|
719
|
+
* - JSON ←→ `DreameVacuumStrAIProperty` ({ "<key>": bool })
|
|
720
|
+
*
|
|
721
|
+
* This module exposes per-feature decode + a read-modify-write encode that
|
|
722
|
+
* preserves the other features, so a consumer can present each AI toggle as an
|
|
723
|
+
* independent boolean without knowing which on-wire encoding the model uses.
|
|
724
|
+
*/
|
|
725
|
+
/** Canonical AI-obstacle feature keys. A given model exposes a subset. */
|
|
726
|
+
type DreameAiFeature = 'furnitureDetection' | 'obstacleDetection' | 'obstaclePicture' | 'fluidDetection' | 'petDetection' | 'obstacleImageUpload' | 'petAvoidance' | 'fuzzyObstacleDetection' | 'petPicture' | 'petFocusedDetection' | 'largeParticlesBoost' | 'humanDetection';
|
|
727
|
+
/**
|
|
728
|
+
* Bit value in the INT-encoded `AI_DETECTION` (Tasshack `DreameVacuumAIProperty`).
|
|
729
|
+
* A feature absent here has no int representation (JSON-only).
|
|
730
|
+
*/
|
|
731
|
+
declare const AI_FEATURE_BIT: Readonly<Partial<Record<DreameAiFeature, number>>>;
|
|
732
|
+
/**
|
|
733
|
+
* Key in the JSON-string-encoded `AI_DETECTION` (Tasshack
|
|
734
|
+
* `DreameVacuumStrAIProperty`). A feature absent here has no JSON representation.
|
|
735
|
+
*/
|
|
736
|
+
declare const AI_FEATURE_JSON_KEY: Readonly<Partial<Record<DreameAiFeature, string>>>;
|
|
737
|
+
/** The raw on-wire `AI_DETECTION` value: an int bitmask, a JSON string, or absent. */
|
|
738
|
+
type AiDetectionRaw = number | string | null;
|
|
739
|
+
/**
|
|
740
|
+
* Decode ONE AI feature's on/off state from the raw `AI_DETECTION` value,
|
|
741
|
+
* transparently handling both the int-bitmask and JSON-string encodings.
|
|
742
|
+
* Returns `null` when the value is absent, the encoding cannot represent the
|
|
743
|
+
* feature, or the JSON payload omits the key.
|
|
744
|
+
*/
|
|
745
|
+
declare function decodeAiFeature(raw: AiDetectionRaw, feature: DreameAiFeature): boolean | null;
|
|
746
|
+
/**
|
|
747
|
+
* Compute the value to WRITE back to `AI_DETECTION` after toggling ONE feature,
|
|
748
|
+
* preserving the on-wire encoding and the other features. Mirrors Tasshack
|
|
749
|
+
* `set_ai_detection`: an int payload is the full new bitmask; a JSON payload
|
|
750
|
+
* carries ONLY the changed key (the device merges it server-side).
|
|
751
|
+
*
|
|
752
|
+
* Throws when the current value/encoding is unknown (a blind read-modify-write
|
|
753
|
+
* could clobber the other features) or the feature has no representation in the
|
|
754
|
+
* active encoding.
|
|
755
|
+
*/
|
|
756
|
+
declare function encodeAiFeatureWrite(raw: AiDetectionRaw, feature: DreameAiFeature, value: boolean): number | string;
|
|
757
|
+
|
|
713
758
|
/**
|
|
714
759
|
* Per-model vacuum capability records (ported from malard/node-dreame
|
|
715
760
|
* src/capabilities.ts, MIT — attribution retained) plus a resolver that
|
|
@@ -743,6 +788,12 @@ interface VacuumCapabilities {
|
|
|
743
788
|
canMap: boolean;
|
|
744
789
|
supportedSuctionLevels: readonly SuctionLevel[];
|
|
745
790
|
supportedWaterVolumes: readonly WaterVolume[];
|
|
791
|
+
/**
|
|
792
|
+
* The AI-obstacle toggles this model exposes (subset of {@link DreameAiFeature}),
|
|
793
|
+
* each surfaced as an independent boolean from the packed `AI_DETECTION` property.
|
|
794
|
+
* Empty when `hasAiObstacleDetection` is false.
|
|
795
|
+
*/
|
|
796
|
+
supportedAiFeatures: readonly DreameAiFeature[];
|
|
746
797
|
}
|
|
747
798
|
declare const MODEL_CAPABILITIES$1: Readonly<Record<string, VacuumCapabilities>>;
|
|
748
799
|
/** Resolve a model to its rich vacuum capability record (frozen / fallback). */
|
|
@@ -1215,6 +1266,27 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1215
1266
|
get sideBrushLeftPct(): number | null;
|
|
1216
1267
|
get filterLeftPct(): number | null;
|
|
1217
1268
|
get volume(): number | null;
|
|
1269
|
+
/**
|
|
1270
|
+
* The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON
|
|
1271
|
+
* string, depending on firmware — or `null` when not yet observed. Prefer the
|
|
1272
|
+
* decoded {@link aiFeature} / {@link supportedAiFeatures} surface.
|
|
1273
|
+
*/
|
|
1274
|
+
get aiDetectionRaw(): AiDetectionRaw;
|
|
1275
|
+
/** The AI-obstacle toggles this model exposes (from its capability record). */
|
|
1276
|
+
get supportedAiFeatures(): readonly DreameAiFeature[];
|
|
1277
|
+
/**
|
|
1278
|
+
* Read ONE AI-obstacle toggle's on/off state, decoded from the packed
|
|
1279
|
+
* `AI_DETECTION` value (transparent across the int / JSON encodings). `null`
|
|
1280
|
+
* when the value is unobserved or the feature is not representable.
|
|
1281
|
+
*/
|
|
1282
|
+
aiFeature(feature: DreameAiFeature): boolean | null;
|
|
1283
|
+
/**
|
|
1284
|
+
* Toggle ONE AI-obstacle feature, preserving the others via a read-modify-write
|
|
1285
|
+
* of the packed `AI_DETECTION` property (mirrors Tasshack `set_ai_detection`).
|
|
1286
|
+
* Capability-gated on `hasAiObstacleDetection`; rejects when the current value
|
|
1287
|
+
* is unknown (a blind write would clobber the other toggles).
|
|
1288
|
+
*/
|
|
1289
|
+
setAiFeature(feature: DreameAiFeature, value: boolean): Promise<unknown>;
|
|
1218
1290
|
/**
|
|
1219
1291
|
* Start cleaning (MIoT action siid 2 aiid 1). Named `startCleaning` — NOT
|
|
1220
1292
|
* `start` — because `BaseDevice.start()` is the lifecycle method that opens
|
|
@@ -1289,6 +1361,19 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1289
1361
|
* forwarded verbatim.
|
|
1290
1362
|
*/
|
|
1291
1363
|
fetchLatestMap(opts?: Omit<VacuumGetMapInput, 'filename'>): Promise<VacuumMap | null>;
|
|
1364
|
+
/**
|
|
1365
|
+
* Seed {@link mapFilename} from the CLOUD SHADOW (the last value the robot
|
|
1366
|
+
* pushed for siid 6 piid 3) WITHOUT waking it — so a docked/idle robot can
|
|
1367
|
+
* surface its LAST cleaning map without a fresh clean. Emits the usual
|
|
1368
|
+
* `propertyChanged`/`stateChanged` (so a map-watching consumer re-renders) and
|
|
1369
|
+
* returns the resolved {@link mapFilename} (null when the model has no map or
|
|
1370
|
+
* the shadow carries no map path yet).
|
|
1371
|
+
*
|
|
1372
|
+
* NOTE: the shadow holds the last LIVE-PATH filename; its OSS blob may have
|
|
1373
|
+
* expired, in which case a follow-up {@link fetchLatestMap} rejects — treat
|
|
1374
|
+
* that as "no saved map available".
|
|
1375
|
+
*/
|
|
1376
|
+
refreshSavedMapFilename(): Promise<string | null>;
|
|
1292
1377
|
/**
|
|
1293
1378
|
* The current room/segment id, derived from the most-recently-decoded map's
|
|
1294
1379
|
* active-segment set (`sa`). `null` when no map has been fetched or no
|
|
@@ -1334,6 +1419,9 @@ declare class VacuumDevice extends BaseDevice<VacuumDeviceEvents> {
|
|
|
1334
1419
|
}, {
|
|
1335
1420
|
readonly siid: 4;
|
|
1336
1421
|
readonly piid: 63;
|
|
1422
|
+
}, {
|
|
1423
|
+
readonly siid: 4;
|
|
1424
|
+
readonly piid: 22;
|
|
1337
1425
|
}, {
|
|
1338
1426
|
readonly siid: 3;
|
|
1339
1427
|
readonly piid: 1;
|
|
@@ -2147,4 +2235,4 @@ declare function createDumper(target: DumperDevice, options?: DumperOptions): Du
|
|
|
2147
2235
|
*/
|
|
2148
2236
|
declare function createClientDumper(client: Nodreame, options?: DumperOptions): Dumper[];
|
|
2149
2237
|
|
|
2150
|
-
export { BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, DreameApiError, DreameAuthError, type DreameCloudState, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, type MapBoundingBox, type MapCleanedAreaOverlay, 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, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, createClientDumper, createDumper, getMowerCapabilities, getVacuumCapabilities, renderMowerSvg, renderVacuumPng, resolveCapabilities };
|
|
2238
|
+
export { AI_FEATURE_BIT, AI_FEATURE_JSON_KEY, type AiDetectionRaw, BaseDevice, type BaseDeviceEvents, type BatchDeviceDataFetcher, type CapabilityResolver, ChargingStatus, type CleanOpts, CleaningMode, type CreateDeviceArgs, DefaultCapabilityResolver, type DeviceCapabilities, type DeviceDump, type DeviceEvent, type DreameAiFeature, DreameApiError, DreameAuthError, type DreameCloudState, type DreameDevice, DreameDeviceOfflineError, DreameError, type DreameRegion, type DreameSession, DreameTransportError, type DumperOptions, LIBRARY_NAME, MODEL_CAPABILITIES as MOWER_MODEL_CAPABILITIES, type MapBoundingBox, type MapCleanedAreaOverlay, 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, MODEL_CAPABILITIES$1 as VACUUM_MODEL_CAPABILITIES, type VacuumCapabilities, VacuumCapabilityResolver, VacuumDevice, type VacuumGetMapInput, type VacuumMap, WaterVolume, createClientDumper, createDumper, decodeAiFeature, encodeAiFeatureWrite, getMowerCapabilities, getVacuumCapabilities, renderMowerSvg, renderVacuumPng, resolveCapabilities };
|
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.
|
|
3
|
+
var LIBRARY_VERSION = "1.6.0";
|
|
4
4
|
|
|
5
5
|
// src/transport/errors.ts
|
|
6
6
|
var DreameError = class extends Error {
|
|
@@ -1149,6 +1149,14 @@ var VACUUM_PROP = {
|
|
|
1149
1149
|
CLEANING_MODE: { siid: 4, piid: 23 },
|
|
1150
1150
|
/** VERIFIED r2532a — Child Lock boolean. */
|
|
1151
1151
|
CHILD_LOCK: { siid: 4, piid: 27 },
|
|
1152
|
+
/**
|
|
1153
|
+
* AI obstacle-detection toggle bundle (Tasshack types.py:1609 — `AI_DETECTION`,
|
|
1154
|
+
* siid 4 piid 22). The value packs ALL AI-obstacle switches into ONE property,
|
|
1155
|
+
* encoded as EITHER an int bitmask (`DreameVacuumAIProperty`) OR a JSON string
|
|
1156
|
+
* (`DreameVacuumStrAIProperty`) depending on firmware. Decode/encode per-feature
|
|
1157
|
+
* via `ai-detection.ts` ({@link decodeAiFeature} / {@link encodeAiFeatureWrite}).
|
|
1158
|
+
*/
|
|
1159
|
+
AI_DETECTION: { siid: 4, piid: 22 },
|
|
1152
1160
|
/** VERIFIED r2532a 2026-05-03 — task progress percentage 0..100. */
|
|
1153
1161
|
TASK_PROGRESS_PCT: { siid: 4, piid: 63 },
|
|
1154
1162
|
/** VERIFIED r2532a — mop-drying progress (minutes ticking during MopDrying). */
|
|
@@ -1487,7 +1495,91 @@ function parseFaultList(value) {
|
|
|
1487
1495
|
return codes;
|
|
1488
1496
|
}
|
|
1489
1497
|
|
|
1498
|
+
// src/models/vacuum/ai-detection.ts
|
|
1499
|
+
var AI_FEATURE_BIT = {
|
|
1500
|
+
furnitureDetection: 1,
|
|
1501
|
+
obstacleDetection: 2,
|
|
1502
|
+
obstaclePicture: 4,
|
|
1503
|
+
fluidDetection: 8,
|
|
1504
|
+
petDetection: 16,
|
|
1505
|
+
obstacleImageUpload: 32,
|
|
1506
|
+
// AI_IMAGE = 64 is an internal flag, not a user toggle — intentionally omitted.
|
|
1507
|
+
petAvoidance: 128,
|
|
1508
|
+
fuzzyObstacleDetection: 256,
|
|
1509
|
+
petPicture: 512,
|
|
1510
|
+
petFocusedDetection: 1024,
|
|
1511
|
+
largeParticlesBoost: 2048
|
|
1512
|
+
};
|
|
1513
|
+
var AI_FEATURE_JSON_KEY = {
|
|
1514
|
+
obstacleDetection: "obstacle_detect_switch",
|
|
1515
|
+
obstacleImageUpload: "obstacle_app_display_switch",
|
|
1516
|
+
petDetection: "whether_have_pet",
|
|
1517
|
+
humanDetection: "human_detect_switch",
|
|
1518
|
+
furnitureDetection: "furniture_detect_switch",
|
|
1519
|
+
fluidDetection: "fluid_detect_switch"
|
|
1520
|
+
};
|
|
1521
|
+
function isRecord(v) {
|
|
1522
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
1523
|
+
}
|
|
1524
|
+
function parseJsonPayload(raw) {
|
|
1525
|
+
try {
|
|
1526
|
+
const parsed = JSON.parse(raw);
|
|
1527
|
+
return isRecord(parsed) ? parsed : null;
|
|
1528
|
+
} catch {
|
|
1529
|
+
return null;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function coerceBool(v) {
|
|
1533
|
+
if (typeof v === "boolean") return v;
|
|
1534
|
+
if (v === 1 || v === "1") return true;
|
|
1535
|
+
if (v === 0 || v === "0") return false;
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
function decodeAiFeature(raw, feature) {
|
|
1539
|
+
if (typeof raw === "number") {
|
|
1540
|
+
const bit = AI_FEATURE_BIT[feature];
|
|
1541
|
+
if (bit === void 0) return null;
|
|
1542
|
+
return (raw & bit) === bit;
|
|
1543
|
+
}
|
|
1544
|
+
if (typeof raw === "string") {
|
|
1545
|
+
const key2 = AI_FEATURE_JSON_KEY[feature];
|
|
1546
|
+
if (key2 === void 0) return null;
|
|
1547
|
+
const payload = parseJsonPayload(raw);
|
|
1548
|
+
if (payload === null) return null;
|
|
1549
|
+
return coerceBool(payload[key2]);
|
|
1550
|
+
}
|
|
1551
|
+
return null;
|
|
1552
|
+
}
|
|
1553
|
+
function encodeAiFeatureWrite(raw, feature, value) {
|
|
1554
|
+
if (typeof raw === "number") {
|
|
1555
|
+
const bit = AI_FEATURE_BIT[feature];
|
|
1556
|
+
if (bit === void 0) {
|
|
1557
|
+
throw new Error(`encodeAiFeatureWrite: feature "${feature}" has no int-bitmask representation`);
|
|
1558
|
+
}
|
|
1559
|
+
return value ? raw | bit : raw & ~bit;
|
|
1560
|
+
}
|
|
1561
|
+
if (typeof raw === "string") {
|
|
1562
|
+
const key2 = AI_FEATURE_JSON_KEY[feature];
|
|
1563
|
+
if (key2 === void 0) {
|
|
1564
|
+
throw new Error(`encodeAiFeatureWrite: feature "${feature}" has no JSON-key representation`);
|
|
1565
|
+
}
|
|
1566
|
+
return JSON.stringify({ [key2]: value });
|
|
1567
|
+
}
|
|
1568
|
+
throw new Error(
|
|
1569
|
+
`encodeAiFeatureWrite: AI_DETECTION value not yet known \u2014 cannot safely toggle "${feature}"`
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1490
1573
|
// src/models/vacuum/capabilities.ts
|
|
1574
|
+
var X50_AI_FEATURES = [
|
|
1575
|
+
"obstacleDetection",
|
|
1576
|
+
"petDetection",
|
|
1577
|
+
"furnitureDetection",
|
|
1578
|
+
"fluidDetection",
|
|
1579
|
+
"obstaclePicture",
|
|
1580
|
+
"obstacleImageUpload",
|
|
1581
|
+
"fuzzyObstacleDetection"
|
|
1582
|
+
];
|
|
1491
1583
|
var FALLBACK = {
|
|
1492
1584
|
verified: false,
|
|
1493
1585
|
canMop: false,
|
|
@@ -1516,7 +1608,9 @@ var FALLBACK = {
|
|
|
1516
1608
|
2 /* Intense */,
|
|
1517
1609
|
3 /* Max */
|
|
1518
1610
|
],
|
|
1519
|
-
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */]
|
|
1611
|
+
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */],
|
|
1612
|
+
// An unknown model advertises no AI toggles until its feature set is verified.
|
|
1613
|
+
supportedAiFeatures: []
|
|
1520
1614
|
};
|
|
1521
1615
|
var X50_FAMILY = {
|
|
1522
1616
|
canMop: true,
|
|
@@ -1542,7 +1636,8 @@ var X50_FAMILY = {
|
|
|
1542
1636
|
2 /* Intense */,
|
|
1543
1637
|
3 /* Max */
|
|
1544
1638
|
],
|
|
1545
|
-
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */]
|
|
1639
|
+
supportedWaterVolumes: [1 /* Low */, 2 /* Medium */, 3 /* High */],
|
|
1640
|
+
supportedAiFeatures: X50_AI_FEATURES
|
|
1546
1641
|
};
|
|
1547
1642
|
function deepFreeze(obj) {
|
|
1548
1643
|
if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
|
|
@@ -2676,6 +2771,39 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2676
2771
|
get volume() {
|
|
2677
2772
|
return this.#num(SETTINGS_PROP.VOLUME.siid, SETTINGS_PROP.VOLUME.piid);
|
|
2678
2773
|
}
|
|
2774
|
+
// -- AI obstacle-detection ----------------------------------------------
|
|
2775
|
+
/**
|
|
2776
|
+
* The raw `AI_DETECTION` value (siid 4 piid 22) — an int bitmask or a JSON
|
|
2777
|
+
* string, depending on firmware — or `null` when not yet observed. Prefer the
|
|
2778
|
+
* decoded {@link aiFeature} / {@link supportedAiFeatures} surface.
|
|
2779
|
+
*/
|
|
2780
|
+
get aiDetectionRaw() {
|
|
2781
|
+
const v = this.getProperty(VACUUM_PROP.AI_DETECTION.siid, VACUUM_PROP.AI_DETECTION.piid)?.value;
|
|
2782
|
+
return typeof v === "number" || typeof v === "string" ? v : null;
|
|
2783
|
+
}
|
|
2784
|
+
/** The AI-obstacle toggles this model exposes (from its capability record). */
|
|
2785
|
+
get supportedAiFeatures() {
|
|
2786
|
+
return this.#caps.supportedAiFeatures;
|
|
2787
|
+
}
|
|
2788
|
+
/**
|
|
2789
|
+
* Read ONE AI-obstacle toggle's on/off state, decoded from the packed
|
|
2790
|
+
* `AI_DETECTION` value (transparent across the int / JSON encodings). `null`
|
|
2791
|
+
* when the value is unobserved or the feature is not representable.
|
|
2792
|
+
*/
|
|
2793
|
+
aiFeature(feature) {
|
|
2794
|
+
return decodeAiFeature(this.aiDetectionRaw, feature);
|
|
2795
|
+
}
|
|
2796
|
+
/**
|
|
2797
|
+
* Toggle ONE AI-obstacle feature, preserving the others via a read-modify-write
|
|
2798
|
+
* of the packed `AI_DETECTION` property (mirrors Tasshack `set_ai_detection`).
|
|
2799
|
+
* Capability-gated on `hasAiObstacleDetection`; rejects when the current value
|
|
2800
|
+
* is unknown (a blind write would clobber the other toggles).
|
|
2801
|
+
*/
|
|
2802
|
+
async setAiFeature(feature, value) {
|
|
2803
|
+
this.#requireCap(this.#caps.hasAiObstacleDetection, "setAiFeature", "AI obstacle detection");
|
|
2804
|
+
const next = encodeAiFeatureWrite(this.aiDetectionRaw, feature, value);
|
|
2805
|
+
return this.setProperty({ ...VACUUM_PROP.AI_DETECTION, value: next });
|
|
2806
|
+
}
|
|
2679
2807
|
// -- command helpers ----------------------------------------------------
|
|
2680
2808
|
#resolveCleanOpts(opts) {
|
|
2681
2809
|
const repeats = Math.max(1, Math.trunc(opts.repeats ?? 1));
|
|
@@ -2839,6 +2967,23 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2839
2967
|
if (filename === null) return null;
|
|
2840
2968
|
return this.getMap({ filename, ...opts });
|
|
2841
2969
|
}
|
|
2970
|
+
/**
|
|
2971
|
+
* Seed {@link mapFilename} from the CLOUD SHADOW (the last value the robot
|
|
2972
|
+
* pushed for siid 6 piid 3) WITHOUT waking it — so a docked/idle robot can
|
|
2973
|
+
* surface its LAST cleaning map without a fresh clean. Emits the usual
|
|
2974
|
+
* `propertyChanged`/`stateChanged` (so a map-watching consumer re-renders) and
|
|
2975
|
+
* returns the resolved {@link mapFilename} (null when the model has no map or
|
|
2976
|
+
* the shadow carries no map path yet).
|
|
2977
|
+
*
|
|
2978
|
+
* NOTE: the shadow holds the last LIVE-PATH filename; its OSS blob may have
|
|
2979
|
+
* expired, in which case a follow-up {@link fetchLatestMap} rejects — treat
|
|
2980
|
+
* that as "no saved map available".
|
|
2981
|
+
*/
|
|
2982
|
+
async refreshSavedMapFilename() {
|
|
2983
|
+
if (!this.#caps.canMap) return null;
|
|
2984
|
+
await this.refreshCachedProperties([VACUUM_PROP.MAP_PATH]);
|
|
2985
|
+
return this.mapFilename;
|
|
2986
|
+
}
|
|
2842
2987
|
/**
|
|
2843
2988
|
* The current room/segment id, derived from the most-recently-decoded map's
|
|
2844
2989
|
* active-segment set (`sa`). `null` when no map has been fetched or no
|
|
@@ -2894,6 +3039,7 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
|
|
|
2894
3039
|
VACUUM_PROP.WATER_VOLUME,
|
|
2895
3040
|
VACUUM_PROP.CLEAN_MODE_SETTING,
|
|
2896
3041
|
VACUUM_PROP.TASK_PROGRESS_PCT,
|
|
3042
|
+
VACUUM_PROP.AI_DETECTION,
|
|
2897
3043
|
BATTERY_PROP.LEVEL,
|
|
2898
3044
|
BATTERY_PROP.CHARGING_STATUS,
|
|
2899
3045
|
CONSUMABLE_PROP.MAIN_BRUSH_LEFT,
|
|
@@ -3066,7 +3212,7 @@ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
|
|
|
3066
3212
|
})(MowerFault || {});
|
|
3067
3213
|
|
|
3068
3214
|
// src/models/mower/decode.ts
|
|
3069
|
-
function
|
|
3215
|
+
function isRecord2(v) {
|
|
3070
3216
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3071
3217
|
}
|
|
3072
3218
|
function numArrayOrNull(v) {
|
|
@@ -3083,12 +3229,12 @@ function numArrayOrNull(v) {
|
|
|
3083
3229
|
return out;
|
|
3084
3230
|
}
|
|
3085
3231
|
function parseTaskDescriptor(value) {
|
|
3086
|
-
if (!
|
|
3232
|
+
if (!isRecord2(value)) {
|
|
3087
3233
|
return null;
|
|
3088
3234
|
}
|
|
3089
3235
|
const t = value["t"];
|
|
3090
3236
|
const d = value["d"];
|
|
3091
|
-
if (typeof t !== "string" || !
|
|
3237
|
+
if (typeof t !== "string" || !isRecord2(d)) {
|
|
3092
3238
|
return null;
|
|
3093
3239
|
}
|
|
3094
3240
|
const exe = d["exe"];
|
|
@@ -3117,7 +3263,7 @@ function controlActionFor(code) {
|
|
|
3117
3263
|
return controlLookup(code);
|
|
3118
3264
|
}
|
|
3119
3265
|
function parseControlStatus(value) {
|
|
3120
|
-
if (!
|
|
3266
|
+
if (!isRecord2(value)) {
|
|
3121
3267
|
return null;
|
|
3122
3268
|
}
|
|
3123
3269
|
const status = value["status"];
|
|
@@ -3227,7 +3373,7 @@ var MowerCapabilityResolver = class {
|
|
|
3227
3373
|
// src/models/mower/map/parser.ts
|
|
3228
3374
|
var PATH_SENTINEL_X = 32767;
|
|
3229
3375
|
var PATH_SENTINEL_Y = -32768;
|
|
3230
|
-
function
|
|
3376
|
+
function isRecord3(v) {
|
|
3231
3377
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3232
3378
|
}
|
|
3233
3379
|
function asNumber(v, fallback) {
|
|
@@ -3265,7 +3411,7 @@ function parseSplitPos(info) {
|
|
|
3265
3411
|
return 0;
|
|
3266
3412
|
}
|
|
3267
3413
|
function parsePolygonList(dataMap) {
|
|
3268
|
-
if (!
|
|
3414
|
+
if (!isRecord3(dataMap) || dataMap["dataType"] !== "Map") {
|
|
3269
3415
|
return [];
|
|
3270
3416
|
}
|
|
3271
3417
|
const value = dataMap["value"];
|
|
@@ -3277,7 +3423,7 @@ function extractPathCoords(pathList) {
|
|
|
3277
3423
|
}
|
|
3278
3424
|
const out = [];
|
|
3279
3425
|
for (const p of pathList) {
|
|
3280
|
-
if (
|
|
3426
|
+
if (isRecord3(p) && typeof p["x"] === "number" && typeof p["y"] === "number") {
|
|
3281
3427
|
out.push({ x: p["x"], y: p["y"] });
|
|
3282
3428
|
}
|
|
3283
3429
|
}
|
|
@@ -3312,7 +3458,7 @@ function asEntry(entry) {
|
|
|
3312
3458
|
}
|
|
3313
3459
|
const tuple = entry;
|
|
3314
3460
|
const data = tuple[1];
|
|
3315
|
-
if (!
|
|
3461
|
+
if (!isRecord3(data)) {
|
|
3316
3462
|
return null;
|
|
3317
3463
|
}
|
|
3318
3464
|
return { id: tuple[0], data };
|
|
@@ -3390,7 +3536,7 @@ function parseContours(list) {
|
|
|
3390
3536
|
return out;
|
|
3391
3537
|
}
|
|
3392
3538
|
function parseBoundary(raw) {
|
|
3393
|
-
if (!
|
|
3539
|
+
if (!isRecord3(raw)) {
|
|
3394
3540
|
return null;
|
|
3395
3541
|
}
|
|
3396
3542
|
const { x1, y1, x2, y2 } = raw;
|
|
@@ -3401,7 +3547,7 @@ function parseBoundary(raw) {
|
|
|
3401
3547
|
}
|
|
3402
3548
|
function parseMowerMap(mapJsonStr) {
|
|
3403
3549
|
const data = JSON.parse(mapJsonStr);
|
|
3404
|
-
if (!
|
|
3550
|
+
if (!isRecord3(data)) {
|
|
3405
3551
|
throw new Error("Map JSON is not an object");
|
|
3406
3552
|
}
|
|
3407
3553
|
const mapIndex = asNumber(data["mapIndex"], 0);
|
|
@@ -4570,6 +4716,8 @@ function createClientDumper(client, options) {
|
|
|
4570
4716
|
return client.devices.map((d) => createDumper(d, options));
|
|
4571
4717
|
}
|
|
4572
4718
|
export {
|
|
4719
|
+
AI_FEATURE_BIT,
|
|
4720
|
+
AI_FEATURE_JSON_KEY,
|
|
4573
4721
|
BaseDevice,
|
|
4574
4722
|
ChargingStatus,
|
|
4575
4723
|
CleaningMode,
|
|
@@ -4599,6 +4747,8 @@ export {
|
|
|
4599
4747
|
WaterVolume,
|
|
4600
4748
|
createClientDumper,
|
|
4601
4749
|
createDumper,
|
|
4750
|
+
decodeAiFeature,
|
|
4751
|
+
encodeAiFeatureWrite,
|
|
4602
4752
|
getMowerCapabilities,
|
|
4603
4753
|
getVacuumCapabilities,
|
|
4604
4754
|
renderMowerSvg,
|