@riddix/hamh 2.0.42 → 2.0.43

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.
@@ -146781,7 +146781,7 @@ var init_bridge_config_schema = __esm({
146781
146781
  },
146782
146782
  autoComposedDevices: {
146783
146783
  title: "Auto Composed Devices",
146784
- description: "Master toggle: combine related entities from the same Home Assistant device into a single Matter endpoint. Turns on battery, humidity, pressure, power, and energy auto-mapping at once \u2014 a Shelly Plug shows up as one device with power monitoring instead of several siblings.",
146784
+ description: "Master toggle: combine related entities from the same Home Assistant device into a single Matter endpoint. Turns on battery, humidity, pressure, power, and energy auto-mapping at once, a Shelly Plug shows up as one device with power monitoring instead of several siblings.",
146785
146785
  type: "boolean",
146786
146786
  default: false
146787
146787
  },
@@ -146799,13 +146799,13 @@ var init_bridge_config_schema = __esm({
146799
146799
  },
146800
146800
  preferEntityRegistryName: {
146801
146801
  title: "Prefer Entity Registry Name (HA 2026.4 workaround)",
146802
- description: "Use the entity registry name (or original_name) as nodeLabel instead of the composed friendly_name. Since Home Assistant 2026.4, friendly_name is prefixed with the device name, which breaks voice commands that relied on the short entity name. Resolution order: customName \u2192 registry name \u2192 registry original_name \u2192 friendly_name \u2192 entity_id. Matter has no alias concept \u2014 this only changes which single name is reported.",
146802
+ description: "Use the entity registry name (or original_name) as nodeLabel instead of the composed friendly_name. Since Home Assistant 2026.4, friendly_name is prefixed with the device name, which breaks voice commands that relied on the short entity name. Resolution order: customName \u2192 registry name \u2192 registry original_name \u2192 friendly_name \u2192 entity_id. Matter has no alias concept, this only changes which single name is reported.",
146803
146803
  type: "boolean",
146804
146804
  default: false
146805
146805
  },
146806
146806
  vacuumOnOff: {
146807
146807
  title: "Vacuum: Include OnOff Cluster (Alexa)",
146808
- description: "Add an OnOff cluster to robot vacuum endpoints. Alexa REQUIRES this (PowerController) to show robotic vacuums in the app. Without it, Alexa commissions the device but never displays it. In Server Mode this is enabled automatically \u2014 only check this for bridge mode. WARNING: OnOff is NOT part of the Matter RVC device type specification. Enabling this may break Apple Home (shows 'Updating') and Google Home.",
146808
+ description: "Add an OnOff cluster to robot vacuum endpoints. Alexa REQUIRES this (PowerController) to show robotic vacuums in the app. Without it, Alexa commissions the device but never displays it. In Server Mode this is enabled automatically, only check this for bridge mode. WARNING: OnOff is NOT part of the Matter RVC device type specification. Enabling this may break Apple Home (shows 'Updating') and Google Home.",
146809
146809
  type: "boolean"
146810
146810
  },
146811
146811
  alexaPreserveBrightnessOnTurnOn: {
@@ -148596,7 +148596,14 @@ function addLogEntry(entry) {
148596
148596
  function logsApi(_logger) {
148597
148597
  const router = express5.Router();
148598
148598
  router.get("/", (req, res) => {
148599
- const { level, search, limit = "100", offset = "0" } = req.query;
148599
+ const {
148600
+ level,
148601
+ search,
148602
+ category,
148603
+ facility,
148604
+ limit = "100",
148605
+ offset = "0"
148606
+ } = req.query;
148600
148607
  const limitNum = Math.min(
148601
148608
  500,
148602
148609
  Math.max(1, parseInt(limit, 10) || 100)
@@ -148607,6 +148614,18 @@ function logsApi(_logger) {
148607
148614
  const levels = level.split(",").map((l) => l.toLowerCase().trim());
148608
148615
  entries = entries.filter((e) => levels.includes(e.level.toLowerCase()));
148609
148616
  }
148617
+ if (category && typeof category === "string") {
148618
+ const cats = category.split(",").map((c) => c.toLowerCase().trim());
148619
+ entries = entries.filter(
148620
+ (e) => e.category != null && cats.includes(e.category.toLowerCase())
148621
+ );
148622
+ }
148623
+ if (facility && typeof facility === "string") {
148624
+ const facLower = facility.toLowerCase();
148625
+ entries = entries.filter(
148626
+ (e) => e.facility?.toLowerCase().includes(facLower)
148627
+ );
148628
+ }
148610
148629
  if (search && typeof search === "string") {
148611
148630
  const searchLower = search.toLowerCase();
148612
148631
  entries = entries.filter(
@@ -151608,6 +151627,17 @@ init_esm7();
151608
151627
 
151609
151628
  // src/core/app/logger.ts
151610
151629
  init_esm();
151630
+ var MATTER_TRAFFIC_FACILITIES = /* @__PURE__ */ new Set([
151631
+ "InteractionServer",
151632
+ "MessageExchange",
151633
+ "MessageChannel",
151634
+ "ServerSubscription",
151635
+ "IncomingInteractionServerMessenger",
151636
+ "ExchangeManager"
151637
+ ]);
151638
+ function categoryFor(facility) {
151639
+ return MATTER_TRAFFIC_FACILITIES.has(facility) ? "matter-traffic" : void 0;
151640
+ }
151611
151641
  function logLevelFromString(level) {
151612
151642
  const customNames = {
151613
151643
  SILLY: -1 /* SILLY */
@@ -151653,6 +151683,26 @@ var LoggerService = class {
151653
151683
  MessageExchange: resolvedProtocolLevel
151654
151684
  };
151655
151685
  Logger.format = options.disableColors ? LogFormat.PLAIN : LogFormat.ANSI;
151686
+ const destinationLevel = this.customLogLevelMapping[this._level] ?? this._level;
151687
+ Logger.destinations["hamh-buffer"] = LogDestination({
151688
+ name: "hamh-buffer",
151689
+ level: destinationLevel,
151690
+ facilityLevels: {
151691
+ MessageChannel: resolvedProtocolLevel,
151692
+ MessageExchange: resolvedProtocolLevel
151693
+ },
151694
+ write: (text, message) => {
151695
+ const category = categoryFor(message.facility);
151696
+ if (!category) return;
151697
+ addLogEntry({
151698
+ timestamp: message.now.toISOString(),
151699
+ level: logLevelToString(message.level).toLowerCase(),
151700
+ message: text,
151701
+ facility: message.facility,
151702
+ category
151703
+ });
151704
+ }
151705
+ });
151656
151706
  }
151657
151707
  get(nameOrService) {
151658
151708
  let name;
@@ -152748,7 +152798,7 @@ var HomeAssistantRegistry = class extends Service {
152748
152798
  let refreshing = false;
152749
152799
  this.autoRefresh = setInterval(async () => {
152750
152800
  if (refreshing) {
152751
- logger146.debug("Skipping registry refresh \u2014 previous tick still running");
152801
+ logger146.debug("Skipping registry refresh, previous tick still running");
152752
152802
  return;
152753
152803
  }
152754
152804
  refreshing = true;
@@ -165337,7 +165387,7 @@ var ServerModeServerNode = class extends ServerNode {
165337
165387
  /**
165338
165388
  * Update root-level BasicInformation with entity-specific data.
165339
165389
  * In server mode, controllers (Apple Home, Alexa) read the root node's
165340
- * BasicInformation not the device endpoint's BridgedDeviceBasicInformation.
165390
+ * BasicInformation, not the device endpoint's BridgedDeviceBasicInformation.
165341
165391
  * Without this, server-mode devices show bridge defaults (e.g. "riddix" / "MatterHub").
165342
165392
  */
165343
165393
  async updateDeviceIdentity(entityId, device, mapping, friendlyName) {
@@ -167848,7 +167898,7 @@ ${e?.toString()}`);
167848
167898
  );
167849
167899
  if (totalSubs === 0 && sessions.length > 0) {
167850
167900
  this.log.warn(
167851
- `All subscriptions lost \u2014 ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
167901
+ `All subscriptions lost, ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
167852
167902
  );
167853
167903
  if (!this.deadSessionTimer) {
167854
167904
  this.deadSessionTimer = setTimeout(() => {
@@ -167896,7 +167946,7 @@ ${e?.toString()}`);
167896
167946
  for (const s of [...sessionManager.sessions]) {
167897
167947
  if (s !== newSession && !s.isClosing && s.peerNodeId === newSession.peerNodeId && s.fabric?.fabricIndex === newSession.fabric?.fabricIndex && s.subscriptions.size === 0) {
167898
167948
  this.log.info(
167899
- `Closing stale session ${s.id} (peer ${s.peerNodeId}, 0 subs) \u2014 replaced by session ${newSession.id}`
167949
+ `Closing stale session ${s.id} (peer ${s.peerNodeId}, 0 subs), replaced by session ${newSession.id}`
167900
167950
  );
167901
167951
  s.initiateForceClose().catch(() => {
167902
167952
  });
@@ -168351,7 +168401,7 @@ var BasicInformationServer2 = class extends BridgedDeviceBasicInformationServer
168351
168401
  await super.initialize();
168352
168402
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
168353
168403
  this.update(homeAssistant.entity);
168354
- this.reactTo(homeAssistant.onChange, this.update);
168404
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168355
168405
  }
168356
168406
  update(entity) {
168357
168407
  if (!entity.state || !entity.state.attributes) {
@@ -168417,7 +168467,7 @@ var ElectricalEnergyMeasurementServerBase = class extends FeaturedBase {
168417
168467
  );
168418
168468
  }
168419
168469
  this.update();
168420
- this.reactTo(homeAssistant.onChange, this.update);
168470
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168421
168471
  }
168422
168472
  update() {
168423
168473
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
@@ -168477,7 +168527,7 @@ var ElectricalPowerMeasurementServerBase = class extends ElectricalPowerMeasurem
168477
168527
  );
168478
168528
  }
168479
168529
  this.update();
168480
- this.reactTo(homeAssistant.onChange, this.update);
168530
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168481
168531
  }
168482
168532
  update() {
168483
168533
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
@@ -168530,7 +168580,7 @@ var HumidityMeasurementServerBase = class extends RelativeHumidityMeasurementSer
168530
168580
  await super.initialize();
168531
168581
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
168532
168582
  this.update(homeAssistant.entity);
168533
- this.reactTo(homeAssistant.onChange, this.update);
168583
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168534
168584
  }
168535
168585
  update(entity) {
168536
168586
  if (!entity.state || !entity.state.attributes) {
@@ -168594,7 +168644,7 @@ var PowerSourceServerBase = class extends FeaturedBase2 {
168594
168644
  );
168595
168645
  }
168596
168646
  this.update(homeAssistant.entity);
168597
- this.reactTo(homeAssistant.onChange, this.update);
168647
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168598
168648
  }
168599
168649
  update(entity) {
168600
168650
  if (!entity.state || !entity.state.attributes) {
@@ -168680,7 +168730,7 @@ var TemperatureMeasurementServerBase = class extends TemperatureMeasurementServe
168680
168730
  await super.initialize();
168681
168731
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
168682
168732
  this.update(homeAssistant.entity);
168683
- this.reactTo(homeAssistant.onChange, this.update);
168733
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168684
168734
  }
168685
168735
  update(entity) {
168686
168736
  if (!entity.state || !entity.state.attributes) {
@@ -168722,7 +168772,7 @@ var HepaFilterMonitoringServerBase = class extends FeaturedBase3 {
168722
168772
  await super.initialize();
168723
168773
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
168724
168774
  this.update(homeAssistant.entity);
168725
- this.reactTo(homeAssistant.onChange, this.update);
168775
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
168726
168776
  }
168727
168777
  update(entity) {
168728
168778
  if (!entity.state || !entity.state.attributes) {
@@ -169200,7 +169250,7 @@ var FeaturedBase5 = FanControlServer.with(
169200
169250
  "Rocking",
169201
169251
  "Wind"
169202
169252
  ).set({
169203
- // rockSupport / windSupport are Fixed quality attributes they MUST be set
169253
+ // rockSupport / windSupport are Fixed quality attributes, they MUST be set
169204
169254
  // via .set() at behavior creation time, NOT in initialize().
169205
169255
  // Without these, controllers reject attempts to enable rocking/wind.
169206
169256
  rockSupport: { rockUpDown: true },
@@ -169976,7 +170026,7 @@ var PressureMeasurementServerBase = class extends PressureMeasurementServer {
169976
170026
  await super.initialize();
169977
170027
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
169978
170028
  this.update(homeAssistant.entity);
169979
- this.reactTo(homeAssistant.onChange, this.update);
170029
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
169980
170030
  }
169981
170031
  update(entity) {
169982
170032
  if (!entity.state || !entity.state.attributes) {
@@ -170536,7 +170586,7 @@ var BooleanStateServerBase = class extends BooleanStateServer {
170536
170586
  await super.initialize();
170537
170587
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
170538
170588
  this.update(homeAssistant.entity);
170539
- this.reactTo(homeAssistant.onChange, this.update);
170589
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
170540
170590
  }
170541
170591
  update(entity) {
170542
170592
  if (!entity.state || !entity.state.attributes) {
@@ -170614,7 +170664,7 @@ var PirOccupancySensingServer = class extends PirOccupancySensingServerBase {
170614
170664
  await super.initialize();
170615
170665
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
170616
170666
  this.update(homeAssistant.entity);
170617
- this.reactTo(homeAssistant.onChange, this.update);
170667
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
170618
170668
  }
170619
170669
  update(entity) {
170620
170670
  if (!entity.state || !entity.state.attributes) {
@@ -170682,7 +170732,7 @@ var OccupancySensingServer2 = class extends OccupancySensingServerBase {
170682
170732
  await super.initialize();
170683
170733
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
170684
170734
  this.update(homeAssistant.entity);
170685
- this.reactTo(homeAssistant.onChange, this.update);
170735
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
170686
170736
  }
170687
170737
  update(entity) {
170688
170738
  if (!entity.state || !entity.state.attributes) {
@@ -170797,7 +170847,7 @@ var SmokeAlarmServerImpl = class extends SmokeAlarmServerWithFeature {
170797
170847
  await super.initialize();
170798
170848
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
170799
170849
  this.update(homeAssistant.entity);
170800
- this.reactTo(homeAssistant.onChange, this.update);
170850
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
170801
170851
  }
170802
170852
  update(entity) {
170803
170853
  const isOn = this.agent.get(HomeAssistantEntityBehavior).isAvailable && entity.state.state === "on";
@@ -170811,7 +170861,7 @@ var CoAlarmServerImpl = class extends CoAlarmServerWithFeature {
170811
170861
  await super.initialize();
170812
170862
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
170813
170863
  this.update(homeAssistant.entity);
170814
- this.reactTo(homeAssistant.onChange, this.update);
170864
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
170815
170865
  }
170816
170866
  update(entity) {
170817
170867
  const isOn = this.agent.get(HomeAssistantEntityBehavior).isAvailable && entity.state.state === "on";
@@ -171092,6 +171142,23 @@ var ClimateOnOffServer = OnOffServer2({
171092
171142
 
171093
171143
  // src/matter/endpoints/legacy/climate/behaviors/climate-thermostat-server.ts
171094
171144
  init_dist();
171145
+
171146
+ // src/utils/converters/snap-to-step.ts
171147
+ function snapToStep(target, current, step) {
171148
+ if (step == null || !Number.isFinite(step) || step <= 0) return target;
171149
+ const ratio = target / step;
171150
+ const lower = Math.floor(ratio) * step;
171151
+ const upper = Math.ceil(ratio) * step;
171152
+ if (lower === upper) return target;
171153
+ const distLower = target - lower;
171154
+ const distUpper = upper - target;
171155
+ if (distLower < distUpper) return lower;
171156
+ if (distUpper < distLower) return upper;
171157
+ if (current == null || !Number.isFinite(current)) return upper;
171158
+ return target > current ? upper : lower;
171159
+ }
171160
+
171161
+ // src/matter/endpoints/legacy/climate/behaviors/climate-thermostat-server.ts
171095
171162
  init_home_assistant_entity_behavior();
171096
171163
 
171097
171164
  // src/matter/behaviors/thermostat-server.ts
@@ -171287,7 +171354,7 @@ var ThermostatServerBase = class extends FullFeaturedBase {
171287
171354
  // Heat/Cool/Off but SKIPS Auto (see #handleSystemModeChange in Matter.js).
171288
171355
  // Without this, switching Heat→Auto leaves runningMode stale at Heat.
171289
171356
  // 889010b set for ALL modes (conflicted with reactor), 0678d35 set for NONE
171290
- // (stale in Auto). da04b2e's Auto-only approach is correct the issues
171357
+ // (stale in Auto). da04b2e's Auto-only approach is correct, the issues
171291
171358
  // reported after it were caused by localTemperature fallback, not runningMode.
171292
171359
  ...this.features.heating && this.features.cooling && systemMode === Thermostat3.SystemMode.Auto ? { thermostatRunningMode: runningMode } : {}
171293
171360
  });
@@ -171572,7 +171639,7 @@ var CoolingOnlyThermostatServerBase = class extends CoolingOnlyFeaturedBase {
171572
171639
  static State = class extends CoolingOnlyFeaturedBase.State {
171573
171640
  config;
171574
171641
  };
171575
- // Each variant MUST define its own initialize() see HeatingOnly comment above.
171642
+ // Each variant MUST define its own initialize(), see HeatingOnly comment above.
171576
171643
  async initialize() {
171577
171644
  thermostatPreInitialize(this);
171578
171645
  await super.initialize();
@@ -171594,7 +171661,7 @@ var HeatingAndCoolingThermostatServerBase = class extends HeatingAndCoolingFeatu
171594
171661
  static State = class extends HeatingAndCoolingFeaturedBase.State {
171595
171662
  config;
171596
171663
  };
171597
- // Each variant MUST define its own initialize() see HeatingOnly comment above.
171664
+ // Each variant MUST define its own initialize(), see HeatingOnly comment above.
171598
171665
  async initialize() {
171599
171666
  thermostatPreInitialize(this);
171600
171667
  await super.initialize();
@@ -171675,6 +171742,18 @@ var getTemp = (agent, entity, attributeName) => {
171675
171742
  return Temperature.withUnit(+temperature3, unit);
171676
171743
  }
171677
171744
  };
171745
+ var getStep = (entity) => {
171746
+ const raw = attributes4(entity).target_temp_step;
171747
+ if (raw == null) return void 0;
171748
+ const num = typeof raw === "string" ? parseFloat(raw) : raw;
171749
+ return Number.isFinite(num) && num > 0 ? num : void 0;
171750
+ };
171751
+ var getRefTemp = (entity, attr) => {
171752
+ const raw = attributes4(entity)[attr];
171753
+ if (raw == null) return void 0;
171754
+ const num = typeof raw === "string" ? parseFloat(raw) : raw;
171755
+ return Number.isFinite(num) ? num : void 0;
171756
+ };
171678
171757
  var systemModeToHvacMode = {
171679
171758
  [Thermostat3.SystemMode.Auto]: ClimateHvacMode.heat_cool,
171680
171759
  [Thermostat3.SystemMode.Precooling]: ClimateHvacMode.cool,
@@ -171753,9 +171832,7 @@ var config4 = {
171753
171832
  const direction = getHeatCoolOnlyDirection(entity, agent);
171754
171833
  return direction === "cooling" ? Thermostat3.SystemMode.Cool : Thermostat3.SystemMode.Heat;
171755
171834
  }
171756
- const hasCoolCapability = modes.includes(ClimateHvacMode.cool) || modes.includes(ClimateHvacMode.heat_cool);
171757
- const hasHeatCapability = modes.includes(ClimateHvacMode.heat) || modes.includes(ClimateHvacMode.heat_cool);
171758
- const hasMatterAuto = (modes.includes(ClimateHvacMode.heat_cool) || modes.includes(ClimateHvacMode.auto)) && hasCoolCapability && hasHeatCapability;
171835
+ const hasMatterAuto = modes.includes(ClimateHvacMode.heat_cool) && (modes.includes(ClimateHvacMode.heat) || modes.includes(ClimateHvacMode.cool));
171759
171836
  if (hasMatterAuto) {
171760
171837
  return systemMode;
171761
171838
  }
@@ -171769,11 +171846,34 @@ var config4 = {
171769
171846
  if (hasCooling && !hasHeating) {
171770
171847
  return Thermostat3.SystemMode.Cool;
171771
171848
  }
171849
+ const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171850
+ const entityId = homeAssistant.entityId;
171772
171851
  const action = attributes4(entity).hvac_action;
171773
171852
  if (action === ClimateHvacAction.cooling) {
171853
+ lastHvacDirection.set(entityId, "cooling");
171774
171854
  return Thermostat3.SystemMode.Cool;
171775
171855
  }
171776
- return Thermostat3.SystemMode.Heat;
171856
+ if (action === ClimateHvacAction.heating) {
171857
+ lastHvacDirection.set(entityId, "heating");
171858
+ return Thermostat3.SystemMode.Heat;
171859
+ }
171860
+ const remembered = lastHvacDirection.get(entityId);
171861
+ if (remembered) {
171862
+ return remembered === "cooling" ? Thermostat3.SystemMode.Cool : Thermostat3.SystemMode.Heat;
171863
+ }
171864
+ const current = attributes4(entity).current_temperature;
171865
+ const target = attributes4(entity).temperature;
171866
+ if (typeof current === "number" && typeof target === "number") {
171867
+ if (current > target) {
171868
+ lastHvacDirection.set(entityId, "cooling");
171869
+ return Thermostat3.SystemMode.Cool;
171870
+ }
171871
+ if (current < target) {
171872
+ lastHvacDirection.set(entityId, "heating");
171873
+ return Thermostat3.SystemMode.Heat;
171874
+ }
171875
+ }
171876
+ return Thermostat3.SystemMode.Cool;
171777
171877
  }
171778
171878
  return systemMode;
171779
171879
  },
@@ -171835,19 +171935,37 @@ var config4 = {
171835
171935
  data: { hvac_mode: targetMode }
171836
171936
  };
171837
171937
  },
171838
- setTargetTemperature: (value, agent) => ({
171839
- action: "climate.set_temperature",
171840
- data: {
171841
- temperature: value.toUnit(getUnit(agent))
171842
- }
171843
- }),
171844
- setTargetTemperatureRange: ({ low, high }, agent) => ({
171845
- action: "climate.set_temperature",
171846
- data: {
171847
- target_temp_low: low.toUnit(getUnit(agent)),
171848
- target_temp_high: high.toUnit(getUnit(agent))
171849
- }
171850
- })
171938
+ setTargetTemperature: (value, agent) => {
171939
+ const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171940
+ const entity = homeAssistant.entity.state;
171941
+ const unit = getUnit(agent);
171942
+ const target = value.toUnit(unit);
171943
+ const step = getStep(entity);
171944
+ const current = getRefTemp(entity, "temperature") ?? getRefTemp(entity, "target_temperature");
171945
+ return {
171946
+ action: "climate.set_temperature",
171947
+ data: {
171948
+ temperature: snapToStep(target, current, step)
171949
+ }
171950
+ };
171951
+ },
171952
+ setTargetTemperatureRange: ({ low, high }, agent) => {
171953
+ const homeAssistant = agent.get(HomeAssistantEntityBehavior);
171954
+ const entity = homeAssistant.entity.state;
171955
+ const unit = getUnit(agent);
171956
+ const step = getStep(entity);
171957
+ const lowTarget = low.toUnit(unit);
171958
+ const highTarget = high.toUnit(unit);
171959
+ const lowCurrent = getRefTemp(entity, "target_temp_low") ?? getRefTemp(entity, "temperature");
171960
+ const highCurrent = getRefTemp(entity, "target_temp_high") ?? getRefTemp(entity, "temperature");
171961
+ return {
171962
+ action: "climate.set_temperature",
171963
+ data: {
171964
+ target_temp_low: snapToStep(lowTarget, lowCurrent, step),
171965
+ target_temp_high: snapToStep(highTarget, highCurrent, step)
171966
+ }
171967
+ };
171968
+ }
171851
171969
  };
171852
171970
  function ClimateThermostatServer(initialState = {}, features2) {
171853
171971
  return ThermostatServer2(config4, initialState, features2);
@@ -171941,7 +172059,7 @@ function ClimateDevice(homeAssistantEntity) {
171941
172059
  minCoolSetpointLimit: toMatterTemp(attributes7.min_temp) ?? 0,
171942
172060
  maxCoolSetpointLimit: toMatterTemp(attributes7.max_temp) ?? 5e3
171943
172061
  };
171944
- const autoMode = supportsHeating && supportsCooling && (attributes7.hvac_modes.includes(ClimateHvacMode.heat_cool) || attributes7.hvac_modes.includes(ClimateHvacMode.auto)) && (attributes7.hvac_modes.includes(ClimateHvacMode.heat) || attributes7.hvac_modes.includes(ClimateHvacMode.cool));
172062
+ const autoMode = supportsHeating && supportsCooling && attributes7.hvac_modes.includes(ClimateHvacMode.heat_cool) && (attributes7.hvac_modes.includes(ClimateHvacMode.heat) || attributes7.hvac_modes.includes(ClimateHvacMode.cool));
171945
172063
  return ClimateDeviceType(
171946
172064
  supportsOnOff,
171947
172065
  supportsHumidity,
@@ -172066,6 +172184,7 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
172066
172184
  this.state.targetPositionTiltPercent100ths = null;
172067
172185
  }
172068
172186
  }
172187
+ this.state.type = this.features.lift && this.features.tilt ? WindowCovering3.WindowCoveringType.TiltBlindLift : this.features.tilt ? WindowCovering3.WindowCoveringType.TiltBlindTiltOnly : WindowCovering3.WindowCoveringType.Rollershade;
172069
172188
  await super.initialize();
172070
172189
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
172071
172190
  this.update(homeAssistant.entity);
@@ -172184,7 +172303,7 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
172184
172303
  } else if (type === MovementType.Tilt) {
172185
172304
  if (targetPercent100ths == null && this.lastLiftMovementDirection === direction && Date.now() - this.lastLiftMovementMs < 50) {
172186
172305
  logger179.info(
172187
- `Skipping tilt ${MovementDirection[direction]} \u2014 lift already moving in same direction`
172306
+ `Skipping tilt ${MovementDirection[direction]}, lift already moving in same direction`
172188
172307
  );
172189
172308
  return;
172190
172309
  }
@@ -172358,6 +172477,13 @@ var DEVICE_CLASS_TO_MATTER_TYPE = {
172358
172477
  shade: {
172359
172478
  type: WindowCovering3.WindowCoveringType.Rollershade,
172360
172479
  endProductType: WindowCovering3.EndProductType.RollerShade
172480
+ },
172481
+ // Velux-style motorized roof/casement windows. There's no Matter
172482
+ // WindowCoveringType for "window", so we map to lift-only Rollershade
172483
+ // and use Unknown end-product to avoid implying a specific physical form.
172484
+ window: {
172485
+ type: WindowCovering3.WindowCoveringType.Rollershade,
172486
+ endProductType: WindowCovering3.EndProductType.Unknown
172361
172487
  }
172362
172488
  };
172363
172489
  var deviceClassMapping = (entity) => {
@@ -172366,12 +172492,21 @@ var deviceClassMapping = (entity) => {
172366
172492
  const mapping = DEVICE_CLASS_TO_MATTER_TYPE[raw.toLowerCase()];
172367
172493
  if (!mapping) return void 0;
172368
172494
  const supportedFeatures = attributes5(entity).supported_features ?? 0;
172495
+ const hasLift = (supportedFeatures & CoverSupportedFeatures.support_open) !== 0;
172369
172496
  const hasTilt = (supportedFeatures & CoverSupportedFeatures.support_open_tilt) !== 0;
172370
- if (mapping.type === WindowCovering3.WindowCoveringType.TiltBlindTiltOnly && !hasTilt) {
172371
- return {
172372
- type: WindowCovering3.WindowCoveringType.Rollershade,
172373
- endProductType: mapping.endProductType
172374
- };
172497
+ if (mapping.type === WindowCovering3.WindowCoveringType.TiltBlindTiltOnly) {
172498
+ if (!hasTilt) {
172499
+ return {
172500
+ type: WindowCovering3.WindowCoveringType.Rollershade,
172501
+ endProductType: mapping.endProductType
172502
+ };
172503
+ }
172504
+ if (hasLift) {
172505
+ return {
172506
+ type: WindowCovering3.WindowCoveringType.TiltBlindLift,
172507
+ endProductType: mapping.endProductType
172508
+ };
172509
+ }
172375
172510
  }
172376
172511
  return mapping;
172377
172512
  };
@@ -174603,7 +174738,7 @@ var SpeakerLevelControlServerBase = class extends FeaturedBase8 {
174603
174738
  *
174604
174739
  * The base class registers a reactor on onOff$Changed that sets
174605
174740
  * currentLevel = onLevel. This is designed for lights (restore brightness
174606
- * on power-on) but is wrong for speakers it overwrites the correct
174741
+ * on power-on) but is wrong for speakers, it overwrites the correct
174607
174742
  * volume (e.g. 191 for 75%) with a stale onLevel value, causing Google
174608
174743
  * Home to display the wrong percentage (Issue #79).
174609
174744
  */
@@ -174950,7 +175085,7 @@ var AirQualitySensorServerImpl = class extends AirQualityServerWithFeatures {
174950
175085
  await super.initialize();
174951
175086
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
174952
175087
  this.update(homeAssistant.entity);
174953
- this.reactTo(homeAssistant.onChange, this.update);
175088
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
174954
175089
  }
174955
175090
  update(entity) {
174956
175091
  if (!entity.state || !entity.state.attributes) {
@@ -175061,7 +175196,7 @@ var CarbonMonoxideConcentrationMeasurementServer2 = class extends CarbonMonoxide
175061
175196
  await super.initialize();
175062
175197
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175063
175198
  this.update(homeAssistant.entity);
175064
- this.reactTo(homeAssistant.onChange, this.update);
175199
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175065
175200
  }
175066
175201
  update(entity) {
175067
175202
  if (!entity.state || !entity.state.attributes) {
@@ -175092,7 +175227,7 @@ var CoAirQualityServer = class extends CoAirQualityServerBase {
175092
175227
  await super.initialize();
175093
175228
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175094
175229
  this.update(homeAssistant.entity);
175095
- this.reactTo(homeAssistant.onChange, this.update);
175230
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175096
175231
  }
175097
175232
  update(entity) {
175098
175233
  const state = entity.state.state;
@@ -175132,7 +175267,7 @@ var StandalonePowerServer = class extends ElectricalPowerMeasurementServer {
175132
175267
  await super.initialize();
175133
175268
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175134
175269
  this.update(homeAssistant.entity);
175135
- this.reactTo(homeAssistant.onChange, this.update);
175270
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175136
175271
  }
175137
175272
  update(entity) {
175138
175273
  if (!entity.state || !entity.state.attributes) {
@@ -175190,7 +175325,7 @@ var StandaloneEnergyServer = class extends EnergyFeaturedBase {
175190
175325
  await super.initialize();
175191
175326
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175192
175327
  this.update(homeAssistant.entity);
175193
- this.reactTo(homeAssistant.onChange, this.update);
175328
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175194
175329
  }
175195
175330
  update(entity) {
175196
175331
  if (!entity.state || !entity.state.attributes) {
@@ -175246,7 +175381,7 @@ var FlowMeasurementServerBase = class extends FlowMeasurementServer {
175246
175381
  await super.initialize();
175247
175382
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175248
175383
  this.update(homeAssistant.entity);
175249
- this.reactTo(homeAssistant.onChange, this.update);
175384
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175250
175385
  }
175251
175386
  update(entity) {
175252
175387
  if (!entity.state || !entity.state.attributes) {
@@ -175338,7 +175473,7 @@ var FormaldehydeConcentrationMeasurementServer2 = class extends FormaldehydeConc
175338
175473
  await super.initialize();
175339
175474
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175340
175475
  this.update(homeAssistant.entity);
175341
- this.reactTo(homeAssistant.onChange, this.update);
175476
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175342
175477
  }
175343
175478
  update(entity) {
175344
175479
  if (!entity.state || !entity.state.attributes) {
@@ -175369,7 +175504,7 @@ var FormaldehydeAirQualityServer = class extends FormaldehydeAirQualityServerBas
175369
175504
  await super.initialize();
175370
175505
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175371
175506
  this.update(homeAssistant.entity);
175372
- this.reactTo(homeAssistant.onChange, this.update);
175507
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175373
175508
  }
175374
175509
  update(entity) {
175375
175510
  const state = entity.state.state;
@@ -175428,7 +175563,7 @@ var IlluminanceMeasurementServerBase = class extends IlluminanceMeasurementServe
175428
175563
  await super.initialize();
175429
175564
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175430
175565
  this.update(homeAssistant.entity);
175431
- this.reactTo(homeAssistant.onChange, this.update);
175566
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175432
175567
  }
175433
175568
  update(entity) {
175434
175569
  if (!entity.state || !entity.state.attributes) {
@@ -175512,7 +175647,7 @@ var NitrogenDioxideConcentrationMeasurementServer2 = class extends NitrogenDioxi
175512
175647
  await super.initialize();
175513
175648
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175514
175649
  this.update(homeAssistant.entity);
175515
- this.reactTo(homeAssistant.onChange, this.update);
175650
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175516
175651
  }
175517
175652
  update(entity) {
175518
175653
  if (!entity.state || !entity.state.attributes) {
@@ -175542,7 +175677,7 @@ var No2AirQualityServer = class extends No2AirQualityServerBase {
175542
175677
  await super.initialize();
175543
175678
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175544
175679
  this.update(homeAssistant.entity);
175545
- this.reactTo(homeAssistant.onChange, this.update);
175680
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175546
175681
  }
175547
175682
  update(entity) {
175548
175683
  const state = entity.state.state;
@@ -175605,7 +175740,7 @@ var OzoneConcentrationMeasurementServer2 = class extends OzoneConcentrationMeasu
175605
175740
  await super.initialize();
175606
175741
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175607
175742
  this.update(homeAssistant.entity);
175608
- this.reactTo(homeAssistant.onChange, this.update);
175743
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175609
175744
  }
175610
175745
  update(entity) {
175611
175746
  if (!entity.state || !entity.state.attributes) {
@@ -175635,7 +175770,7 @@ var OzoneAirQualityServer = class extends OzoneAirQualityServerBase {
175635
175770
  await super.initialize();
175636
175771
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175637
175772
  this.update(homeAssistant.entity);
175638
- this.reactTo(homeAssistant.onChange, this.update);
175773
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175639
175774
  }
175640
175775
  update(entity) {
175641
175776
  const state = entity.state.state;
@@ -175698,7 +175833,7 @@ var Pm1ConcentrationMeasurementServer2 = class extends Pm1ConcentrationMeasureme
175698
175833
  await super.initialize();
175699
175834
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175700
175835
  this.update(homeAssistant.entity);
175701
- this.reactTo(homeAssistant.onChange, this.update);
175836
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175702
175837
  }
175703
175838
  update(entity) {
175704
175839
  if (!entity.state || !entity.state.attributes) {
@@ -175728,7 +175863,7 @@ var Pm1AirQualityServer = class extends Pm1AirQualityServerBase {
175728
175863
  await super.initialize();
175729
175864
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175730
175865
  this.update(homeAssistant.entity);
175731
- this.reactTo(homeAssistant.onChange, this.update);
175866
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175732
175867
  }
175733
175868
  update(entity) {
175734
175869
  const state = entity.state.state;
@@ -175811,7 +175946,7 @@ var RadonConcentrationMeasurementServer2 = class extends RadonConcentrationMeasu
175811
175946
  await super.initialize();
175812
175947
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175813
175948
  this.update(homeAssistant.entity);
175814
- this.reactTo(homeAssistant.onChange, this.update);
175949
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175815
175950
  }
175816
175951
  update(entity) {
175817
175952
  if (!entity.state || !entity.state.attributes) {
@@ -175841,7 +175976,7 @@ var RadonAirQualityServer = class extends RadonAirQualityServerBase {
175841
175976
  await super.initialize();
175842
175977
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175843
175978
  this.update(homeAssistant.entity);
175844
- this.reactTo(homeAssistant.onChange, this.update);
175979
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175845
175980
  }
175846
175981
  update(entity) {
175847
175982
  const state = entity.state.state;
@@ -175959,7 +176094,7 @@ var TvocConcentrationMeasurementServer = class extends TvocConcentrationMeasurem
175959
176094
  await super.initialize();
175960
176095
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
175961
176096
  this.update(homeAssistant.entity);
175962
- this.reactTo(homeAssistant.onChange, this.update);
176097
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
175963
176098
  }
175964
176099
  update(entity) {
175965
176100
  if (!entity.state || !entity.state.attributes) {
@@ -176009,7 +176144,7 @@ var TvocAirQualityServer = class extends TvocAirQualityServerBase {
176009
176144
  await super.initialize();
176010
176145
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176011
176146
  this.update(homeAssistant.entity);
176012
- this.reactTo(homeAssistant.onChange, this.update);
176147
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176013
176148
  }
176014
176149
  update(entity) {
176015
176150
  if (!entity.state || !entity.state.attributes) {
@@ -176076,7 +176211,7 @@ var CarbonDioxideConcentrationMeasurementServer2 = class extends CarbonDioxideCo
176076
176211
  await super.initialize();
176077
176212
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176078
176213
  this.update(homeAssistant.entity);
176079
- this.reactTo(homeAssistant.onChange, this.update);
176214
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176080
176215
  }
176081
176216
  update(entity) {
176082
176217
  if (!entity.state || !entity.state.attributes) {
@@ -176109,7 +176244,7 @@ var Co2AirQualityServer = class extends Co2AirQualityServerBase {
176109
176244
  await super.initialize();
176110
176245
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176111
176246
  this.update(homeAssistant.entity);
176112
- this.reactTo(homeAssistant.onChange, this.update);
176247
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176113
176248
  }
176114
176249
  update(entity) {
176115
176250
  const state = entity.state.state;
@@ -176172,7 +176307,7 @@ var Pm10ConcentrationMeasurementServer2 = class extends Pm10ConcentrationMeasure
176172
176307
  await super.initialize();
176173
176308
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176174
176309
  this.update(homeAssistant.entity);
176175
- this.reactTo(homeAssistant.onChange, this.update);
176310
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176176
176311
  }
176177
176312
  update(entity) {
176178
176313
  if (!entity.state || !entity.state.attributes) {
@@ -176204,7 +176339,7 @@ var Pm10AirQualityServer = class extends Pm10AirQualityServerBase {
176204
176339
  await super.initialize();
176205
176340
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176206
176341
  this.update(homeAssistant.entity);
176207
- this.reactTo(homeAssistant.onChange, this.update);
176342
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176208
176343
  }
176209
176344
  update(entity) {
176210
176345
  const state = entity.state.state;
@@ -176276,7 +176411,7 @@ var Pm25ConcentrationMeasurementServer2 = class extends Pm25ConcentrationMeasure
176276
176411
  );
176277
176412
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176278
176413
  this.update(homeAssistant.entity);
176279
- this.reactTo(homeAssistant.onChange, this.update);
176414
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176280
176415
  }
176281
176416
  update(entity) {
176282
176417
  if (!entity.state || !entity.state.attributes) {
@@ -176311,7 +176446,7 @@ var Pm25AirQualityServer = class extends Pm25AirQualityServerBase {
176311
176446
  logger189.debug("Pm25AirQualityServer: after super.initialize()");
176312
176447
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
176313
176448
  this.update(homeAssistant.entity);
176314
- this.reactTo(homeAssistant.onChange, this.update);
176449
+ this.reactTo(homeAssistant.onChange, this.update, { offline: true });
176315
176450
  }
176316
176451
  update(entity) {
176317
176452
  const state = entity.state.state;
@@ -176608,7 +176743,7 @@ function SensorDevice(homeAssistantEntity) {
176608
176743
  if (deviceClass) {
176609
176744
  diagnosticEventBus.emit(
176610
176745
  "entity_warning",
176611
- `Sensor "${homeAssistantEntity.entity.entity_id}" has unsupported device_class "${deviceClass}" \u2014 skipped`,
176746
+ `Sensor "${homeAssistantEntity.entity.entity_id}" has unsupported device_class "${deviceClass}", skipped`,
176612
176747
  {
176613
176748
  entityId: homeAssistantEntity.entity.entity_id,
176614
176749
  details: { device_class: deviceClass }
@@ -176695,6 +176830,7 @@ init_dist();
176695
176830
  init_esm();
176696
176831
  init_home_assistant_entity_behavior();
176697
176832
  var logger190 = Logger.get("VacuumIdentifyServer");
176833
+ var IDENTIFY_BUTTON_SUFFIXES = ["_identify", "_locate", "_find_me"];
176698
176834
  var VacuumIdentifyServer = class extends IdentifyServer2 {
176699
176835
  triggerEffect(effect) {
176700
176836
  this.#locate("triggerEffect");
@@ -176709,16 +176845,40 @@ var VacuumIdentifyServer = class extends IdentifyServer2 {
176709
176845
  #locate(source) {
176710
176846
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
176711
176847
  const features2 = homeAssistant.entity.state.attributes.supported_features ?? 0;
176712
- const hasLocate = testBit(features2, VacuumDeviceFeature.LOCATE);
176713
- if (!hasLocate) {
176714
- logger190.warn(
176715
- `${source} for ${homeAssistant.entityId} \u2014 LOCATE not in supported_features (${features2}), calling vacuum.locate anyway`
176716
- );
176717
- } else {
176848
+ if (testBit(features2, VacuumDeviceFeature.LOCATE)) {
176718
176849
  logger190.info(`${source} \u2192 vacuum.locate for ${homeAssistant.entityId}`);
176850
+ homeAssistant.callAction({ action: "vacuum.locate" });
176851
+ return;
176719
176852
  }
176853
+ const sibling = this.#findIdentifyButton(homeAssistant);
176854
+ if (sibling) {
176855
+ logger190.info(
176856
+ `${source} \u2192 button.press ${sibling} for ${homeAssistant.entityId}`
176857
+ );
176858
+ homeAssistant.callAction({ action: "button.press", target: sibling });
176859
+ return;
176860
+ }
176861
+ logger190.warn(
176862
+ `${source} for ${homeAssistant.entityId}, LOCATE not in supported_features (${features2}), trying vacuum.locate anyway`
176863
+ );
176720
176864
  homeAssistant.callAction({ action: "vacuum.locate" });
176721
176865
  }
176866
+ #findIdentifyButton(homeAssistant) {
176867
+ const deviceId = homeAssistant.entity.registry?.device_id;
176868
+ if (!deviceId) return void 0;
176869
+ const registry2 = this.env.get(HomeAssistantRegistry);
176870
+ for (const entity of Object.values(registry2.entities)) {
176871
+ if (entity.device_id !== deviceId) continue;
176872
+ if (!entity.entity_id.startsWith("button.")) continue;
176873
+ const uniqueId = entity.unique_id ?? "";
176874
+ if (IDENTIFY_BUTTON_SUFFIXES.some(
176875
+ (s) => entity.entity_id.endsWith(s) || uniqueId.endsWith(s)
176876
+ )) {
176877
+ return entity.entity_id;
176878
+ }
176879
+ }
176880
+ return void 0;
176881
+ }
176722
176882
  };
176723
176883
 
176724
176884
  // src/matter/behaviors/rvc-run-mode-server.ts
@@ -176829,7 +176989,7 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
176829
176989
  if (!currentRoomEntityId) {
176830
176990
  this.logShortCircuitOnce(
176831
176991
  "no-mapping",
176832
- "currentRoom sensor: no currentRoomEntity in mapping \u2014 auto-detect did not run or sensor not on same HA device"
176992
+ "currentRoom sensor: no currentRoomEntity in mapping, auto-detect did not run or sensor not on same HA device"
176833
176993
  );
176834
176994
  return;
176835
176995
  }
@@ -176842,26 +177002,22 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
176842
177002
  );
176843
177003
  return;
176844
177004
  }
176845
- if (s.activeAreas.length === 0) {
176846
- this.logShortCircuitOnce(
176847
- "no-active-areas",
176848
- `currentRoom sensor: activeAreas empty while cleaning \u2014 sensor=${currentRoomEntityId} state="${roomState.state}"`
176849
- );
176850
- return;
176851
- }
176852
177005
  const serviceArea = this.agent.get(ServiceAreaBehavior);
177006
+ const externalSession = s.activeAreas.length === 0;
177007
+ const supportedAreaIds = serviceArea.state.supportedAreas.map(
177008
+ (a) => a.areaId
177009
+ );
177010
+ const isAllowedArea = (id) => externalSession ? supportedAreaIds.includes(id) : s.activeAreas.includes(id);
176853
177011
  const sensorAttrs = roomState.attributes;
176854
177012
  const segmentId = sensorAttrs.segment_id ?? sensorAttrs.room_id;
176855
177013
  const roomName = roomState.state;
176856
177014
  let matchedAreaId = null;
176857
- if (segmentId != null) {
176858
- if (s.activeAreas.includes(segmentId)) {
176859
- matchedAreaId = segmentId;
176860
- }
177015
+ if (segmentId != null && isAllowedArea(segmentId)) {
177016
+ matchedAreaId = segmentId;
176861
177017
  }
176862
177018
  if (matchedAreaId === null && segmentId != null) {
176863
177019
  for (const area of serviceArea.state.supportedAreas) {
176864
- if (s.activeAreas.includes(area.areaId) && area.areaId % 1e4 === segmentId) {
177020
+ if (isAllowedArea(area.areaId) && area.areaId % 1e4 === segmentId) {
176865
177021
  matchedAreaId = area.areaId;
176866
177022
  break;
176867
177023
  }
@@ -176871,7 +177027,7 @@ var RvcRunModeServerBase = class extends RvcRunModeServer {
176871
177027
  const area = serviceArea.state.supportedAreas.find(
176872
177028
  (a) => a.areaInfo.locationInfo?.locationName?.toLowerCase() === roomName.toLowerCase()
176873
177029
  );
176874
- if (area && s.activeAreas.includes(area.areaId)) {
177030
+ if (area && isAllowedArea(area.areaId)) {
176875
177031
  matchedAreaId = area.areaId;
176876
177032
  }
176877
177033
  }
@@ -178210,33 +178366,24 @@ function buildSupportedModes3(fanSpeedList, mopIntensityList, cleaningModeOption
178210
178366
  }
178211
178367
  return modes;
178212
178368
  }
178213
- var CLEANING_MODE_ALIASES = {
178214
- [0 /* Sweeping */]: [
178215
- "Sweeping",
178216
- "Vacuum",
178217
- "Vacuuming",
178218
- "Sweep",
178219
- "vacuum",
178220
- "sweeping"
178221
- ],
178222
- [1 /* Mopping */]: ["Mopping", "Mop", "mopping", "mop", "wet_mop"],
178223
- [2 /* SweepingAndMopping */]: [
178224
- "Sweeping and mopping",
178225
- "Vacuum and mop",
178226
- "Vacuum & Mop",
178227
- "Vacuum & mop",
178228
- "vacuum_and_mop",
178229
- "sweeping_and_mopping"
178230
- ],
178231
- [3 /* MoppingAfterSweeping */]: [
178232
- "Mopping after sweeping",
178233
- "mopping_after_sweeping",
178234
- "Vacuum then mop",
178235
- "Mop after vacuum",
178236
- "vacuum_then_mop",
178237
- "mop_after_vacuum"
178238
- ]
178369
+ var CLEAN_TOKENS = {
178370
+ vacuum: /\b(vacuum|vacuuming|sweep|sweeping|suction)\b/i,
178371
+ mop: /\b(mop|mopping|wipe|wet|wash|scrub)\b/i,
178372
+ sequential: /\b(then|after|before|followed|following|first|secondly|sequentially)\b/i
178239
178373
  };
178374
+ function normalizeCleanLabel(s) {
178375
+ return s.toLowerCase().replace(/[_\-+&/. ]+/g, " ").replace(/\band\b/g, " ").trim();
178376
+ }
178377
+ function classifyCleanOption(option) {
178378
+ const s = normalizeCleanLabel(option);
178379
+ const hasVac = CLEAN_TOKENS.vacuum.test(s);
178380
+ const hasMop = CLEAN_TOKENS.mop.test(s);
178381
+ const hasSeq = CLEAN_TOKENS.sequential.test(s);
178382
+ if (hasVac && hasMop && hasSeq) return 3 /* MoppingAfterSweeping */;
178383
+ if (hasVac && hasMop) return 2 /* SweepingAndMopping */;
178384
+ if (hasMop) return 1 /* Mopping */;
178385
+ return 0 /* Sweeping */;
178386
+ }
178240
178387
  var CLEAN_TYPE_LABELS = {
178241
178388
  [0 /* Sweeping */]: "Sweeping",
178242
178389
  [1 /* Mopping */]: "Mopping",
@@ -178369,17 +178516,7 @@ function matchMopIntensityOption(name, availableOptions) {
178369
178516
  }
178370
178517
  function parseCleanType(modeString) {
178371
178518
  if (!modeString) return 0 /* Sweeping */;
178372
- const s = modeString.toLowerCase();
178373
- if (s.includes("mopping after") || s.includes("after sweeping") || s.includes("then_mop") || s.includes("then mop")) {
178374
- return 3 /* MoppingAfterSweeping */;
178375
- }
178376
- if (s.includes("and") || s.includes("sweeping and mopping")) {
178377
- return 2 /* SweepingAndMopping */;
178378
- }
178379
- if (s === "mopping" || s.includes("mop")) {
178380
- return 1 /* Mopping */;
178381
- }
178382
- return 0 /* Sweeping */;
178519
+ return classifyCleanOption(modeString);
178383
178520
  }
178384
178521
  function cleanTypeToModeId(ct) {
178385
178522
  switch (ct) {
@@ -178420,30 +178557,20 @@ var CLEAN_TYPE_FALLBACK = {
178420
178557
  [3 /* MoppingAfterSweeping */]: 2 /* SweepingAndMopping */
178421
178558
  };
178422
178559
  function findMatchingCleanOption(ct, availableOptions) {
178423
- const aliases = CLEANING_MODE_ALIASES[ct];
178424
- if (!availableOptions || availableOptions.length === 0) return aliases[0];
178560
+ if (!availableOptions || availableOptions.length === 0) {
178561
+ return CLEAN_TYPE_LABELS[ct];
178562
+ }
178425
178563
  const typesToTry = [ct];
178426
178564
  const fallback = CLEAN_TYPE_FALLBACK[ct];
178427
178565
  if (fallback !== void 0) typesToTry.push(fallback);
178428
178566
  for (const type of typesToTry) {
178429
- const typeAliases = CLEANING_MODE_ALIASES[type];
178430
- for (const alias of typeAliases) {
178431
- const match = availableOptions.find(
178432
- (o) => o.toLowerCase() === alias.toLowerCase()
178433
- );
178434
- if (match) return match;
178435
- }
178436
- for (const alias of typeAliases) {
178437
- const match = availableOptions.find(
178438
- (o) => o.toLowerCase().includes(alias.toLowerCase())
178439
- );
178440
- if (match) return match;
178441
- }
178567
+ const match = availableOptions.find((o) => classifyCleanOption(o) === type);
178568
+ if (match) return match;
178442
178569
  }
178443
178570
  logger196.warn(
178444
178571
  `No match for ${CLEAN_TYPE_LABELS[ct]} in [${availableOptions.join(", ")}]`
178445
178572
  );
178446
- return aliases[0];
178573
+ return availableOptions[0];
178447
178574
  }
178448
178575
  function buildCleaningModeAction(targetCleanType, agent) {
178449
178576
  const selectEntityId = getCleaningModeSelectEntity(agent);
@@ -178578,7 +178705,7 @@ function createCleanModeConfig(fanSpeedList, mopIntensityList, cleaningModeOptio
178578
178705
  const vacuumEntityId = homeAssistant.entityId;
178579
178706
  const mapping = homeAssistant.state.mapping;
178580
178707
  logger196.info(
178581
- `setCleanMode(${mode}) for ${vacuumEntityId} \u2014 suctionEntity=${mapping?.suctionLevelEntity ?? "none"}, mopEntity=${mapping?.mopIntensityEntity ?? "none"}, fanSpeedList=${JSON.stringify(fanSpeedList ?? [])}, mopIntensityList=${JSON.stringify(mopIntensityList ?? [])}, customTags=${JSON.stringify(customFanSpeedTags ?? {})}`
178708
+ `setCleanMode(${mode}) for ${vacuumEntityId}, suctionEntity=${mapping?.suctionLevelEntity ?? "none"}, mopEntity=${mapping?.mopIntensityEntity ?? "none"}, fanSpeedList=${JSON.stringify(fanSpeedList ?? [])}, mopIntensityList=${JSON.stringify(mopIntensityList ?? [])}, customTags=${JSON.stringify(customFanSpeedTags ?? {})}`
178582
178709
  );
178583
178710
  if (mopIntensityList && mopIntensityList.length > 0 && isMopIntensityMode(mode)) {
178584
178711
  const mopIndex = mode - MOP_INTENSITY_MODE_BASE;
@@ -180545,7 +180672,7 @@ var BridgeEndpointManager = class extends Service {
180545
180672
  this.mappingFingerprints.delete(endpoint.entityId);
180546
180673
  } else if (this.registry.isAutoComposedDevicesEnabled() && this.registry.isComposedSubEntityUsed(endpoint.entityId)) {
180547
180674
  this.log.info(
180548
- `Deleting standalone endpoint ${endpoint.entityId} \u2014 consumed by composed device`
180675
+ `Deleting standalone endpoint ${endpoint.entityId}, consumed by composed device`
180549
180676
  );
180550
180677
  try {
180551
180678
  await endpoint.delete();
@@ -180583,14 +180710,14 @@ var BridgeEndpointManager = class extends Service {
180583
180710
  if (!memoryLimitReached && isHeapUnderPressure()) {
180584
180711
  memoryLimitReached = true;
180585
180712
  this.log.error(
180586
- "Memory pressure detected \u2014 skipping remaining entities to prevent OOM crash. Reduce the number of entities in this bridge or increase the Node.js heap size (NODE_OPTIONS=--max-old-space-size=1024)."
180713
+ "Memory pressure detected, skipping remaining entities to prevent OOM crash. Reduce the number of entities in this bridge or increase the Node.js heap size (NODE_OPTIONS=--max-old-space-size=1024)."
180587
180714
  );
180588
180715
  }
180589
180716
  if (memoryLimitReached) {
180590
180717
  if (!existingEndpoints.some((e) => e.entityId === entityId)) {
180591
180718
  this._failedEntities.push({
180592
180719
  entityId,
180593
- reason: "Skipped due to memory pressure \u2014 reduce entities or increase heap size"
180720
+ reason: "Skipped due to memory pressure, reduce entities or increase heap size"
180594
180721
  });
180595
180722
  }
180596
180723
  continue;
@@ -180602,7 +180729,7 @@ var BridgeEndpointManager = class extends Service {
180602
180729
  }
180603
180730
  if (this.registry.isAutoComposedDevicesEnabled() && this.registry.isComposedSubEntityUsed(entityId)) {
180604
180731
  this.log.debug(
180605
- `Skipping ${entityId} \u2014 already part of a composed device`
180732
+ `Skipping ${entityId}, already part of a composed device`
180606
180733
  );
180607
180734
  continue;
180608
180735
  }
@@ -180702,7 +180829,7 @@ var BridgeEndpointManager = class extends Service {
180702
180829
  }
180703
180830
  /**
180704
180831
  * Log detailed behavior error information for debugging "Behaviors have errors".
180705
- * Matter.js EndpointBehaviorsError extends AggregateError the `errors` array
180832
+ * Matter.js EndpointBehaviorsError extends AggregateError, the `errors` array
180706
180833
  * contains individual behavior crash errors (one per failed behavior).
180707
180834
  */
180708
180835
  logDetailedError(entityId, error) {
@@ -180851,7 +180978,7 @@ var BridgeRegistry = class _BridgeRegistry {
180851
180978
  * Check if auto composed devices mode is enabled.
180852
180979
  * When enabled, temperature sensors with auto-mapped humidity/pressure/battery
180853
180980
  * build real Matter Composed Devices (BridgedNodeEndpoint with sub-endpoints)
180854
- * rather than stacking extra clusters onto a flat TemperatureSensor
180981
+ * rather than stacking extra clusters onto a flat TemperatureSensor.
180855
180982
  * Apple Home, Google Home, and Alexa render each sub-endpoint using its
180856
180983
  * own device type.
180857
180984
  */
@@ -180997,8 +181124,8 @@ var BridgeRegistry = class _BridgeRegistry {
180997
181124
  } else if (id.endsWith("_mode")) {
180998
181125
  const options = state.attributes?.options;
180999
181126
  if (options?.some(
181000
- (o) => /^(vacuum|mop|sweep|vacuum_and_mop|vacuum_then_mop|mopping|sweeping|sweeping_and_mopping|mopping_after_sweeping)$/i.test(
181001
- o
181127
+ (o) => /^(vacuum|mop|sweep|sweep_mop|sweep_before_mopping|sweep_then_mop|vacuum_and_mop|vacuum_then_mop|mopping|sweeping|sweeping_and_mopping|mopping_after_sweeping)$/i.test(
181128
+ o.replace(/\s+/g, "_")
181002
181129
  )
181003
181130
  )) {
181004
181131
  cleaningModeEntity = entity.entity_id;
@@ -181156,14 +181283,14 @@ var BridgeRegistry = class _BridgeRegistry {
181156
181283
  const segments = areaMapping[haAreaId];
181157
181284
  if (!segments || segments.length === 0) {
181158
181285
  _BridgeRegistry.cleanAreaLogger.debug(
181159
- `${entityId}: Skipping HA area ${haAreaId} \u2014 no segments mapped`
181286
+ `${entityId}: Skipping HA area ${haAreaId}, no segments mapped`
181160
181287
  );
181161
181288
  continue;
181162
181289
  }
181163
181290
  if (validSegmentIds && !segments.some((sid) => validSegmentIds.has(sid))) {
181164
181291
  const areaName2 = this.registry.areas.get(haAreaId) ?? haAreaId;
181165
181292
  _BridgeRegistry.cleanAreaLogger.info(
181166
- `${entityId}: Skipping stale HA area "${areaName2}" (${haAreaId}) \u2014 segments [${segments.join(", ")}] no longer exist on vacuum`
181293
+ `${entityId}: Skipping stale HA area "${areaName2}" (${haAreaId}), segments [${segments.join(", ")}] no longer exist on vacuum`
181167
181294
  );
181168
181295
  continue;
181169
181296
  }
@@ -181639,7 +181766,7 @@ ${e?.toString()}`);
181639
181766
  );
181640
181767
  if (totalSubs === 0 && sessions.length > 0) {
181641
181768
  this.log.warn(
181642
- `All subscriptions lost \u2014 ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
181769
+ `All subscriptions lost, ${sessions.length} session(s) still active, waiting for controller to re-subscribe`
181643
181770
  );
181644
181771
  if (!this.deadSessionTimer) {
181645
181772
  this.deadSessionTimer = setTimeout(() => {
@@ -181678,7 +181805,7 @@ ${e?.toString()}`);
181678
181805
  for (const s of [...sessionManager.sessions]) {
181679
181806
  if (s !== newSession && !s.isClosing && s.peerNodeId === newSession.peerNodeId && s.fabric?.fabricIndex === newSession.fabric?.fabricIndex && s.subscriptions.size === 0) {
181680
181807
  this.log.info(
181681
- `Closing stale session ${s.id} (peer ${s.peerNodeId}, 0 subs) \u2014 replaced by session ${newSession.id}`
181808
+ `Closing stale session ${s.id} (peer ${s.peerNodeId}, 0 subs), replaced by session ${newSession.id}`
181682
181809
  );
181683
181810
  s.initiateForceClose().catch(() => {
181684
181811
  });
@@ -182099,7 +182226,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
182099
182226
  }
182100
182227
  }
182101
182228
  } else {
182102
- logger205.warn(`${entityId}: No device_id \u2014 cannot auto-assign battery`);
182229
+ logger205.warn(`${entityId}: No device_id, cannot auto-assign battery`);
182103
182230
  }
182104
182231
  const payload = {
182105
182232
  entity_id: entityId,
@@ -182155,7 +182282,7 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
182155
182282
  * Previous approaches that pushed state through
182156
182283
  * HomeAssistantEntityBehavior failed because the reactor writes
182157
182284
  * (RvcOperationalStateServer.update()) run inside the postCommit
182158
- * phase of the HAEntityBehavior transaction those writes are
182285
+ * phase of the HAEntityBehavior transaction, those writes are
182159
182286
  * buffered but never committed, so no attrsChanged event reaches
182160
182287
  * the ServerSubscription.
182161
182288
  *
@@ -182401,11 +182528,11 @@ var ServerModeEndpointManager = class extends Service {
182401
182528
  }
182402
182529
  if (isHeapUnderPressure()) {
182403
182530
  this.log.error(
182404
- "Memory pressure detected \u2014 cannot create device endpoint. Reduce entities on other bridges or increase the Node.js heap size (NODE_OPTIONS=--max-old-space-size=1024)."
182531
+ "Memory pressure detected, cannot create device endpoint. Reduce entities on other bridges or increase the Node.js heap size (NODE_OPTIONS=--max-old-space-size=1024)."
182405
182532
  );
182406
182533
  this._failedEntities.push({
182407
182534
  entityId,
182408
- reason: "Skipped due to memory pressure \u2014 reduce entities or increase heap size"
182535
+ reason: "Skipped due to memory pressure, reduce entities or increase heap size"
182409
182536
  });
182410
182537
  return;
182411
182538
  }
@@ -182795,7 +182922,7 @@ function patchLevelControlTlv() {
182795
182922
  );
182796
182923
  } else {
182797
182924
  logger206.warn(
182798
- "Failed to patch LevelControl TLV schemas \u2014 field definitions not found. Google Home brightness adjustment may not work."
182925
+ "Failed to patch LevelControl TLV schemas, field definitions not found. Google Home brightness adjustment may not work."
182799
182926
  );
182800
182927
  }
182801
182928
  }