@riddix/hamh 2.0.30 → 2.0.31

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/README.md CHANGED
@@ -37,7 +37,7 @@ of port forwarding etc.
37
37
 
38
38
  | Channel | Branch | Current Version | Description |
39
39
  |---------|--------|-----------------|-------------|
40
- | **Stable** | `main` | v2.0.30 | Production-ready, recommended for most users |
40
+ | **Stable** | `main` | v2.0.31 | Production-ready, recommended for most users |
41
41
  | **Alpha** | `alpha` | v2.1.0-alpha.x | Pre-release with new features, for early adopters |
42
42
  | **Testing** | `testing` | v4.1.0-testing.x | ⚠️ **Highly unstable!** Experimental features, may break |
43
43
 
@@ -52,30 +52,31 @@ of port forwarding etc.
52
52
  ## 🎉 What's New
53
53
 
54
54
  <details>
55
- <summary><strong>📦 Stable Features (v2.0.30)</strong> - Click to expand</summary>
55
+ <summary><strong>📦 Stable Features (v2.0.31)</strong> - Click to expand</summary>
56
56
 
57
- **New in v2.0.30:**
57
+ **New in v2.0.31:**
58
58
 
59
59
  | Feature | Description |
60
60
  |---------|-------------|
61
- | **� Mapped Entity Propagation Fix** | Propagate mapped entity changes (battery, humidity, etc.) to Matter endpoints fixes stale sensor readings |
62
- | **🖥️ API Error Surfacing** | Surface API errors instead of silently swallowing them ([#232](https://github.com/RiDDiX/home-assistant-matter-hub/issues/232)) |
61
+ | **🎮 Controller Profiles & Area Setup** | New bridge wizard with controller-specific profiles (Apple Home, Google Home, Alexa) and area-based bridge setup |
62
+ | **🌀 Fan Speed/Preset Fix** | Fix fan speed and preset control not working from Matter controllers ([#233](https://github.com/RiDDiX/home-assistant-matter-hub/issues/233)) |
63
+ | **💡 Optimistic State Fix** | Prevent stale HA state from reverting optimistic brightness/color updates ([#230](https://github.com/RiDDiX/home-assistant-matter-hub/issues/230)) |
64
+ | **🪟 Cover Target Fix** | Route boundary cover targets to open/close regardless of direction ([#240](https://github.com/RiDDiX/home-assistant-matter-hub/issues/240)) |
65
+ | **�️ Humidity Auto-Mapping Default** | Make autoHumidityMapping default-enabled like autoPressureMapping |
63
66
 
64
- **Previously in v2.0.29:**
67
+ **Previously in v2.0.30:**
65
68
 
66
69
  | Feature | Description |
67
70
  |---------|-------------|
68
- | **� Light currentLevel Fix** | Retain light currentLevel when off to prevent Apple Home 100% brightness on turn-on ([#225](https://github.com/RiDDiX/home-assistant-matter-hub/issues/225)) |
69
- | **�️ Bridge Config Save Fix** | Decouple save button from RJSF schema validation errors ([#232](https://github.com/RiDDiX/home-assistant-matter-hub/issues/232)) |
70
- | **🌀 Fan Device Feature Fix** | Correct FanDeviceFeature TURN_ON/TURN_OFF enum values to match Home Assistant |
71
- | **🌡️ Humidity Auto-Mapping Fix** | Correct autoHumidityMapping schema default to match runtime behavior |
71
+ | **🔗 Mapped Entity Propagation Fix** | Propagate mapped entity changes (battery, humidity, etc.) to Matter endpoints fixes stale sensor readings |
72
+ | **🖥️ API Error Surfacing** | Surface API errors instead of silently swallowing them ([#232](https://github.com/RiDDiX/home-assistant-matter-hub/issues/232)) |
72
73
 
73
74
  </details>
74
75
 
75
76
  <details>
76
77
  <summary><strong>🧪 Alpha Features (v2.1.0-alpha.x)</strong> - Click to expand</summary>
77
78
 
78
- **Alpha is currently in sync with Stable (v2.0.30).** All alpha features have been promoted to stable. New alpha features will appear here as development continues.
79
+ **Alpha is currently in sync with Stable (v2.0.31).** All alpha features have been promoted to stable. New alpha features will appear here as development continues.
79
80
 
80
81
  </details>
81
82
 
@@ -101,6 +102,9 @@ of port forwarding etc.
101
102
  <details>
102
103
  <summary><strong>📜 Previous Stable Versions</strong> - Click to expand</summary>
103
104
 
105
+ ### v2.0.30
106
+ Mapped Entity Propagation Fix, API Error Surfacing
107
+
104
108
  ### v2.0.29
105
109
  Light currentLevel Fix, Bridge Config Save Fix, Fan Device Feature Fix, Humidity Auto-Mapping Fix
106
110
 
@@ -146085,6 +146085,13 @@ var init_clusters2 = __esm({
146085
146085
  }
146086
146086
  });
146087
146087
 
146088
+ // ../common/dist/controller-profiles.js
146089
+ var init_controller_profiles = __esm({
146090
+ "../common/dist/controller-profiles.js"() {
146091
+ "use strict";
146092
+ }
146093
+ });
146094
+
146088
146095
  // ../common/dist/diagnostic-event.js
146089
146096
  var init_diagnostic_event = __esm({
146090
146097
  "../common/dist/diagnostic-event.js"() {
@@ -146694,7 +146701,7 @@ var init_bridge_config_schema = __esm({
146694
146701
  title: "Auto Humidity Mapping",
146695
146702
  description: "Automatically combine humidity sensors with temperature sensors from the same Home Assistant device. When enabled, humidity sensors will be merged into temperature sensors to create combined TemperatureHumiditySensor devices.",
146696
146703
  type: "boolean",
146697
- default: false
146704
+ default: true
146698
146705
  },
146699
146706
  autoPressureMapping: {
146700
146707
  title: "Auto Pressure Mapping",
@@ -146999,6 +147006,7 @@ var init_dist = __esm({
146999
147006
  init_bridge_export();
147000
147007
  init_bridge_templates();
147001
147008
  init_clusters2();
147009
+ init_controller_profiles();
147002
147010
  init_diagnostic_event();
147003
147011
  init_domains();
147004
147012
  init_endpoint_data();
@@ -149304,6 +149312,73 @@ function matterApi(bridgeService, haRegistry) {
149304
149312
  areas.sort((a, b) => a.name.localeCompare(b.name));
149305
149313
  res.status(200).json(areas);
149306
149314
  });
149315
+ router.get("/areas/summary", async (_, res) => {
149316
+ if (!haRegistry) {
149317
+ res.status(503).json({ error: "Home Assistant registry not available" });
149318
+ return;
149319
+ }
149320
+ const supportedDomains = /* @__PURE__ */ new Set([
149321
+ "light",
149322
+ "switch",
149323
+ "sensor",
149324
+ "binary_sensor",
149325
+ "climate",
149326
+ "cover",
149327
+ "fan",
149328
+ "lock",
149329
+ "media_player",
149330
+ "vacuum",
149331
+ "valve",
149332
+ "humidifier",
149333
+ "water_heater",
149334
+ "select",
149335
+ "input_select",
149336
+ "input_boolean",
149337
+ "alarm_control_panel",
149338
+ "event",
149339
+ "automation",
149340
+ "script",
149341
+ "scene"
149342
+ ]);
149343
+ const entities = Object.values(haRegistry.entities);
149344
+ const devices = haRegistry.devices;
149345
+ const states = haRegistry.states;
149346
+ const areaSummary = /* @__PURE__ */ new Map();
149347
+ for (const [areaId, areaName] of haRegistry.areas) {
149348
+ areaSummary.set(areaId, { name: areaName, entityCount: 0, domains: {} });
149349
+ }
149350
+ for (const entity of entities) {
149351
+ if (entity.disabled_by != null) continue;
149352
+ const domain = entity.entity_id.split(".")[0];
149353
+ if (!supportedDomains.has(domain)) continue;
149354
+ const state = states[entity.entity_id];
149355
+ if (!state || state.state === "unavailable") continue;
149356
+ let areaId;
149357
+ const entityAreaId = entity.area_id;
149358
+ if (entityAreaId && haRegistry.areas.has(entityAreaId)) {
149359
+ areaId = entityAreaId;
149360
+ } else {
149361
+ const device = entity.device_id ? devices[entity.device_id] : void 0;
149362
+ const deviceAreaId = device?.area_id;
149363
+ if (deviceAreaId && haRegistry.areas.has(deviceAreaId)) {
149364
+ areaId = deviceAreaId;
149365
+ }
149366
+ }
149367
+ if (!areaId) continue;
149368
+ const summary = areaSummary.get(areaId);
149369
+ if (summary) {
149370
+ summary.entityCount++;
149371
+ summary.domains[domain] = (summary.domains[domain] || 0) + 1;
149372
+ }
149373
+ }
149374
+ const result = [...areaSummary.entries()].map(([area_id, data]) => ({
149375
+ area_id,
149376
+ name: data.name,
149377
+ entityCount: data.entityCount,
149378
+ domains: data.domains
149379
+ })).sort((a, b) => a.name.localeCompare(b.name));
149380
+ res.status(200).json(result);
149381
+ });
149307
149382
  router.get("/filter-values", async (_, res) => {
149308
149383
  if (!haRegistry) {
149309
149384
  res.status(503).json({ error: "Home Assistant registry not available" });
@@ -165933,12 +166008,8 @@ var FanControlServerBase = class extends FeaturedBase3 {
165933
166008
  return;
165934
166009
  }
165935
166010
  this.agent.asLocalActor(() => {
165936
- const percentSetting = Math.floor(speed / this.state.speedMax * 100);
165937
- this.targetPercentSettingChanged(
165938
- percentSetting,
165939
- this.state.percentSetting,
165940
- context
165941
- );
166011
+ const percentage = Math.floor(speed / this.state.speedMax * 100);
166012
+ this.applyPercentageAction(percentage);
165942
166013
  });
165943
166014
  }
165944
166015
  targetFanModeChanged(fanMode, _oldValue, context) {
@@ -165951,16 +166022,12 @@ var FanControlServerBase = class extends FeaturedBase3 {
165951
166022
  return;
165952
166023
  }
165953
166024
  const targetFanMode = FanMode.create(fanMode, this.state.fanModeSequence);
165954
- const config10 = this.state.config;
165955
166025
  if (targetFanMode.mode === FanControl3.FanMode.Auto) {
165956
- homeAssistant.callAction(config10.setAutoMode(void 0, this.agent));
165957
- } else {
165958
- const percentage = targetFanMode.speedPercent();
165959
- this.targetPercentSettingChanged(
165960
- percentage,
165961
- this.state.percentSetting,
165962
- context
166026
+ homeAssistant.callAction(
166027
+ this.state.config.setAutoMode(void 0, this.agent)
165963
166028
  );
166029
+ } else {
166030
+ this.applyPercentageAction(targetFanMode.speedPercent());
165964
166031
  }
165965
166032
  });
165966
166033
  }
@@ -165972,45 +166039,48 @@ var FanControlServerBase = class extends FeaturedBase3 {
165972
166039
  return;
165973
166040
  }
165974
166041
  this.agent.asLocalActor(() => {
165975
- const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
165976
- if (!homeAssistant.isAvailable) {
165977
- return;
165978
- }
165979
- const config10 = this.state.config;
165980
- const supportsPercentage = config10.supportsPercentage(
166042
+ this.applyPercentageAction(percentage);
166043
+ });
166044
+ }
166045
+ applyPercentageAction(percentage) {
166046
+ const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
166047
+ if (!homeAssistant.isAvailable) {
166048
+ return;
166049
+ }
166050
+ const config10 = this.state.config;
166051
+ const supportsPercentage = config10.supportsPercentage(
166052
+ homeAssistant.entity.state,
166053
+ this.agent
166054
+ );
166055
+ if (percentage === 0) {
166056
+ homeAssistant.callAction(config10.turnOff(void 0, this.agent));
166057
+ } else if (supportsPercentage) {
166058
+ const stepSize = config10.getStepSize(
165981
166059
  homeAssistant.entity.state,
165982
166060
  this.agent
165983
166061
  );
165984
- if (percentage === 0) {
165985
- homeAssistant.callAction(config10.turnOff(void 0, this.agent));
165986
- } else if (supportsPercentage) {
165987
- const stepSize = config10.getStepSize(
165988
- homeAssistant.entity.state,
165989
- this.agent
165990
- );
165991
- const roundedPercentage = stepSize && stepSize > 0 ? Math.round(percentage / stepSize) * stepSize : percentage;
165992
- const clampedPercentage = Math.max(
165993
- stepSize ?? 1,
165994
- Math.min(100, roundedPercentage)
166062
+ const roundedPercentage = stepSize && stepSize > 0 ? Math.round(percentage / stepSize) * stepSize : percentage;
166063
+ const clampedPercentage = Math.max(
166064
+ stepSize ?? 1,
166065
+ Math.min(100, roundedPercentage)
166066
+ );
166067
+ homeAssistant.callAction(config10.turnOn(clampedPercentage, this.agent));
166068
+ } else {
166069
+ const presetModes = config10.getPresetModes(homeAssistant.entity.state, this.agent) ?? [];
166070
+ const speedPresets = presetModes.filter(
166071
+ (m) => m.toLowerCase() !== "auto"
166072
+ );
166073
+ if (speedPresets.length > 0) {
166074
+ const presetIndex = Math.min(
166075
+ Math.floor(percentage / 100 * speedPresets.length),
166076
+ speedPresets.length - 1
165995
166077
  );
165996
- homeAssistant.callAction(config10.turnOn(clampedPercentage, this.agent));
165997
- } else {
165998
- const presetModes = config10.getPresetModes(homeAssistant.entity.state, this.agent) ?? [];
165999
- const speedPresets = presetModes.filter(
166000
- (m) => m.toLowerCase() !== "auto"
166078
+ const targetPreset = speedPresets[presetIndex];
166079
+ homeAssistant.callAction(
166080
+ config10.setPresetMode(targetPreset, this.agent)
166001
166081
  );
166002
- if (speedPresets.length > 0) {
166003
- const presetIndex = Math.min(
166004
- Math.floor(percentage / 100 * speedPresets.length),
166005
- speedPresets.length - 1
166006
- );
166007
- const targetPreset = speedPresets[presetIndex];
166008
- homeAssistant.callAction(
166009
- config10.setPresetMode(targetPreset, this.agent)
166010
- );
166011
- }
166012
166082
  }
166013
- });
166083
+ }
166014
166084
  }
166015
166085
  targetAirflowDirectionChanged(airflowDirection, _oldValue, context) {
166016
166086
  if (transactionIsOffline(context)) {
@@ -166150,6 +166220,8 @@ init_home_assistant_entity_behavior();
166150
166220
  init_esm();
166151
166221
  init_home_assistant_entity_behavior();
166152
166222
  var lastTurnOnTimestamps = /* @__PURE__ */ new Map();
166223
+ var optimisticLevelTimestamps = /* @__PURE__ */ new Map();
166224
+ var OPTIMISTIC_LEVEL_COOLDOWN_MS = 2e3;
166153
166225
  function notifyLightTurnedOn(entityId) {
166154
166226
  lastTurnOnTimestamps.set(entityId, Date.now());
166155
166227
  }
@@ -166194,6 +166266,11 @@ var LevelControlServerBase = class extends FeaturedBase4 {
166194
166266
  if (currentLevel != null) {
166195
166267
  currentLevel = Math.min(Math.max(minLevel, currentLevel), maxLevel);
166196
166268
  }
166269
+ const lastOptimistic = optimisticLevelTimestamps.get(entity.entity_id);
166270
+ const inCooldown = lastOptimistic != null && Date.now() - lastOptimistic < OPTIMISTIC_LEVEL_COOLDOWN_MS;
166271
+ if (inCooldown && currentLevel != null) {
166272
+ currentLevel = null;
166273
+ }
166197
166274
  applyPatchState(this.state, {
166198
166275
  minLevel,
166199
166276
  maxLevel,
@@ -166261,6 +166338,7 @@ var LevelControlServerBase = class extends FeaturedBase4 {
166261
166338
  };
166262
166339
  }
166263
166340
  this.state.currentLevel = level;
166341
+ optimisticLevelTimestamps.set(entityId, Date.now());
166264
166342
  homeAssistant.callAction(action);
166265
166343
  }
166266
166344
  };
@@ -168638,9 +168716,9 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
168638
168716
  `handleMovement: type=${MovementType[type]}, direction=${MovementDirection[direction]}, target=${targetPercent100ths}, currentLift=${currentLift}, currentTilt=${currentTilt}, absolutePosition=${this.features.absolutePosition}`
168639
168717
  );
168640
168718
  if (type === MovementType.Lift) {
168641
- if (direction === MovementDirection.Open && (targetPercent100ths == null || targetPercent100ths === 0)) {
168719
+ if (targetPercent100ths === 0) {
168642
168720
  this.handleLiftOpen();
168643
- } else if (direction === MovementDirection.Close && (targetPercent100ths == null || targetPercent100ths === 1e4)) {
168721
+ } else if (targetPercent100ths === 1e4) {
168644
168722
  this.handleLiftClose();
168645
168723
  } else if (targetPercent100ths != null && this.features.absolutePosition) {
168646
168724
  this.handleGoToLiftPosition(targetPercent100ths);
@@ -168650,9 +168728,9 @@ var WindowCoveringServerBase = class _WindowCoveringServerBase extends FeaturedB
168650
168728
  this.handleLiftClose();
168651
168729
  }
168652
168730
  } else if (type === MovementType.Tilt) {
168653
- if (direction === MovementDirection.Open && (targetPercent100ths == null || targetPercent100ths === 0)) {
168731
+ if (targetPercent100ths === 0) {
168654
168732
  this.handleTiltOpen();
168655
- } else if (direction === MovementDirection.Close && (targetPercent100ths == null || targetPercent100ths === 1e4)) {
168733
+ } else if (targetPercent100ths === 1e4) {
168656
168734
  this.handleTiltClose();
168657
168735
  } else if (targetPercent100ths != null && this.features.absolutePosition) {
168658
168736
  this.handleGoToTiltPosition(targetPercent100ths);
@@ -169349,6 +169427,8 @@ init_nodejs();
169349
169427
  // src/matter/behaviors/color-control-server.ts
169350
169428
  init_home_assistant_entity_behavior();
169351
169429
  var logger170 = Logger.get("ColorControlServer");
169430
+ var optimisticColorTimestamps = /* @__PURE__ */ new Map();
169431
+ var OPTIMISTIC_COLOR_COOLDOWN_MS = 2e3;
169352
169432
  var FeaturedBase7 = ColorControlServer.with("ColorTemperature", "HueSaturation");
169353
169433
  var ColorControlServerBase = class extends FeaturedBase7 {
169354
169434
  pendingTransitionTime;
@@ -169427,6 +169507,8 @@ var ColorControlServerBase = class extends FeaturedBase7 {
169427
169507
  const newColorMode = this.getColorModeFromFeatures(
169428
169508
  config10.getCurrentMode(entity.state, this.agent)
169429
169509
  );
169510
+ const lastOptimistic = optimisticColorTimestamps.get(entity.entity_id);
169511
+ const inCooldown = lastOptimistic != null && Date.now() - lastOptimistic < OPTIMISTIC_COLOR_COOLDOWN_MS;
169430
169512
  if (this.features.colorTemperature) {
169431
169513
  const existingMireds = this.state.colorTemperatureMireds;
169432
169514
  if (existingMireds != null) {
@@ -169451,12 +169533,12 @@ var ColorControlServerBase = class extends FeaturedBase7 {
169451
169533
  applyPatchState(this.state, {
169452
169534
  coupleColorTempToLevelMinMireds: minMireds,
169453
169535
  startUpColorTemperatureMireds: startUpMireds,
169454
- colorTemperatureMireds: effectiveMireds
169536
+ ...inCooldown ? {} : { colorTemperatureMireds: effectiveMireds }
169455
169537
  });
169456
169538
  }
169457
169539
  applyPatchState(this.state, {
169458
- colorMode: newColorMode,
169459
- ...this.features.hueSaturation ? {
169540
+ ...inCooldown ? {} : { colorMode: newColorMode },
169541
+ ...this.features.hueSaturation && !inCooldown ? {
169460
169542
  currentHue: hue,
169461
169543
  currentSaturation: saturation
169462
169544
  } : {}
@@ -169483,6 +169565,7 @@ var ColorControlServerBase = class extends FeaturedBase7 {
169483
169565
  colorTemperatureMireds: targetMireds,
169484
169566
  colorMode: ColorControl3.ColorMode.ColorTemperatureMireds
169485
169567
  });
169568
+ optimisticColorTimestamps.set(homeAssistant.entityId, Date.now());
169486
169569
  homeAssistant.callAction(action);
169487
169570
  }
169488
169571
  moveToHueLogic(targetHue) {
@@ -169513,6 +169596,7 @@ var ColorControlServerBase = class extends FeaturedBase7 {
169513
169596
  currentSaturation: targetSaturation,
169514
169597
  colorMode: ColorControl3.ColorMode.CurrentHueAndCurrentSaturation
169515
169598
  });
169599
+ optimisticColorTimestamps.set(homeAssistant.entityId, Date.now());
169516
169600
  homeAssistant.callAction(action);
169517
169601
  }
169518
169602
  applyTransition(action) {
@@ -170816,6 +170900,8 @@ init_home_assistant_entity_behavior();
170816
170900
  init_esm();
170817
170901
  init_home_assistant_entity_behavior();
170818
170902
  var logger175 = Logger.get("SpeakerLevelControlServer");
170903
+ var optimisticLevelTimestamps2 = /* @__PURE__ */ new Map();
170904
+ var OPTIMISTIC_LEVEL_COOLDOWN_MS2 = 2e3;
170819
170905
  var FeaturedBase9 = LevelControlServer.with("OnOff");
170820
170906
  var SpeakerLevelControlServerBase = class extends FeaturedBase9 {
170821
170907
  async initialize() {
@@ -170851,10 +170937,15 @@ var SpeakerLevelControlServerBase = class extends FeaturedBase9 {
170851
170937
  logger175.debug(
170852
170938
  `[${entityId}] Volume update: HA=${currentLevelPercent != null ? Math.round(currentLevelPercent * 100) : "null"}% -> currentLevel=${currentLevel}`
170853
170939
  );
170940
+ const lastOptimistic = optimisticLevelTimestamps2.get(entity.entity_id);
170941
+ const inCooldown = lastOptimistic != null && Date.now() - lastOptimistic < OPTIMISTIC_LEVEL_COOLDOWN_MS2;
170942
+ if (inCooldown && currentLevel != null) {
170943
+ currentLevel = null;
170944
+ }
170854
170945
  applyPatchState(this.state, {
170855
170946
  minLevel,
170856
170947
  maxLevel,
170857
- currentLevel
170948
+ ...currentLevel != null ? { currentLevel } : {}
170858
170949
  });
170859
170950
  }
170860
170951
  async moveToLevel(request) {
@@ -170897,6 +170988,7 @@ var SpeakerLevelControlServerBase = class extends FeaturedBase9 {
170897
170988
  return;
170898
170989
  }
170899
170990
  this.state.currentLevel = level;
170991
+ optimisticLevelTimestamps2.set(entityId, Date.now());
170900
170992
  homeAssistant.callAction(
170901
170993
  config10.moveToLevelPercent(levelPercent, this.agent)
170902
170994
  );
@@ -175445,15 +175537,15 @@ var BridgeRegistry = class _BridgeRegistry {
175445
175537
  }
175446
175538
  /**
175447
175539
  * Check if auto humidity mapping is enabled for this bridge.
175448
- * Default: false (disabled by default, user must explicitly enable).
175540
+ * Default: true (enabled by default).
175449
175541
  * When enabled, humidity sensors on the same device as a temperature sensor
175450
175542
  * are combined into a single TemperatureHumiditySensor endpoint.
175451
175543
  * Note: Apple Home does not display humidity on TemperatureSensorDevice
175452
- * endpoints, so users on Apple Home should keep this disabled.
175544
+ * endpoints, so users on Apple Home should explicitly disable this.
175453
175545
  * See: https://github.com/RiDDiX/home-assistant-matter-hub/issues/133
175454
175546
  */
175455
175547
  isAutoHumidityMappingEnabled() {
175456
- return this.dataProvider.featureFlags?.autoHumidityMapping === true || this.dataProvider.featureFlags?.autoComposedDevices === true;
175548
+ return this.dataProvider.featureFlags?.autoHumidityMapping !== false || this.dataProvider.featureFlags?.autoComposedDevices === true;
175457
175549
  }
175458
175550
  /**
175459
175551
  * Find a humidity sensor entity that belongs to the same HA device.