@riddix/hamh 2.1.0-alpha.629 → 2.1.0-alpha.631

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.38 | Production-ready, recommended for most users |
40
+ | **Stable** | `main` | v2.0.41 | 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,15 +52,40 @@ of port forwarding etc.
52
52
  ## 🎉 What's New
53
53
 
54
54
  <details>
55
- <summary><strong>📦 Stable Features (v2.0.38)</strong> - Click to expand</summary>
55
+ <summary><strong>📦 Stable Features (v2.0.41)</strong> - Click to expand</summary>
56
56
 
57
- **HOTFIX (post v2.0.38):**
57
+ **New in v2.0.41:**
58
+
59
+ | Feature | Description |
60
+ |---------|-------------|
61
+ | 🌡️ Google Home AC offline fix | `DeadFrontBehavior` on climate OnOff cluster so RoomAirConditioner stops showing offline on Google Home ([#302](https://github.com/RiDDiX/home-assistant-matter-hub/issues/302)) |
62
+ | 🪟 Cover device_class mapping | Map HA `garage`/`gate`/`window`/`awning`/etc. to the matching Matter WindowCovering type so voice commands hit the right device type ([#304](https://github.com/RiDDiX/home-assistant-matter-hub/issues/304)) |
63
+ | 📺 LG TV commissioning patch | Local patch on matter.js 0.16.11 to accept long NOC operational cert serials ([#305](https://github.com/RiDDiX/home-assistant-matter-hub/issues/305)) |
64
+ | 💡 Alexa brightness-reset behind flag | Old Alexa brightness-reset heuristic moved behind `alexaPreserveBrightnessOnTurnOn`, default off — Apple Home "set room to 100%" works again ([#306](https://github.com/RiDDiX/home-assistant-matter-hub/issues/306)) |
65
+ | 🌀 Google Home fan speed | Uses `fan.set_percentage` so already-on fans pick up speed changes from Google Home ([#308](https://github.com/RiDDiX/home-assistant-matter-hub/issues/308)) |
66
+ | ❄️ Climate auto mode | Expose Matter Auto mode when HA reports `auto` in `hvac_modes` ([#309](https://github.com/RiDDiX/home-assistant-matter-hub/issues/309)) |
67
+ | 🆔 Server-mode root identity | Root identity now applies as a single transaction, so controllers don't drop devices mid-swap ([#311](https://github.com/RiDDiX/home-assistant-matter-hub/issues/311)) |
68
+ | 🪟 Lift-only blinds | No more `TiltBlindTiltOnly` on covers without tilt — fixes Alexa routines for roller blinds ([#312](https://github.com/RiDDiX/home-assistant-matter-hub/issues/312)) |
69
+ | 🏷️ Per-entity `disableClimateOnOff` | Turn off the OnOff cluster on climate endpoints per entity for controllers that prefer mode-only control |
70
+ | 🔢 `serialNumberSuffix` per bridge | Append a suffix to every entity serial — useful if controllers like Aqara cache stale device data |
71
+ | 📝 `protocolLogLevel` option | Quiet matter.js logs independently from the app log level |
72
+ | 🖥️ Bridge HW/SW version strings | HA device-registry `hw_version`/`sw_version` now show up in Matter BasicInformation on server-mode endpoints |
73
+ | 🎨 Extended color light: XY + enhancedColorMode | XY feature added as mandatory, `enhancedColorMode` mirrors `colorMode` |
74
+ | 🎭 Groups + Scenes | Scenes and Groups clusters added on light, plug, and fan endpoints |
75
+ | 💧 Boolean state configuration | Cluster added on leak, freeze, rain, and contact sensors |
76
+ | 🌍 Spanish translation | New `es` locale ([#314](https://github.com/RiDDiX/home-assistant-matter-hub/pull/314), thanks [@Yllelder](https://github.com/Yllelder)) |
77
+ | 🧵 Matter.js 0.16.11 (pinned) | Kept pinned, local NOC serial patch applied |
78
+ | ⬆️ Dep bumps | Vite 8, jsdom 29, MUI x-tree-view 9, i18next 26, react-i18next 17, TypeScript 6.0.3, biome pinned 2.4.3, pnpm overrides for transitive CVEs |
79
+
80
+ **Reliability & resilience:** parallel bridge stop in `stopAll`/`restartAll`, parallel HA registry fetches, serialized bridge start/stop lifecycle, serialized `updateStates` with plugin listener detach, HA reconnect retry on transient network errors, 30s timeout on `sendMessagePromise`, port-conflict reject on web-api start, graceful shutdown on `/api/backup/restart`, `AppEnvironment` disposal on SIGINT, stale optimistic state sweep, pending debouncer clear, healthcheck 401 fix under basic auth, deep-equal entity attribute comparison, overlap guard for auto-refresh, safer mireds conversion, aligned `colorMode` publishing, surfaced bridge import errors, corrected thermostat running state for unknown modes + drying, unified Node version across Dockerfiles, sourcemaps excluded from npm tarball, unused deps dropped (rxjs, strip-color, lodash), unused `config-validator` utility removed.
81
+
82
+ **Previously in v2.0.39 & v2.0.40 (hotfix releases):**
58
83
  - Fixed crash loop on startup caused by Node 22 native WebSocket dropping connections ([#297](https://github.com/RiDDiX/home-assistant-matter-hub/issues/297), [#299](https://github.com/RiDDiX/home-assistant-matter-hub/issues/299)) — affects both aarch64 (RPi) and amd64
59
84
  - Fixed service initialization errors being silently swallowed, causing the process to hang instead of exiting
60
85
  - Registry fetch now waits for WebSocket reconnect between retries and has increased retry tolerance
61
86
  - Fixed `select`, `input_select`, `siren` domains showing as unsupported in filter preview ([#298](https://github.com/RiDDiX/home-assistant-matter-hub/issues/298))
62
87
 
63
- **New in v2.0.38:**
88
+ **Previously in v2.0.38:**
64
89
 
65
90
  | Feature | Description |
66
91
  |---------|-------------|
@@ -102,7 +127,7 @@ of port forwarding etc.
102
127
  <details>
103
128
  <summary><strong>🧪 Alpha Features (v2.1.0-alpha.x)</strong> - Click to expand</summary>
104
129
 
105
- **Alpha is currently level with Stable (v2.0.38).** All alpha features from the v2.1.0-alpha.600 line have been promoted into v2.0.38. New alpha work continues from `v2.1.0-alpha.601` onward and will appear here as development progresses.
130
+ **Alpha is currently level with Stable (v2.0.41).** All alpha work from the v2.1.0-alpha.601 line through v2.1.0-alpha.626 has been promoted into v2.0.41. New alpha work continues from `v2.1.0-alpha.627` onward and will appear here as development progresses.
106
131
 
107
132
  </details>
108
133
 
@@ -128,6 +153,12 @@ of port forwarding etc.
128
153
  <details>
129
154
  <summary><strong>📜 Previous Stable Versions</strong> - Click to expand</summary>
130
155
 
156
+ ### v2.0.40
157
+ Filter preview domain fix — `select`, `input_select`, `siren` now show as supported ([#298](https://github.com/RiDDiX/home-assistant-matter-hub/issues/298))
158
+
159
+ ### v2.0.39
160
+ Node 22 WebSocket crash loop fix ([#297](https://github.com/RiDDiX/home-assistant-matter-hub/issues/297), [#299](https://github.com/RiDDiX/home-assistant-matter-hub/issues/299)), service init error surfacing, registry retry hardening, support link added
161
+
131
162
  ### v2.0.37
132
163
  Aqara productNameFromNodeLabel flag, Matter.js 0.16.11, Swedish locale update
133
164
 
@@ -147874,6 +147874,7 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
147874
147874
  valetudoIdentifier: config10.valetudoIdentifier,
147875
147875
  coverSwapOpenClose: config10.coverSwapOpenClose,
147876
147876
  disableClimateOnOff: config10.disableClimateOnOff,
147877
+ disableClimateFanControl: config10.disableClimateFanControl,
147877
147878
  customServiceAreas: config10.customServiceAreas,
147878
147879
  customFanSpeedTags: config10.customFanSpeedTags,
147879
147880
  composedEntities: config10.composedEntities
@@ -148020,6 +148021,7 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
148020
148021
  valetudoIdentifier: config10.valetudoIdentifier,
148021
148022
  coverSwapOpenClose: config10.coverSwapOpenClose,
148022
148023
  disableClimateOnOff: config10.disableClimateOnOff,
148024
+ disableClimateFanControl: config10.disableClimateFanControl,
148023
148025
  customServiceAreas: config10.customServiceAreas,
148024
148026
  customFanSpeedTags: config10.customFanSpeedTags,
148025
148027
  composedEntities: config10.composedEntities
@@ -148840,6 +148842,7 @@ function entityMappingApi(mappingStorage) {
148840
148842
  valetudoIdentifier: body.valetudoIdentifier,
148841
148843
  coverSwapOpenClose: body.coverSwapOpenClose,
148842
148844
  disableClimateOnOff: body.disableClimateOnOff,
148845
+ disableClimateFanControl: body.disableClimateFanControl,
148843
148846
  composedEntities: body.composedEntities
148844
148847
  };
148845
148848
  const config10 = await mappingStorage.setMapping(request);
@@ -149303,7 +149306,8 @@ function configToProfileEntry(config10) {
149303
149306
  customFanSpeedTags: config10.customFanSpeedTags,
149304
149307
  valetudoIdentifier: config10.valetudoIdentifier,
149305
149308
  coverSwapOpenClose: config10.coverSwapOpenClose,
149306
- disableClimateOnOff: config10.disableClimateOnOff
149309
+ disableClimateOnOff: config10.disableClimateOnOff,
149310
+ disableClimateFanControl: config10.disableClimateFanControl
149307
149311
  };
149308
149312
  }
149309
149313
  function mappingProfileApi(mappingStorage) {
@@ -149425,7 +149429,8 @@ function mappingProfileApi(mappingStorage) {
149425
149429
  customFanSpeedTags: entry.customFanSpeedTags,
149426
149430
  valetudoIdentifier: entry.valetudoIdentifier,
149427
149431
  coverSwapOpenClose: entry.coverSwapOpenClose,
149428
- disableClimateOnOff: entry.disableClimateOnOff
149432
+ disableClimateOnOff: entry.disableClimateOnOff,
149433
+ disableClimateFanControl: entry.disableClimateFanControl
149429
149434
  });
149430
149435
  applied++;
149431
149436
  } catch (e) {
@@ -153295,9 +153300,10 @@ var EntityMappingStorage = class extends Service {
153295
153300
  valetudoIdentifier: request.valetudoIdentifier?.trim() || void 0,
153296
153301
  coverSwapOpenClose: request.coverSwapOpenClose || void 0,
153297
153302
  disableClimateOnOff: request.disableClimateOnOff || void 0,
153303
+ disableClimateFanControl: request.disableClimateFanControl || void 0,
153298
153304
  composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0
153299
153305
  };
153300
- if (!config10.matterDeviceType && !config10.customName && !config10.customProductName && !config10.customVendorName && !config10.customSerialNumber && config10.disabled !== true && !config10.filterLifeEntity && !config10.cleaningModeEntity && !config10.temperatureEntity && !config10.humidityEntity && !config10.batteryEntity && !config10.roomEntities && !config10.disableLockPin && !config10.powerEntity && !config10.energyEntity && !config10.pressureEntity && !config10.suctionLevelEntity && !config10.mopIntensityEntity && (!config10.customServiceAreas || config10.customServiceAreas.length === 0) && (!config10.customFanSpeedTags || Object.keys(config10.customFanSpeedTags).length === 0) && !config10.currentRoomEntity && !config10.valetudoIdentifier && !config10.coverSwapOpenClose && !config10.disableClimateOnOff && (!config10.composedEntities || config10.composedEntities.length === 0)) {
153306
+ if (!config10.matterDeviceType && !config10.customName && !config10.customProductName && !config10.customVendorName && !config10.customSerialNumber && config10.disabled !== true && !config10.filterLifeEntity && !config10.cleaningModeEntity && !config10.temperatureEntity && !config10.humidityEntity && !config10.batteryEntity && !config10.roomEntities && !config10.disableLockPin && !config10.powerEntity && !config10.energyEntity && !config10.pressureEntity && !config10.suctionLevelEntity && !config10.mopIntensityEntity && (!config10.customServiceAreas || config10.customServiceAreas.length === 0) && (!config10.customFanSpeedTags || Object.keys(config10.customFanSpeedTags).length === 0) && !config10.currentRoomEntity && !config10.valetudoIdentifier && !config10.coverSwapOpenClose && !config10.disableClimateOnOff && !config10.disableClimateFanControl && (!config10.composedEntities || config10.composedEntities.length === 0)) {
153301
153307
  bridgeMap.delete(request.entityId);
153302
153308
  } else {
153303
153309
  bridgeMap.set(request.entityId, config10);
@@ -171917,10 +171923,7 @@ function ClimateDevice(homeAssistantEntity) {
171917
171923
  }
171918
171924
  const supportsHumidity = attributes7.current_humidity != null || testBit(supportedFeatures, ClimateDeviceFeature.TARGET_HUMIDITY);
171919
171925
  const supportsOnOff = testBit(supportedFeatures, ClimateDeviceFeature.TURN_ON) && testBit(supportedFeatures, ClimateDeviceFeature.TURN_OFF) && homeAssistantEntity.mapping?.disableClimateOnOff !== true;
171920
- const supportsFanMode = testBit(
171921
- supportedFeatures,
171922
- ClimateDeviceFeature.FAN_MODE
171923
- );
171926
+ const supportsFanMode = testBit(supportedFeatures, ClimateDeviceFeature.FAN_MODE) && homeAssistantEntity.mapping?.disableClimateFanControl !== true;
171924
171927
  const initialState = {
171925
171928
  // Pass actual current_temperature for initial state.
171926
171929
  // If unavailable (null/undefined), update() will fall back to the
@@ -177608,9 +177611,8 @@ function buildSupportedModes2(attributes7, includeUnnamedRooms = false, customAr
177608
177611
  }
177609
177612
  return modes;
177610
177613
  }
177611
- function handleCustomServiceAreas(selectedAreas, customAreas, homeAssistant, serviceArea) {
177614
+ function handleCustomServiceAreas(selectedAreas, customAreas, homeAssistant) {
177612
177615
  const matched = selectedAreas.map((areaId) => customAreas[areaId - 1]).filter(Boolean);
177613
- serviceArea.state.selectedAreas = [];
177614
177616
  if (matched.length === 0) {
177615
177617
  logger194.warn(
177616
177618
  `Custom service areas: no match for selected IDs ${selectedAreas.join(", ")}`
@@ -177680,14 +177682,12 @@ var vacuumRvcRunModeConfig = {
177680
177682
  return handleCustomServiceAreas(
177681
177683
  selectedAreas,
177682
177684
  customAreas,
177683
- homeAssistant,
177684
- serviceArea
177685
+ homeAssistant
177685
177686
  );
177686
177687
  }
177687
177688
  const cleanAreaRooms = homeAssistant.state.mapping?.cleanAreaRooms;
177688
177689
  if (cleanAreaRooms && cleanAreaRooms.length > 0) {
177689
177690
  const haAreaIds = resolveCleanAreaIds(selectedAreas, cleanAreaRooms);
177690
- serviceArea.state.selectedAreas = [];
177691
177691
  if (haAreaIds.length > 0) {
177692
177692
  logger194.info(
177693
177693
  `CLEAN_AREA: cleaning HA areas: ${haAreaIds.join(", ")}`
@@ -177713,7 +177713,6 @@ var vacuumRvcRunModeConfig = {
177713
177713
  logger194.info(
177714
177714
  `Roborock: Pressing button entities for selected rooms: ${buttonEntityIds.join(", ")}`
177715
177715
  );
177716
- serviceArea.state.selectedAreas = [];
177717
177716
  for (let i = 1; i < buttonEntityIds.length; i++) {
177718
177717
  homeAssistant.callAction({
177719
177718
  action: "button.press",
@@ -177728,7 +177727,6 @@ var vacuumRvcRunModeConfig = {
177728
177727
  }
177729
177728
  const vacuumEntityId = homeAssistant.entityId;
177730
177729
  if (vacuumEntityId.startsWith("vacuum.valetudo_")) {
177731
- serviceArea.state.selectedAreas = [];
177732
177730
  return buildValetudoSegmentAction(
177733
177731
  vacuumEntityId,
177734
177732
  selectedAreas,
@@ -177751,7 +177749,6 @@ var vacuumRvcRunModeConfig = {
177751
177749
  logger194.info(
177752
177750
  `Starting cleaning with selected areas: ${roomIds.join(", ")}`
177753
177751
  );
177754
- serviceArea.state.selectedAreas = [];
177755
177752
  if (isDreameVacuum(attributes7)) {
177756
177753
  if (targetMapName) {
177757
177754
  const vacName = vacuumEntityId.replace("vacuum.", "");