@riddix/hamh 2.1.0-alpha.726 → 2.1.0-alpha.728

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.
@@ -126207,6 +126207,7 @@ function entityMappingApi(mappingStorage) {
126207
126207
  customServiceAreas: body.customServiceAreas,
126208
126208
  customFanSpeedTags: body.customFanSpeedTags,
126209
126209
  currentRoomEntity: body.currentRoomEntity,
126210
+ cleanedAreaEntity: body.cleanedAreaEntity,
126210
126211
  valetudoIdentifier: body.valetudoIdentifier,
126211
126212
  coverSwapOpenClose: body.coverSwapOpenClose,
126212
126213
  coverSliderDebounceMs: body.coverSliderDebounceMs,
@@ -130826,6 +130827,7 @@ var EntityMappingStorage = class extends Service {
130826
130827
  ) ?? void 0,
130827
130828
  customFanSpeedTags: request.customFanSpeedTags && Object.keys(request.customFanSpeedTags).length > 0 ? request.customFanSpeedTags : void 0,
130828
130829
  currentRoomEntity: request.currentRoomEntity?.trim() || void 0,
130830
+ cleanedAreaEntity: request.cleanedAreaEntity?.trim() || void 0,
130829
130831
  valetudoIdentifier: request.valetudoIdentifier?.trim() || void 0,
130830
130832
  coverSwapOpenClose: request.coverSwapOpenClose || void 0,
130831
130833
  coverSliderDebounceMs: sanitizeDebounceMs(request.coverSliderDebounceMs),
@@ -130836,7 +130838,7 @@ var EntityMappingStorage = class extends Service {
130836
130838
  climateAutoMode: request.climateAutoMode || void 0,
130837
130839
  composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0
130838
130840
  };
130839
- if (!config11.matterDeviceType && !config11.customName && !config11.customProductName && !config11.customVendorName && !config11.customSerialNumber && config11.customVendorId === void 0 && config11.disabled !== true && !config11.filterLifeEntity && !config11.cleaningModeEntity && !config11.temperatureEntity && !config11.humidityEntity && !config11.batteryEntity && !config11.roomEntities && !config11.disableLockPin && !config11.powerEntity && !config11.energyEntity && !config11.pressureEntity && !config11.suctionLevelEntity && !config11.mopIntensityEntity && (!config11.customServiceAreas || config11.customServiceAreas.length === 0) && (!config11.customFanSpeedTags || Object.keys(config11.customFanSpeedTags).length === 0) && !config11.currentRoomEntity && !config11.valetudoIdentifier && !config11.coverSwapOpenClose && !config11.coverSliderDebounceMs && !config11.disableClimateOnOff && !config11.disableClimateFanControl && !config11.climateKeepModeOnIdle && !config11.climateExposeFan && !config11.climateAutoMode && (!config11.composedEntities || config11.composedEntities.length === 0)) {
130841
+ if (!config11.matterDeviceType && !config11.customName && !config11.customProductName && !config11.customVendorName && !config11.customSerialNumber && config11.customVendorId === void 0 && config11.disabled !== true && !config11.filterLifeEntity && !config11.cleaningModeEntity && !config11.temperatureEntity && !config11.humidityEntity && !config11.batteryEntity && !config11.roomEntities && !config11.disableLockPin && !config11.powerEntity && !config11.energyEntity && !config11.pressureEntity && !config11.suctionLevelEntity && !config11.mopIntensityEntity && (!config11.customServiceAreas || config11.customServiceAreas.length === 0) && (!config11.customFanSpeedTags || Object.keys(config11.customFanSpeedTags).length === 0) && !config11.currentRoomEntity && !config11.cleanedAreaEntity && !config11.valetudoIdentifier && !config11.coverSwapOpenClose && !config11.coverSliderDebounceMs && !config11.disableClimateOnOff && !config11.disableClimateFanControl && !config11.climateKeepModeOnIdle && !config11.climateExposeFan && !config11.climateAutoMode && (!config11.composedEntities || config11.composedEntities.length === 0)) {
130840
130842
  bridgeMap.delete(request.entityId);
130841
130843
  } else {
130842
130844
  bridgeMap.set(request.entityId, config11);
@@ -157462,6 +157464,27 @@ init_nodejs();
157462
157464
 
157463
157465
  // src/matter/behaviors/rvc-run-mode-server.ts
157464
157466
  init_home_assistant_entity_behavior();
157467
+
157468
+ // src/matter/behaviors/infer-cleaned-area-progress.ts
157469
+ function inferCleanedAreaProgress(cleanedSqm, orderedAreas) {
157470
+ if (orderedAreas.length === 0) {
157471
+ return { currentArea: null, completed: [] };
157472
+ }
157473
+ const cleaned = Number.isFinite(cleanedSqm) ? Math.max(0, cleanedSqm) : 0;
157474
+ let cumulative = 0;
157475
+ const completed = [];
157476
+ for (const area of orderedAreas) {
157477
+ const size = Number.isFinite(area.sizeSqm) ? Math.max(0, area.sizeSqm) : 0;
157478
+ cumulative += size;
157479
+ if (cleaned < cumulative) {
157480
+ return { currentArea: area.areaId, completed };
157481
+ }
157482
+ completed.push(area.areaId);
157483
+ }
157484
+ return { currentArea: null, completed };
157485
+ }
157486
+
157487
+ // src/matter/behaviors/rvc-run-mode-server.ts
157465
157488
  var logger215 = Logger.get("RvcRunModeServer");
157466
157489
  var ROOM_MODE_BASE = 100;
157467
157490
  function isRoomMode(mode) {
@@ -157477,7 +157500,8 @@ function getSession(endpoint) {
157477
157500
  activeAreas: [],
157478
157501
  loggedShortCircuits: /* @__PURE__ */ new Set(),
157479
157502
  observedCleaning: false,
157480
- pendingDispatches: []
157503
+ pendingDispatches: [],
157504
+ cleanedAreaBaseline: null
157481
157505
  };
157482
157506
  cleaningSessions.set(endpoint, session);
157483
157507
  }
@@ -157570,7 +157594,12 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
157570
157594
  }
157571
157595
  }
157572
157596
  if (newMode === 1 /* Cleaning */) {
157573
- this.updateCurrentRoomFromSensor();
157597
+ const mapping = this.agent.get(HomeAssistantEntityBehavior).state.mapping;
157598
+ if (mapping?.currentRoomEntity) {
157599
+ this.updateCurrentRoomFromSensor();
157600
+ } else if (mapping?.cleanedAreaEntity) {
157601
+ this.updateCurrentRoomFromCleanedArea();
157602
+ }
157574
157603
  }
157575
157604
  }
157576
157605
  /**
@@ -157660,6 +157689,80 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
157660
157689
  }
157661
157690
  }
157662
157691
  }
157692
+ /** Read the cumulative cleaned-area sensor (m2), or null if not configured. */
157693
+ readCleanedAreaSqm() {
157694
+ try {
157695
+ const mapping = this.agent.get(HomeAssistantEntityBehavior).state.mapping;
157696
+ const entityId = mapping?.cleanedAreaEntity;
157697
+ if (!entityId) {
157698
+ return null;
157699
+ }
157700
+ return this.agent.env.get(EntityStateProvider).getNumericState(entityId);
157701
+ } catch {
157702
+ return null;
157703
+ }
157704
+ }
157705
+ /**
157706
+ * For batch vacuums that report cumulative cleaned area but not the current
157707
+ * room, infer currentArea + progress from the cleaned area and the per-room
157708
+ * sizeSqm. Display-only and batch-only; skipped unless every selected area
157709
+ * has a size. The currentRoom sensor path takes priority (#368).
157710
+ */
157711
+ updateCurrentRoomFromCleanedArea() {
157712
+ try {
157713
+ const s = getSession(this.endpoint);
157714
+ if (s.pendingDispatches.length > 0 || s.activeAreas.length === 0) {
157715
+ return;
157716
+ }
157717
+ const mapping = this.agent.get(HomeAssistantEntityBehavior).state.mapping;
157718
+ const entityId = mapping?.cleanedAreaEntity;
157719
+ if (!entityId) {
157720
+ return;
157721
+ }
157722
+ const raw = this.agent.env.get(EntityStateProvider).getNumericState(entityId);
157723
+ if (raw == null) {
157724
+ this.logShortCircuitOnce(
157725
+ "no-cleaned-area-state",
157726
+ `cleanedArea sensor: no numeric state for ${entityId}`
157727
+ );
157728
+ return;
157729
+ }
157730
+ const customAreas = mapping?.customServiceAreas;
157731
+ const ordered = [];
157732
+ for (const areaId of s.activeAreas) {
157733
+ const size = customAreas?.[areaId - 1]?.sizeSqm;
157734
+ if (typeof size !== "number" || !Number.isFinite(size) || size <= 0) {
157735
+ this.logShortCircuitOnce(
157736
+ "no-sizes",
157737
+ "cleanedArea sensor: not every selected area has a sizeSqm"
157738
+ );
157739
+ return;
157740
+ }
157741
+ ordered.push({ areaId, sizeSqm: size });
157742
+ }
157743
+ if (s.cleanedAreaBaseline == null || raw < s.cleanedAreaBaseline) {
157744
+ s.cleanedAreaBaseline = raw;
157745
+ }
157746
+ const cleaned = Math.max(0, raw - s.cleanedAreaBaseline);
157747
+ const { currentArea, completed } = inferCleanedAreaProgress(
157748
+ cleaned,
157749
+ ordered
157750
+ );
157751
+ for (const id of completed) {
157752
+ s.completedAreas.add(id);
157753
+ }
157754
+ if (currentArea === s.lastCurrentArea) {
157755
+ return;
157756
+ }
157757
+ s.lastCurrentArea = currentArea;
157758
+ this.trySetCurrentArea(currentArea);
157759
+ } catch (e) {
157760
+ const msg = e instanceof Error ? e.message : String(e);
157761
+ if (!msg.includes("No provider for") && !msg.includes("not supported")) {
157762
+ logger215.warn(`cleanedArea room update failed: ${msg}`);
157763
+ }
157764
+ }
157765
+ }
157663
157766
  /**
157664
157767
  * Safely update ServiceArea.currentArea and progress.
157665
157768
  * When areaId is set, marks it as Operating in progress.
@@ -157718,6 +157821,29 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
157718
157821
  status: s.completedAreas.has(id) ? ServiceArea3.OperationalStatus.Completed : ServiceArea3.OperationalStatus.Pending
157719
157822
  }));
157720
157823
  }
157824
+ /**
157825
+ * Stop before finishing: mark the areas the vacuum actually reached as
157826
+ * Completed and the rest as Skipped (an out-of-band stop per the Matter
157827
+ * ServiceArea spec), then clear currentArea. The old path marked every
157828
+ * area Completed, which told Apple Home rooms were cleaned when they were
157829
+ * not, so they got dropped from the next selection (#367).
157830
+ */
157831
+ finalizeProgressOnStop() {
157832
+ const s = getSession(this.endpoint);
157833
+ try {
157834
+ const serviceArea = this.agent.get(ServiceAreaBehavior);
157835
+ if (s.activeAreas.length > 0) {
157836
+ const last = serviceArea.state.currentArea;
157837
+ const state = serviceArea.state;
157838
+ state.progress = s.activeAreas.map((id) => ({
157839
+ areaId: id,
157840
+ status: s.completedAreas.has(id) || id === last ? ServiceArea3.OperationalStatus.Completed : ServiceArea3.OperationalStatus.Skipped
157841
+ }));
157842
+ }
157843
+ serviceArea.state.currentArea = null;
157844
+ } catch {
157845
+ }
157846
+ }
157721
157847
  /**
157722
157848
  * Find the ServiceArea area ID that corresponds to a run mode value
157723
157849
  * by matching the mode label to the area location name.
@@ -157754,6 +157880,7 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
157754
157880
  s.lastCurrentArea = null;
157755
157881
  s.loggedShortCircuits.clear();
157756
157882
  s.pendingDispatches = [];
157883
+ s.cleanedAreaBaseline = this.readCleanedAreaSqm();
157757
157884
  this.trySetCurrentArea(s.activeAreas[0]);
157758
157885
  homeAssistant.callAction(this.state.config.start(void 0, this.agent));
157759
157886
  this.state.currentMode = newMode;
@@ -157792,6 +157919,7 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
157792
157919
  s.lastCurrentArea = null;
157793
157920
  s.loggedShortCircuits.clear();
157794
157921
  s.pendingDispatches = [];
157922
+ s.cleanedAreaBaseline = this.readCleanedAreaSqm();
157795
157923
  this.trySetCurrentArea(s.activeAreas[0]);
157796
157924
  }
157797
157925
  } catch {
@@ -157800,13 +157928,14 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
157800
157928
  break;
157801
157929
  }
157802
157930
  case 0 /* Idle */:
157803
- this.trySetCurrentArea(null);
157931
+ this.finalizeProgressOnStop();
157804
157932
  s.completedAreas.clear();
157805
157933
  s.lastCurrentArea = null;
157806
157934
  s.activeAreas = [];
157807
157935
  s.loggedShortCircuits.clear();
157808
157936
  s.pendingDispatches = [];
157809
157937
  s.observedCleaning = false;
157938
+ s.cleanedAreaBaseline = null;
157810
157939
  homeAssistant.callAction(
157811
157940
  this.state.config.returnToBase(void 0, this.agent)
157812
157941
  );