@apocaliss92/nodedreame 1.1.0 → 1.3.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 CHANGED
@@ -47,6 +47,7 @@ __export(index_exports, {
47
47
  MowerChargingStatus: () => MowerChargingStatus,
48
48
  MowerControlAction: () => MowerControlAction,
49
49
  MowerDevice: () => MowerDevice,
50
+ MowerFault: () => MowerFault,
50
51
  MowerStatus: () => MowerStatus,
51
52
  MowerTaskStatus: () => MowerTaskStatus,
52
53
  Nodreame: () => Nodreame,
@@ -56,6 +57,8 @@ __export(index_exports, {
56
57
  VacuumCapabilityResolver: () => VacuumCapabilityResolver,
57
58
  VacuumDevice: () => VacuumDevice,
58
59
  WaterVolume: () => WaterVolume,
60
+ createClientDumper: () => createClientDumper,
61
+ createDumper: () => createDumper,
59
62
  getMowerCapabilities: () => getMowerCapabilities,
60
63
  getVacuumCapabilities: () => getVacuumCapabilities,
61
64
  renderMowerSvg: () => renderMowerSvg,
@@ -66,6 +69,7 @@ module.exports = __toCommonJS(index_exports);
66
69
 
67
70
  // src/support/version.ts
68
71
  var LIBRARY_NAME = "nodedreame";
72
+ var LIBRARY_VERSION = "1.3.0";
69
73
 
70
74
  // src/transport/errors.ts
71
75
  var DreameError = class extends Error {
@@ -1292,11 +1296,27 @@ var MiotState = /* @__PURE__ */ ((MiotState2) => {
1292
1296
  MiotState2[MiotState2["ReturnToEmpty"] = 28] = "ReturnToEmpty";
1293
1297
  MiotState2[MiotState2["WaitingForTask"] = 29] = "WaitingForTask";
1294
1298
  MiotState2[MiotState2["CleanWashboardBase"] = 30] = "CleanWashboardBase";
1299
+ MiotState2[MiotState2["ReturningToDrain"] = 31] = "ReturningToDrain";
1300
+ MiotState2[MiotState2["Draining"] = 32] = "Draining";
1295
1301
  MiotState2[MiotState2["AutoWaterDraining"] = 33] = "AutoWaterDraining";
1302
+ MiotState2[MiotState2["Emptying"] = 34] = "Emptying";
1303
+ MiotState2[MiotState2["DustBagDrying"] = 35] = "DustBagDrying";
1304
+ MiotState2[MiotState2["DustBagDryingPaused"] = 36] = "DustBagDryingPaused";
1305
+ MiotState2[MiotState2["HeadingToExtraCleaning"] = 37] = "HeadingToExtraCleaning";
1306
+ MiotState2[MiotState2["ExtraCleaning"] = 38] = "ExtraCleaning";
1307
+ MiotState2[MiotState2["FindingPetPaused"] = 95] = "FindingPetPaused";
1308
+ MiotState2[MiotState2["FindingPet"] = 96] = "FindingPet";
1296
1309
  MiotState2[MiotState2["ShortcutRunning"] = 97] = "ShortcutRunning";
1297
1310
  MiotState2[MiotState2["CameraMonitoring"] = 98] = "CameraMonitoring";
1298
1311
  MiotState2[MiotState2["CameraMonitoringPaused"] = 99] = "CameraMonitoringPaused";
1299
1312
  MiotState2[MiotState2["InitialDeepClean"] = 101] = "InitialDeepClean";
1313
+ MiotState2[MiotState2["InitialDeepCleanPaused"] = 102] = "InitialDeepCleanPaused";
1314
+ MiotState2[MiotState2["Sanitizing"] = 103] = "Sanitizing";
1315
+ MiotState2[MiotState2["SanitizingWithDry"] = 104] = "SanitizingWithDry";
1316
+ MiotState2[MiotState2["ChangingMop"] = 105] = "ChangingMop";
1317
+ MiotState2[MiotState2["ChangingMopPaused"] = 106] = "ChangingMopPaused";
1318
+ MiotState2[MiotState2["FloorMaintaining"] = 107] = "FloorMaintaining";
1319
+ MiotState2[MiotState2["FloorMaintainingPaused"] = 108] = "FloorMaintainingPaused";
1300
1320
  return MiotState2;
1301
1321
  })(MiotState || {});
1302
1322
  var ChargingStatus = /* @__PURE__ */ ((ChargingStatus2) => {
@@ -1328,21 +1348,87 @@ var CleaningMode = /* @__PURE__ */ ((CleaningMode2) => {
1328
1348
  var MiotError = /* @__PURE__ */ ((MiotError2) => {
1329
1349
  MiotError2[MiotError2["Clear"] = 0] = "Clear";
1330
1350
  MiotError2[MiotError2["WheelRotationAnomaly"] = 1] = "WheelRotationAnomaly";
1351
+ MiotError2[MiotError2["Cliff"] = 2] = "Cliff";
1352
+ MiotError2[MiotError2["Bumper"] = 3] = "Bumper";
1353
+ MiotError2[MiotError2["Gesture"] = 4] = "Gesture";
1354
+ MiotError2[MiotError2["BumperRepeat"] = 5] = "BumperRepeat";
1355
+ MiotError2[MiotError2["DropRepeat"] = 6] = "DropRepeat";
1356
+ MiotError2[MiotError2["OpticalFlow"] = 7] = "OpticalFlow";
1357
+ MiotError2[MiotError2["Box"] = 8] = "Box";
1358
+ MiotError2[MiotError2["TankBox"] = 9] = "TankBox";
1359
+ MiotError2[MiotError2["WaterBoxEmpty"] = 10] = "WaterBoxEmpty";
1360
+ MiotError2[MiotError2["BoxFull"] = 11] = "BoxFull";
1361
+ MiotError2[MiotError2["Brush"] = 12] = "Brush";
1362
+ MiotError2[MiotError2["SideBrush"] = 13] = "SideBrush";
1363
+ MiotError2[MiotError2["Fan"] = 14] = "Fan";
1364
+ MiotError2[MiotError2["LeftWheelMotor"] = 15] = "LeftWheelMotor";
1365
+ MiotError2[MiotError2["RightWheelMotor"] = 16] = "RightWheelMotor";
1366
+ MiotError2[MiotError2["TurnSuffocate"] = 17] = "TurnSuffocate";
1331
1367
  MiotError2[MiotError2["RobotLifted"] = 18] = "RobotLifted";
1332
- MiotError2[MiotError2["TaskComplete"] = 68] = "TaskComplete";
1333
- MiotError2[MiotError2["ManualMopInstallRequired"] = 74] = "ManualMopInstallRequired";
1334
- MiotError2[MiotError2["WastewaterTankFull"] = 105] = "WastewaterTankFull";
1335
- MiotError2[MiotError2["CleanWaterTankEmpty"] = 107] = "CleanWaterTankEmpty";
1336
- MiotError2[MiotError2["WashboardFilterNeedsCleaning"] = 114] = "WashboardFilterNeedsCleaning";
1337
- MiotError2[MiotError2["MopPadsMissing"] = 120] = "MopPadsMissing";
1368
+ MiotError2[MiotError2["ChargerGet"] = 19] = "ChargerGet";
1338
1369
  MiotError2[MiotError2["BatteryLow"] = 20] = "BatteryLow";
1339
1370
  MiotError2[MiotError2["ChargeFault"] = 21] = "ChargeFault";
1340
1371
  MiotError2[MiotError2["BatteryPercentageAnomaly"] = 22] = "BatteryPercentageAnomaly";
1372
+ MiotError2[MiotError2["Heart"] = 23] = "Heart";
1373
+ MiotError2[MiotError2["CameraOcclusion"] = 24] = "CameraOcclusion";
1374
+ MiotError2[MiotError2["Move"] = 25] = "Move";
1375
+ MiotError2[MiotError2["FlowShielding"] = 26] = "FlowShielding";
1376
+ MiotError2[MiotError2["InfraredShielding"] = 27] = "InfraredShielding";
1341
1377
  MiotError2[MiotError2["ChargeNoElectric"] = 28] = "ChargeNoElectric";
1342
1378
  MiotError2[MiotError2["BatteryFault"] = 29] = "BatteryFault";
1379
+ MiotError2[MiotError2["FanSpeedError"] = 30] = "FanSpeedError";
1380
+ MiotError2[MiotError2["LeftWheelSpeed"] = 31] = "LeftWheelSpeed";
1381
+ MiotError2[MiotError2["RightWheelSpeed"] = 32] = "RightWheelSpeed";
1382
+ MiotError2[MiotError2["Bmi055Acce"] = 33] = "Bmi055Acce";
1383
+ MiotError2[MiotError2["Bmi055Gyro"] = 34] = "Bmi055Gyro";
1384
+ MiotError2[MiotError2["Xv7001"] = 35] = "Xv7001";
1385
+ MiotError2[MiotError2["LeftMagnet"] = 36] = "LeftMagnet";
1386
+ MiotError2[MiotError2["RightMagnet"] = 37] = "RightMagnet";
1387
+ MiotError2[MiotError2["FlowError"] = 38] = "FlowError";
1388
+ MiotError2[MiotError2["InfraredFault"] = 39] = "InfraredFault";
1389
+ MiotError2[MiotError2["CameraFault"] = 40] = "CameraFault";
1390
+ MiotError2[MiotError2["StrongMagnet"] = 41] = "StrongMagnet";
1391
+ MiotError2[MiotError2["WaterPump"] = 42] = "WaterPump";
1392
+ MiotError2[MiotError2["Rtc"] = 43] = "Rtc";
1393
+ MiotError2[MiotError2["AutoKeyTrig"] = 44] = "AutoKeyTrig";
1394
+ MiotError2[MiotError2["P3v3"] = 45] = "P3v3";
1395
+ MiotError2[MiotError2["CameraIdle"] = 46] = "CameraIdle";
1396
+ MiotError2[MiotError2["Blocked"] = 47] = "Blocked";
1397
+ MiotError2[MiotError2["LdsError"] = 48] = "LdsError";
1398
+ MiotError2[MiotError2["LdsBumper"] = 49] = "LdsBumper";
1399
+ MiotError2[MiotError2["WaterPump2"] = 50] = "WaterPump2";
1400
+ MiotError2[MiotError2["FilterBlocked"] = 51] = "FilterBlocked";
1401
+ MiotError2[MiotError2["Edge"] = 54] = "Edge";
1402
+ MiotError2[MiotError2["Carpet"] = 55] = "Carpet";
1403
+ MiotError2[MiotError2["Laser"] = 56] = "Laser";
1404
+ MiotError2[MiotError2["Edge2"] = 57] = "Edge2";
1405
+ MiotError2[MiotError2["Ultrasonic"] = 58] = "Ultrasonic";
1406
+ MiotError2[MiotError2["NoGoZone"] = 59] = "NoGoZone";
1407
+ MiotError2[MiotError2["Route"] = 61] = "Route";
1408
+ MiotError2[MiotError2["Route2"] = 62] = "Route2";
1409
+ MiotError2[MiotError2["Blocked2"] = 63] = "Blocked2";
1410
+ MiotError2[MiotError2["Blocked3"] = 64] = "Blocked3";
1411
+ MiotError2[MiotError2["Restricted"] = 65] = "Restricted";
1412
+ MiotError2[MiotError2["Restricted2"] = 66] = "Restricted2";
1413
+ MiotError2[MiotError2["Restricted3"] = 67] = "Restricted3";
1414
+ MiotError2[MiotError2["TaskComplete"] = 68] = "TaskComplete";
1415
+ MiotError2[MiotError2["MopRemoved"] = 69] = "MopRemoved";
1416
+ MiotError2[MiotError2["MopRemoved2"] = 70] = "MopRemoved2";
1417
+ MiotError2[MiotError2["MopPadStopRotate"] = 71] = "MopPadStopRotate";
1418
+ MiotError2[MiotError2["MopPadStopRotate2"] = 72] = "MopPadStopRotate2";
1419
+ MiotError2[MiotError2["ManualMopInstallRequired"] = 74] = "ManualMopInstallRequired";
1343
1420
  MiotError2[MiotError2["LowBatteryTurnOff"] = 75] = "LowBatteryTurnOff";
1421
+ MiotError2[MiotError2["DirtyTankNotInstalled"] = 76] = "DirtyTankNotInstalled";
1422
+ MiotError2[MiotError2["RobotInHiddenRoom"] = 78] = "RobotInHiddenRoom";
1423
+ MiotError2[MiotError2["LdsFailedToLift"] = 79] = "LdsFailedToLift";
1344
1424
  MiotError2[MiotError2["RobotStuck"] = 80] = "RobotStuck";
1345
1425
  MiotError2[MiotError2["RobotStuckRepeat"] = 81] = "RobotStuckRepeat";
1426
+ MiotError2[MiotError2["SlipperyFloor"] = 82] = "SlipperyFloor";
1427
+ MiotError2[MiotError2["UnknownError"] = 84] = "UnknownError";
1428
+ MiotError2[MiotError2["CheckMopInstall"] = 85] = "CheckMopInstall";
1429
+ MiotError2[MiotError2["DirtyWaterTankFull"] = 86] = "DirtyWaterTankFull";
1430
+ MiotError2[MiotError2["RetractableLegStuck"] = 88] = "RetractableLegStuck";
1431
+ MiotError2[MiotError2["InternalError"] = 89] = "InternalError";
1346
1432
  MiotError2[MiotError2["RobotStuck2"] = 90] = "RobotStuck2";
1347
1433
  MiotError2[MiotError2["RobotStuckOnTables"] = 91] = "RobotStuckOnTables";
1348
1434
  MiotError2[MiotError2["RobotStuckOnPassage"] = 92] = "RobotStuckOnPassage";
@@ -1353,19 +1439,82 @@ var MiotError = /* @__PURE__ */ ((MiotError2) => {
1353
1439
  MiotError2[MiotError2["RobotStuckOnPet"] = 97] = "RobotStuckOnPet";
1354
1440
  MiotError2[MiotError2["RobotStuckOnSlipperySurface"] = 98] = "RobotStuckOnSlipperySurface";
1355
1441
  MiotError2[MiotError2["RobotStuckOnCarpet"] = 99] = "RobotStuckOnCarpet";
1356
- MiotError2[MiotError2["RobotStuckOnCurtain"] = 200] = "RobotStuckOnCurtain";
1357
1442
  MiotError2[MiotError2["BinFull"] = 101] = "BinFull";
1443
+ MiotError2[MiotError2["BinOpen"] = 102] = "BinOpen";
1444
+ MiotError2[MiotError2["BinOpen2"] = 103] = "BinOpen2";
1445
+ MiotError2[MiotError2["BinFull2"] = 104] = "BinFull2";
1446
+ MiotError2[MiotError2["WastewaterTankFull"] = 105] = "WastewaterTankFull";
1447
+ MiotError2[MiotError2["DirtyWaterTank"] = 106] = "DirtyWaterTank";
1448
+ MiotError2[MiotError2["CleanWaterTankEmpty"] = 107] = "CleanWaterTankEmpty";
1449
+ MiotError2[MiotError2["DirtyWaterTank2"] = 108] = "DirtyWaterTank2";
1450
+ MiotError2[MiotError2["DirtyWaterTankBlocked"] = 109] = "DirtyWaterTankBlocked";
1451
+ MiotError2[MiotError2["DirtyWaterTankPump"] = 110] = "DirtyWaterTankPump";
1452
+ MiotError2[MiotError2["MopPad"] = 111] = "MopPad";
1453
+ MiotError2[MiotError2["WetMopPad"] = 112] = "WetMopPad";
1454
+ MiotError2[MiotError2["WashboardFilterNeedsCleaning"] = 114] = "WashboardFilterNeedsCleaning";
1455
+ MiotError2[MiotError2["CleanTankLevel"] = 116] = "CleanTankLevel";
1358
1456
  MiotError2[MiotError2["StationDisconnected"] = 117] = "StationDisconnected";
1457
+ MiotError2[MiotError2["DirtyTankLevel"] = 118] = "DirtyTankLevel";
1458
+ MiotError2[MiotError2["WashboardLevel"] = 119] = "WashboardLevel";
1459
+ MiotError2[MiotError2["MopPadsMissing"] = 120] = "MopPadsMissing";
1359
1460
  MiotError2[MiotError2["DustBagFull"] = 121] = "DustBagFull";
1461
+ MiotError2[MiotError2["UnknownWarning"] = 122] = "UnknownWarning";
1462
+ MiotError2[MiotError2["SelfTestFailed"] = 123] = "SelfTestFailed";
1463
+ MiotError2[MiotError2["WashboardNotWorking"] = 124] = "WashboardNotWorking";
1464
+ MiotError2[MiotError2["DrainageFailed"] = 125] = "DrainageFailed";
1465
+ MiotError2[MiotError2["MopNotDetected"] = 126] = "MopNotDetected";
1466
+ MiotError2[MiotError2["MopHolderError"] = 127] = "MopHolderError";
1467
+ MiotError2[MiotError2["DockError"] = 128] = "DockError";
1468
+ MiotError2[MiotError2["WashFailed"] = 129] = "WashFailed";
1469
+ MiotError2[MiotError2["RobotStuckOnCurtain"] = 200] = "RobotStuckOnCurtain";
1470
+ MiotError2[MiotError2["EdgeMopStopRotate"] = 201] = "EdgeMopStopRotate";
1471
+ MiotError2[MiotError2["EdgeMopDetached"] = 202] = "EdgeMopDetached";
1472
+ MiotError2[MiotError2["ChassisLiftMalfunction"] = 203] = "ChassisLiftMalfunction";
1473
+ MiotError2[MiotError2["InternalError2"] = 207] = "InternalError2";
1474
+ MiotError2[MiotError2["MopCoverError"] = 209] = "MopCoverError";
1475
+ MiotError2[MiotError2["RollerMopError"] = 210] = "RollerMopError";
1476
+ MiotError2[MiotError2["OnboardWaterTankEmpty"] = 213] = "OnboardWaterTankEmpty";
1477
+ MiotError2[MiotError2["OnboardDirtyWaterTankFull"] = 214] = "OnboardDirtyWaterTankFull";
1478
+ MiotError2[MiotError2["MopNotInstalled"] = 215] = "MopNotInstalled";
1479
+ MiotError2[MiotError2["RollerMopError2"] = 218] = "RollerMopError2";
1480
+ MiotError2[MiotError2["FluffingRollerError"] = 222] = "FluffingRollerError";
1481
+ MiotError2[MiotError2["MopCoverError2"] = 223] = "MopCoverError2";
1482
+ MiotError2[MiotError2["BlockedByObstacle"] = 226] = "BlockedByObstacle";
1483
+ MiotError2[MiotError2["ReturnToChargeFailed"] = 1e3] = "ReturnToChargeFailed";
1360
1484
  return MiotError2;
1361
1485
  })(MiotError || {});
1362
1486
  var TaskStatus = /* @__PURE__ */ ((TaskStatus2) => {
1487
+ TaskStatus2[TaskStatus2["Completed"] = 0] = "Completed";
1363
1488
  TaskStatus2[TaskStatus2["InterruptedOrPaused"] = 1] = "InterruptedOrPaused";
1364
1489
  TaskStatus2[TaskStatus2["Active"] = 2] = "Active";
1365
1490
  TaskStatus2[TaskStatus2["Transitioning"] = 3] = "Transitioning";
1491
+ TaskStatus2[TaskStatus2["SpotCleaning"] = 4] = "SpotCleaning";
1492
+ TaskStatus2[TaskStatus2["FastMapping"] = 5] = "FastMapping";
1366
1493
  TaskStatus2[TaskStatus2["OnDockIdle"] = 6] = "OnDockIdle";
1494
+ TaskStatus2[TaskStatus2["ZoneCleaningPaused"] = 7] = "ZoneCleaningPaused";
1495
+ TaskStatus2[TaskStatus2["SegmentCleaningPaused"] = 8] = "SegmentCleaningPaused";
1496
+ TaskStatus2[TaskStatus2["SpotCleaningPaused"] = 9] = "SpotCleaningPaused";
1497
+ TaskStatus2[TaskStatus2["MapCleaningPaused"] = 10] = "MapCleaningPaused";
1498
+ TaskStatus2[TaskStatus2["DockingPaused"] = 11] = "DockingPaused";
1367
1499
  TaskStatus2[TaskStatus2["TransientPauseEdge"] = 12] = "TransientPauseEdge";
1500
+ TaskStatus2[TaskStatus2["SegmentMoppingPaused"] = 13] = "SegmentMoppingPaused";
1368
1501
  TaskStatus2[TaskStatus2["NeedsIntervention"] = 14] = "NeedsIntervention";
1502
+ TaskStatus2[TaskStatus2["AutoMoppingPaused"] = 15] = "AutoMoppingPaused";
1503
+ TaskStatus2[TaskStatus2["AutoDockingPaused"] = 16] = "AutoDockingPaused";
1504
+ TaskStatus2[TaskStatus2["SegmentDockingPaused"] = 17] = "SegmentDockingPaused";
1505
+ TaskStatus2[TaskStatus2["ZoneDockingPaused"] = 18] = "ZoneDockingPaused";
1506
+ TaskStatus2[TaskStatus2["CruisingPath"] = 20] = "CruisingPath";
1507
+ TaskStatus2[TaskStatus2["CruisingPathPaused"] = 21] = "CruisingPathPaused";
1508
+ TaskStatus2[TaskStatus2["CruisingPoint"] = 22] = "CruisingPoint";
1509
+ TaskStatus2[TaskStatus2["CruisingPointPaused"] = 23] = "CruisingPointPaused";
1510
+ TaskStatus2[TaskStatus2["SummonCleanPaused"] = 24] = "SummonCleanPaused";
1511
+ TaskStatus2[TaskStatus2["ReturningInstallMop"] = 25] = "ReturningInstallMop";
1512
+ TaskStatus2[TaskStatus2["ReturningRemoveMop"] = 26] = "ReturningRemoveMop";
1513
+ TaskStatus2[TaskStatus2["StationCleaning"] = 27] = "StationCleaning";
1514
+ TaskStatus2[TaskStatus2["PetFinding"] = 30] = "PetFinding";
1515
+ TaskStatus2[TaskStatus2["AutoCleaningWashingPaused"] = 31] = "AutoCleaningWashingPaused";
1516
+ TaskStatus2[TaskStatus2["AreaCleaningWashingPaused"] = 32] = "AreaCleaningWashingPaused";
1517
+ TaskStatus2[TaskStatus2["CustomCleaningWashingPaused"] = 33] = "CustomCleaningWashingPaused";
1369
1518
  return TaskStatus2;
1370
1519
  })(TaskStatus || {});
1371
1520
 
@@ -2502,6 +2651,18 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
2502
2651
  get vacuumCapabilities() {
2503
2652
  return this.#caps;
2504
2653
  }
2654
+ /**
2655
+ * The suction levels this model supports, as a {@link SuctionLevel} enum array
2656
+ * (from the capability record). Lets a consumer see which fan speeds are
2657
+ * selectable without reaching into {@link vacuumCapabilities}.
2658
+ */
2659
+ get supportedSuctionLevels() {
2660
+ return this.#caps.supportedSuctionLevels;
2661
+ }
2662
+ /** The water/mop volumes this model supports, as a {@link WaterVolume} enum array. */
2663
+ get supportedWaterVolumes() {
2664
+ return this.#caps.supportedWaterVolumes;
2665
+ }
2505
2666
  #num(siid, piid) {
2506
2667
  return asNum(this.getProperty(siid, piid)?.value);
2507
2668
  }
@@ -2678,11 +2839,11 @@ var VacuumDevice = class _VacuumDevice extends BaseDevice {
2678
2839
  }
2679
2840
  this.#requireCap(this.#caps.canCleanPerRoom, "cleanZones", "per-room cleaning");
2680
2841
  const { repeats, fan, water } = this.#resolveCleanOpts(opts);
2681
- const areas = zones.map((z3) => [
2682
- Math.round(z3.x0),
2683
- Math.round(z3.y0),
2684
- Math.round(z3.x1),
2685
- Math.round(z3.y1),
2842
+ const areas = zones.map((z4) => [
2843
+ Math.round(z4.x0),
2844
+ Math.round(z4.y0),
2845
+ Math.round(z4.x1),
2846
+ Math.round(z4.y1),
2686
2847
  repeats,
2687
2848
  fan,
2688
2849
  water
@@ -2864,6 +3025,83 @@ var MowerTaskStatus = /* @__PURE__ */ ((MowerTaskStatus2) => {
2864
3025
  MowerTaskStatus2[MowerTaskStatus2["SpotIncomplete"] = 7] = "SpotIncomplete";
2865
3026
  return MowerTaskStatus2;
2866
3027
  })(MowerTaskStatus || {});
3028
+ var MowerFault = /* @__PURE__ */ ((MowerFault2) => {
3029
+ MowerFault2[MowerFault2["NoDeviceCode"] = 0] = "NoDeviceCode";
3030
+ MowerFault2[MowerFault2["Tilted"] = 1] = "Tilted";
3031
+ MowerFault2[MowerFault2["Trapped"] = 2] = "Trapped";
3032
+ MowerFault2[MowerFault2["NarrowPathToStation"] = 3] = "NarrowPathToStation";
3033
+ MowerFault2[MowerFault2["LeftWheel"] = 4] = "LeftWheel";
3034
+ MowerFault2[MowerFault2["RightWheel"] = 5] = "RightWheel";
3035
+ MowerFault2[MowerFault2["LiftMotor"] = 6] = "LiftMotor";
3036
+ MowerFault2[MowerFault2["Cutter"] = 7] = "Cutter";
3037
+ MowerFault2[MowerFault2["SidedMotor"] = 8] = "SidedMotor";
3038
+ MowerFault2[MowerFault2["CrashPlate"] = 9] = "CrashPlate";
3039
+ MowerFault2[MowerFault2["Charging"] = 10] = "Charging";
3040
+ MowerFault2[MowerFault2["BatteryOverheat"] = 11] = "BatteryOverheat";
3041
+ MowerFault2[MowerFault2["LidarCovered"] = 12] = "LidarCovered";
3042
+ MowerFault2[MowerFault2["LidarOverheatWithoutMap"] = 13] = "LidarOverheatWithoutMap";
3043
+ MowerFault2[MowerFault2["LidarOverheatWithMap"] = 14] = "LidarOverheatWithMap";
3044
+ MowerFault2[MowerFault2["LidarOverheat"] = 15] = "LidarOverheat";
3045
+ MowerFault2[MowerFault2["LidarDirty"] = 16] = "LidarDirty";
3046
+ MowerFault2[MowerFault2["LidarAbnormal"] = 17] = "LidarAbnormal";
3047
+ MowerFault2[MowerFault2["LocationWeak"] = 18] = "LocationWeak";
3048
+ MowerFault2[MowerFault2["LocationLost"] = 19] = "LocationLost";
3049
+ MowerFault2[MowerFault2["Sensor"] = 20] = "Sensor";
3050
+ MowerFault2[MowerFault2["InForbiddenArea"] = 21] = "InForbiddenArea";
3051
+ MowerFault2[MowerFault2["OutOfMap"] = 22] = "OutOfMap";
3052
+ MowerFault2[MowerFault2["EmergencyStop"] = 23] = "EmergencyStop";
3053
+ MowerFault2[MowerFault2["BatteryLow"] = 24] = "BatteryLow";
3054
+ MowerFault2[MowerFault2["MapFileCrack"] = 25] = "MapFileCrack";
3055
+ MowerFault2[MowerFault2["AwayFromMap"] = 26] = "AwayFromMap";
3056
+ MowerFault2[MowerFault2["HumanDetected"] = 27] = "HumanDetected";
3057
+ MowerFault2[MowerFault2["BladeLoss"] = 28] = "BladeLoss";
3058
+ MowerFault2[MowerFault2["StationLoss"] = 29] = "StationLoss";
3059
+ MowerFault2[MowerFault2["MaintainLoss"] = 30] = "MaintainLoss";
3060
+ MowerFault2[MowerFault2["BackChargeFailed"] = 31] = "BackChargeFailed";
3061
+ MowerFault2[MowerFault2["DockingFailed"] = 32] = "DockingFailed";
3062
+ MowerFault2[MowerFault2["LocatingFailedWithMap"] = 33] = "LocatingFailedWithMap";
3063
+ MowerFault2[MowerFault2["LocatingFailedWithoutMap"] = 34] = "LocatingFailedWithoutMap";
3064
+ MowerFault2[MowerFault2["LocatingAbnormal"] = 35] = "LocatingAbnormal";
3065
+ MowerFault2[MowerFault2["TaskStartFailed"] = 36] = "TaskStartFailed";
3066
+ MowerFault2[MowerFault2["PathImpassable"] = 37] = "PathImpassable";
3067
+ MowerFault2[MowerFault2["LidarDirtyWarning"] = 38] = "LidarDirtyWarning";
3068
+ MowerFault2[MowerFault2["CamDirty"] = 39] = "CamDirty";
3069
+ MowerFault2[MowerFault2["CamAbnormal"] = 40] = "CamAbnormal";
3070
+ MowerFault2[MowerFault2["CamCover"] = 41] = "CamCover";
3071
+ MowerFault2[MowerFault2["BatteryOverheatWarning"] = 42] = "BatteryOverheatWarning";
3072
+ MowerFault2[MowerFault2["BatteryTempLow"] = 43] = "BatteryTempLow";
3073
+ MowerFault2[MowerFault2["AutobuildBorder"] = 44] = "AutobuildBorder";
3074
+ MowerFault2[MowerFault2["AutobuildSide"] = 45] = "AutobuildSide";
3075
+ MowerFault2[MowerFault2["BorderFinish"] = 46] = "BorderFinish";
3076
+ MowerFault2[MowerFault2["NewMap"] = 47] = "NewMap";
3077
+ MowerFault2[MowerFault2["TaskFinish"] = 48] = "TaskFinish";
3078
+ MowerFault2[MowerFault2["DestinationNotReachable"] = 49] = "DestinationNotReachable";
3079
+ MowerFault2[MowerFault2["TaskStart"] = 50] = "TaskStart";
3080
+ MowerFault2[MowerFault2["CruiseStart"] = 51] = "CruiseStart";
3081
+ MowerFault2[MowerFault2["PointAndGoStart"] = 52] = "PointAndGoStart";
3082
+ MowerFault2[MowerFault2["ScheduleStart"] = 53] = "ScheduleStart";
3083
+ MowerFault2[MowerFault2["BatteryLowReturning"] = 54] = "BatteryLowReturning";
3084
+ MowerFault2[MowerFault2["BatteryLowScheduleSuspend"] = 55] = "BatteryLowScheduleSuspend";
3085
+ MowerFault2[MowerFault2["BadWeatherProtecting"] = 56] = "BadWeatherProtecting";
3086
+ MowerFault2[MowerFault2["RainScheduleInterupted"] = 57] = "RainScheduleInterupted";
3087
+ MowerFault2[MowerFault2["RainScheduleSuspend"] = 58] = "RainScheduleSuspend";
3088
+ MowerFault2[MowerFault2["ForzenReturning"] = 59] = "ForzenReturning";
3089
+ MowerFault2[MowerFault2["FrozenScheduleSuspend"] = 60] = "FrozenScheduleSuspend";
3090
+ MowerFault2[MowerFault2["NotDisturbReturning"] = 61] = "NotDisturbReturning";
3091
+ MowerFault2[MowerFault2["NotDisturbScheduleSuspend"] = 62] = "NotDisturbScheduleSuspend";
3092
+ MowerFault2[MowerFault2["WorkingScheduleSuspend"] = 63] = "WorkingScheduleSuspend";
3093
+ MowerFault2[MowerFault2["RemoteControlingScheduleSuspend"] = 64] = "RemoteControlingScheduleSuspend";
3094
+ MowerFault2[MowerFault2["EmergencyStoppedScheduleSuspend"] = 65] = "EmergencyStoppedScheduleSuspend";
3095
+ MowerFault2[MowerFault2["TopCoverOpenScheduleSuspend"] = 66] = "TopCoverOpenScheduleSuspend";
3096
+ MowerFault2[MowerFault2["FaultModeScheduleSuspend"] = 67] = "FaultModeScheduleSuspend";
3097
+ MowerFault2[MowerFault2["ScheduleTimeout"] = 68] = "ScheduleTimeout";
3098
+ MowerFault2[MowerFault2["StationNotConnectedToWorkingArea"] = 69] = "StationNotConnectedToWorkingArea";
3099
+ MowerFault2[MowerFault2["ContinueFromBreakpoint"] = 70] = "ContinueFromBreakpoint";
3100
+ MowerFault2[MowerFault2["IdleTimeoutReturning"] = 71] = "IdleTimeoutReturning";
3101
+ MowerFault2[MowerFault2["PauseTimeoutReturning"] = 72] = "PauseTimeoutReturning";
3102
+ MowerFault2[MowerFault2["TopCoverOpen"] = 73] = "TopCoverOpen";
3103
+ return MowerFault2;
3104
+ })(MowerFault || {});
2867
3105
 
2868
3106
  // src/models/mower/decode.ts
2869
3107
  function isRecord(v) {
@@ -3543,6 +3781,12 @@ var STATUS = enumLookup(
3543
3781
  var CHARGING2 = enumLookup(
3544
3782
  Object.values(MowerChargingStatus).filter((v) => typeof v === "number")
3545
3783
  );
3784
+ var TASK_STATUS = enumLookup(
3785
+ Object.values(MowerTaskStatus).filter((v) => typeof v === "number")
3786
+ );
3787
+ var FAULT = enumLookup(
3788
+ Object.values(MowerFault).filter((v) => typeof v === "number")
3789
+ );
3546
3790
  var MowerDevice = class _MowerDevice extends BaseDevice {
3547
3791
  #caps;
3548
3792
  #fetchBatch;
@@ -3591,6 +3835,25 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
3591
3835
  get taskStatusRaw() {
3592
3836
  return this.#num(MOWER_PROP.TASK_STATUS.siid, MOWER_PROP.TASK_STATUS.piid);
3593
3837
  }
3838
+ /**
3839
+ * TASK_STATUS (5:104) decoded to {@link MowerTaskStatus}, or null. The donor
3840
+ * names only code 7 (SpotIncomplete); codes 2/3/10/13 are documented "Unknown
3841
+ * task status" and intentionally surface as raw via {@link taskStatusRaw}.
3842
+ */
3843
+ get taskStatus() {
3844
+ return TASK_STATUS(this.taskStatusRaw);
3845
+ }
3846
+ get faultRaw() {
3847
+ return this.#num(MOWER_PROP.DEVICE_CODE.siid, MOWER_PROP.DEVICE_CODE.piid);
3848
+ }
3849
+ /**
3850
+ * DEVICE_CODE (2:2) decoded to a {@link MowerFault} (the donor's
3851
+ * BASE_DEVICE_CODES registry, 0..73), or null for a model-specific/undocumented
3852
+ * code — which still surfaces as the raw number via {@link faultRaw}.
3853
+ */
3854
+ get fault() {
3855
+ return FAULT(this.faultRaw);
3856
+ }
3594
3857
  /** Parsed scheduling task descriptor (2:50), or null. */
3595
3858
  get task() {
3596
3859
  return parseTaskDescriptor(
@@ -3665,7 +3928,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
3665
3928
  if (zoneIds.length === 0) {
3666
3929
  throw new RangeError("startMowingZones: zoneIds must not be empty");
3667
3930
  }
3668
- return this.#sendTask(buildZonePayload(zoneIds.map((z3) => Math.trunc(z3))));
3931
+ return this.#sendTask(buildZonePayload(zoneIds.map((z4) => Math.trunc(z4))));
3669
3932
  }
3670
3933
  /** Edge / contour mowing (2:50 o:101). Contour ids are two-int pairs [[1,0]]. */
3671
3934
  async startMowingEdges(contourIds) {
@@ -3733,6 +3996,7 @@ var MowerDevice = class _MowerDevice extends BaseDevice {
3733
3996
  /** Props worth seeding on start() / polling — exported for the facade. */
3734
3997
  static DEFAULT_PROPS = [
3735
3998
  MOWER_PROP.STATUS,
3999
+ MOWER_PROP.DEVICE_CODE,
3736
4000
  MOWER_PROP.BATTERY,
3737
4001
  MOWER_PROP.CHARGING_STATUS,
3738
4002
  MOWER_PROP.TASK_STATUS,
@@ -3888,6 +4152,429 @@ var Nodreame = class extends TypedEmitter {
3888
4152
  await Promise.all(this.#devices.map((d) => d.applySession(session)));
3889
4153
  }
3890
4154
  };
4155
+
4156
+ // src/diagnostics/redact.ts
4157
+ var REDACTED = "[redacted]";
4158
+ var SENSITIVE_KEY_FRAGMENTS = [
4159
+ // identity / secrets
4160
+ "did",
4161
+ "uid",
4162
+ "token",
4163
+ // accessToken, refreshToken, refresh_token, token_type stripped too (safe)
4164
+ "mac",
4165
+ "serial",
4166
+ "email",
4167
+ "account",
4168
+ "password",
4169
+ "passwd",
4170
+ "secret",
4171
+ "authorization",
4172
+ "auth",
4173
+ "credential",
4174
+ "apikey",
4175
+ "api_key",
4176
+ "clientid",
4177
+ "client_id",
4178
+ // location / PII
4179
+ "gps",
4180
+ "coordinate",
4181
+ "latitude",
4182
+ "longitude",
4183
+ "lat",
4184
+ "lon",
4185
+ "lng",
4186
+ "ssid",
4187
+ "wifi",
4188
+ "bssid",
4189
+ "ipaddr",
4190
+ "localip",
4191
+ "binddomain",
4192
+ "host",
4193
+ "address",
4194
+ "room",
4195
+ "area_name",
4196
+ "areaname",
4197
+ "segmentname",
4198
+ "segment_name",
4199
+ // map binary / geometry (location-revealing)
4200
+ "map_info",
4201
+ "mapinfo",
4202
+ "mapblob",
4203
+ // free-text device names that may carry PII. NOTE: the bare fragment `name` is
4204
+ // intentionally NOT listed — it would over-match the catalog's command `name`
4205
+ // field (an enum-derived, non-sensitive label). The specific custom-name
4206
+ // fields below cover every PII-bearing case.
4207
+ "customname",
4208
+ "devicename",
4209
+ "nickname"
4210
+ ];
4211
+ var EXACT_SENSITIVE_KEYS = /* @__PURE__ */ new Set(["ip"]);
4212
+ function isSensitiveKey(key2) {
4213
+ const lower = key2.toLowerCase();
4214
+ if (EXACT_SENSITIVE_KEYS.has(lower)) {
4215
+ return true;
4216
+ }
4217
+ return SENSITIVE_KEY_FRAGMENTS.some((frag) => lower.includes(frag));
4218
+ }
4219
+ function isPlainObject(v) {
4220
+ return typeof v === "object" && v !== null && !Array.isArray(v);
4221
+ }
4222
+ function redact(value) {
4223
+ if (Array.isArray(value)) {
4224
+ return value.map((v) => redact(v));
4225
+ }
4226
+ if (isPlainObject(value)) {
4227
+ const out = {};
4228
+ for (const [k, v] of Object.entries(value)) {
4229
+ out[k] = isSensitiveKey(k) ? REDACTED : redact(v);
4230
+ }
4231
+ return out;
4232
+ }
4233
+ return value;
4234
+ }
4235
+
4236
+ // src/diagnostics/dump-format.ts
4237
+ var import_zod3 = require("zod");
4238
+ var DumpScalarSchema = import_zod3.z.union([import_zod3.z.string(), import_zod3.z.number(), import_zod3.z.boolean()]);
4239
+ var PropertyObservationSchema = import_zod3.z.object({
4240
+ values: import_zod3.z.array(DumpScalarSchema),
4241
+ unmapped: import_zod3.z.array(DumpScalarSchema),
4242
+ enum: import_zod3.z.string().optional(),
4243
+ count: import_zod3.z.number(),
4244
+ firstSeen: import_zod3.z.number(),
4245
+ lastSeen: import_zod3.z.number()
4246
+ });
4247
+ var EventObservationSchema = import_zod3.z.object({
4248
+ at: import_zod3.z.number(),
4249
+ type: import_zod3.z.string(),
4250
+ data: import_zod3.z.unknown().optional()
4251
+ });
4252
+ var RawFrameSchema = import_zod3.z.object({
4253
+ at: import_zod3.z.number(),
4254
+ source: import_zod3.z.string(),
4255
+ payload: import_zod3.z.unknown()
4256
+ });
4257
+ var CommandSchema = import_zod3.z.object({
4258
+ name: import_zod3.z.string(),
4259
+ siid: import_zod3.z.number().optional(),
4260
+ aiid: import_zod3.z.number().optional()
4261
+ });
4262
+ var SensorSchema = import_zod3.z.object({
4263
+ model: import_zod3.z.string(),
4264
+ channel: import_zod3.z.number().optional()
4265
+ });
4266
+ var DeviceDumpSchema = import_zod3.z.object({
4267
+ schemaVersion: import_zod3.z.literal(1),
4268
+ library: import_zod3.z.union([import_zod3.z.literal("nodedreame"), import_zod3.z.literal("nodewitt")]),
4269
+ libraryVersion: import_zod3.z.string(),
4270
+ device: import_zod3.z.object({
4271
+ model: import_zod3.z.string(),
4272
+ firmware: import_zod3.z.string().optional(),
4273
+ region: import_zod3.z.string().optional(),
4274
+ type: import_zod3.z.string().optional()
4275
+ }),
4276
+ observations: import_zod3.z.object({
4277
+ properties: import_zod3.z.record(import_zod3.z.string(), PropertyObservationSchema),
4278
+ events: import_zod3.z.array(EventObservationSchema),
4279
+ rawFrames: import_zod3.z.array(RawFrameSchema).optional()
4280
+ }),
4281
+ catalog: import_zod3.z.object({
4282
+ commands: import_zod3.z.array(CommandSchema).optional(),
4283
+ capabilities: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.unknown()).optional(),
4284
+ sensors: import_zod3.z.array(SensorSchema).optional()
4285
+ }),
4286
+ meta: import_zod3.z.object({
4287
+ startedAt: import_zod3.z.number(),
4288
+ durationMs: import_zod3.z.number(),
4289
+ generatedAt: import_zod3.z.number()
4290
+ })
4291
+ });
4292
+
4293
+ // src/diagnostics/dumper.ts
4294
+ function commandsForFamily(family) {
4295
+ const commands = [];
4296
+ if (family === "vacuum") {
4297
+ for (const [name, ref] of Object.entries(VACUUM_ACTION)) {
4298
+ commands.push({ name, siid: ref.siid, aiid: ref.aiid });
4299
+ }
4300
+ } else if (family === "mower") {
4301
+ for (const [name, ref] of Object.entries(MOWER_ACTION)) {
4302
+ commands.push({ name, siid: ref.siid, aiid: ref.aiid });
4303
+ }
4304
+ }
4305
+ return commands;
4306
+ }
4307
+ function decoderFor(enumName, members) {
4308
+ const lookup = enumLookup(members);
4309
+ return { enumName, isMember: (raw) => lookup(raw) !== null };
4310
+ }
4311
+ function vacuumDecoders() {
4312
+ return {
4313
+ "2.1": decoderFor(
4314
+ "MiotState",
4315
+ Object.values(MiotState).filter((v) => typeof v === "number")
4316
+ ),
4317
+ "3.2": decoderFor(
4318
+ "ChargingStatus",
4319
+ Object.values(ChargingStatus).filter((v) => typeof v === "number")
4320
+ ),
4321
+ "4.4": decoderFor(
4322
+ "SuctionLevel",
4323
+ Object.values(SuctionLevel).filter((v) => typeof v === "number")
4324
+ ),
4325
+ "4.5": decoderFor(
4326
+ "WaterVolume",
4327
+ Object.values(WaterVolume).filter((v) => typeof v === "number")
4328
+ ),
4329
+ "2.6": decoderFor(
4330
+ "CleaningMode",
4331
+ Object.values(CleaningMode).filter((v) => typeof v === "number")
4332
+ ),
4333
+ "4.1": decoderFor(
4334
+ "TaskStatus",
4335
+ Object.values(TaskStatus).filter((v) => typeof v === "number")
4336
+ )
4337
+ };
4338
+ }
4339
+ function mowerDecoders() {
4340
+ return {
4341
+ "2.1": decoderFor(
4342
+ "MowerStatus",
4343
+ Object.values(MowerStatus).filter((v) => typeof v === "number")
4344
+ ),
4345
+ "3.2": decoderFor(
4346
+ "MowerChargingStatus",
4347
+ Object.values(MowerChargingStatus).filter(
4348
+ (v) => typeof v === "number"
4349
+ )
4350
+ ),
4351
+ "5.104": decoderFor(
4352
+ "MowerTaskStatus",
4353
+ Object.values(MowerTaskStatus).filter((v) => typeof v === "number")
4354
+ ),
4355
+ "2.2": decoderFor(
4356
+ "MowerFault",
4357
+ Object.values(MowerFault).filter((v) => typeof v === "number")
4358
+ )
4359
+ };
4360
+ }
4361
+ function toScalar(value) {
4362
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4363
+ return value;
4364
+ }
4365
+ return null;
4366
+ }
4367
+ var PropertyAccumulator = class {
4368
+ #decoders;
4369
+ #entries = /* @__PURE__ */ new Map();
4370
+ constructor(decoders = {}) {
4371
+ this.#decoders = decoders;
4372
+ }
4373
+ record(siid, piid, rawValue, at) {
4374
+ const key2 = `${siid}.${piid}`;
4375
+ const scalar = toScalar(rawValue);
4376
+ let entry = this.#entries.get(key2);
4377
+ if (!entry) {
4378
+ entry = { values: [], unmapped: [], count: 0, firstSeen: at, lastSeen: at };
4379
+ this.#entries.set(key2, entry);
4380
+ }
4381
+ entry.count += 1;
4382
+ entry.lastSeen = at;
4383
+ if (scalar === null) {
4384
+ return;
4385
+ }
4386
+ if (!entry.values.includes(scalar)) {
4387
+ entry.values.push(scalar);
4388
+ }
4389
+ const decoder = this.#decoders[key2];
4390
+ if (decoder && typeof scalar === "number" && !decoder.isMember(scalar)) {
4391
+ if (!entry.unmapped.includes(scalar)) {
4392
+ entry.unmapped.push(scalar);
4393
+ }
4394
+ }
4395
+ }
4396
+ snapshot() {
4397
+ const out = {};
4398
+ for (const [key2, e] of this.#entries) {
4399
+ const decoder = this.#decoders[key2];
4400
+ const base = {
4401
+ values: [...e.values],
4402
+ unmapped: [...e.unmapped],
4403
+ count: e.count,
4404
+ firstSeen: e.firstSeen,
4405
+ lastSeen: e.lastSeen
4406
+ };
4407
+ out[key2] = decoder ? { ...base, enum: decoder.enumName } : base;
4408
+ }
4409
+ return out;
4410
+ }
4411
+ };
4412
+ var DEFAULT_REFRESH_INTERVAL_MS = 3e4;
4413
+ var DEFAULT_MAX_RAW_FRAMES = 500;
4414
+ var Dumper = class {
4415
+ #device;
4416
+ #opts;
4417
+ #acc;
4418
+ #events = [];
4419
+ #rawFrames = [];
4420
+ #catalog;
4421
+ #type;
4422
+ #onProperty;
4423
+ #onEvent;
4424
+ #timer = null;
4425
+ #startedAt = 0;
4426
+ #started = false;
4427
+ constructor(device, catalog, type, decoders, options = {}) {
4428
+ this.#device = device;
4429
+ this.#catalog = catalog;
4430
+ this.#type = type;
4431
+ this.#acc = new PropertyAccumulator(decoders);
4432
+ this.#opts = {
4433
+ captureRawFrames: options.captureRawFrames ?? false,
4434
+ maxRawFrames: options.maxRawFrames ?? DEFAULT_MAX_RAW_FRAMES,
4435
+ refreshIntervalMs: options.refreshIntervalMs ?? DEFAULT_REFRESH_INTERVAL_MS
4436
+ };
4437
+ this.#onProperty = (e) => {
4438
+ this.#acc.record(e.siid, e.piid, e.value, Date.now());
4439
+ this.#pushRaw("mqtt:property", e);
4440
+ };
4441
+ this.#onEvent = (e) => {
4442
+ this.#events.push({
4443
+ at: Date.now(),
4444
+ type: `${e.siid}.${e.eiid}`,
4445
+ data: { arguments: e.arguments }
4446
+ });
4447
+ this.#pushRaw("mqtt:event", e);
4448
+ };
4449
+ }
4450
+ /** Attach to the live stream + arm the periodic shadow refresh. Idempotent. */
4451
+ async start() {
4452
+ if (this.#started) {
4453
+ return;
4454
+ }
4455
+ this.#started = true;
4456
+ this.#startedAt = Date.now();
4457
+ this.#device.on("propertyChanged", this.#onProperty);
4458
+ this.#device.on("event", this.#onEvent);
4459
+ await this.#refresh();
4460
+ if (this.#opts.refreshIntervalMs > 0 && this.#device.refreshFromCache) {
4461
+ this.#timer = setInterval(() => void this.#refresh(), this.#opts.refreshIntervalMs);
4462
+ this.#timer.unref?.();
4463
+ }
4464
+ }
4465
+ /** Detach every listener + clear the timer. Idempotent. */
4466
+ stop() {
4467
+ if (!this.#started) {
4468
+ return Promise.resolve();
4469
+ }
4470
+ this.#started = false;
4471
+ if (this.#timer) {
4472
+ clearInterval(this.#timer);
4473
+ this.#timer = null;
4474
+ }
4475
+ this.#device.off("propertyChanged", this.#onProperty);
4476
+ this.#device.off("event", this.#onEvent);
4477
+ return Promise.resolve();
4478
+ }
4479
+ /** Build the anonymized, schema-valid dump. Works live or after {@link stop}. */
4480
+ export() {
4481
+ const now = Date.now();
4482
+ const dump = {
4483
+ schemaVersion: 1,
4484
+ library: "nodedreame",
4485
+ libraryVersion: LIBRARY_VERSION,
4486
+ device: {
4487
+ model: this.#device.model,
4488
+ type: this.#type
4489
+ },
4490
+ observations: {
4491
+ properties: this.#acc.snapshot(),
4492
+ events: [...this.#events],
4493
+ ...this.#opts.captureRawFrames ? { rawFrames: this.#redactFrames() } : {}
4494
+ },
4495
+ catalog: this.#catalog,
4496
+ meta: {
4497
+ startedAt: this.#startedAt,
4498
+ durationMs: Math.max(0, now - this.#startedAt),
4499
+ generatedAt: now
4500
+ }
4501
+ };
4502
+ const anonymized = redact(dump);
4503
+ return DeviceDumpSchema.parse(anonymized);
4504
+ }
4505
+ /** Deterministic pretty JSON of {@link export}. */
4506
+ exportJson() {
4507
+ return JSON.stringify(this.export(), null, 2);
4508
+ }
4509
+ async #refresh() {
4510
+ if (this.#device.refreshFromCache) {
4511
+ try {
4512
+ await this.#device.refreshFromCache();
4513
+ } catch {
4514
+ }
4515
+ }
4516
+ }
4517
+ #pushRaw(source, payload) {
4518
+ if (!this.#opts.captureRawFrames) {
4519
+ return;
4520
+ }
4521
+ this.#rawFrames.push({ at: Date.now(), source, payload });
4522
+ if (this.#rawFrames.length > this.#opts.maxRawFrames) {
4523
+ this.#rawFrames.shift();
4524
+ }
4525
+ }
4526
+ #redactFrames() {
4527
+ return this.#rawFrames.map((f) => ({ at: f.at, source: f.source, payload: redact(f.payload) }));
4528
+ }
4529
+ };
4530
+ function familyOf(device) {
4531
+ if (device instanceof VacuumDevice) {
4532
+ return "vacuum";
4533
+ }
4534
+ if (device instanceof MowerDevice) {
4535
+ return "mower";
4536
+ }
4537
+ if (!(device instanceof BaseDevice)) {
4538
+ if (device.model.startsWith("dreame.vacuum.")) {
4539
+ return "vacuum";
4540
+ }
4541
+ if (device.model.startsWith("dreame.mower.")) {
4542
+ return "mower";
4543
+ }
4544
+ }
4545
+ return "device";
4546
+ }
4547
+ function decodersFor(device) {
4548
+ switch (familyOf(device)) {
4549
+ case "vacuum":
4550
+ return vacuumDecoders();
4551
+ case "mower":
4552
+ return mowerDecoders();
4553
+ default:
4554
+ return {};
4555
+ }
4556
+ }
4557
+ function typeFor(device) {
4558
+ return familyOf(device);
4559
+ }
4560
+ function catalogFor(device) {
4561
+ return {
4562
+ commands: commandsForFamily(familyOf(device)),
4563
+ capabilities: { tokens: [...device.capabilities.list()] }
4564
+ };
4565
+ }
4566
+ function createDumper(target, options) {
4567
+ return new Dumper(
4568
+ target,
4569
+ catalogFor(target),
4570
+ typeFor(target),
4571
+ decodersFor(target),
4572
+ options ?? {}
4573
+ );
4574
+ }
4575
+ function createClientDumper(client, options) {
4576
+ return client.devices.map((d) => createDumper(d, options));
4577
+ }
3891
4578
  // Annotate the CommonJS export names for ESM import in node:
3892
4579
  0 && (module.exports = {
3893
4580
  BaseDevice,
@@ -3907,6 +4594,7 @@ var Nodreame = class extends TypedEmitter {
3907
4594
  MowerChargingStatus,
3908
4595
  MowerControlAction,
3909
4596
  MowerDevice,
4597
+ MowerFault,
3910
4598
  MowerStatus,
3911
4599
  MowerTaskStatus,
3912
4600
  Nodreame,
@@ -3916,6 +4604,8 @@ var Nodreame = class extends TypedEmitter {
3916
4604
  VacuumCapabilityResolver,
3917
4605
  VacuumDevice,
3918
4606
  WaterVolume,
4607
+ createClientDumper,
4608
+ createDumper,
3919
4609
  getMowerCapabilities,
3920
4610
  getVacuumCapabilities,
3921
4611
  renderMowerSvg,