@riddix/hamh 2.1.0-alpha.628 → 2.1.0-alpha.630

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
 
@@ -147873,6 +147873,7 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
147873
147873
  mopIntensityEntity: config10.mopIntensityEntity,
147874
147874
  valetudoIdentifier: config10.valetudoIdentifier,
147875
147875
  coverSwapOpenClose: config10.coverSwapOpenClose,
147876
+ disableClimateOnOff: config10.disableClimateOnOff,
147876
147877
  customServiceAreas: config10.customServiceAreas,
147877
147878
  customFanSpeedTags: config10.customFanSpeedTags,
147878
147879
  composedEntities: config10.composedEntities
@@ -148018,6 +148019,7 @@ WARNING: ${includeIdentity ? "This backup contains sensitive Matter identity dat
148018
148019
  temperatureEntity: config10.temperatureEntity,
148019
148020
  valetudoIdentifier: config10.valetudoIdentifier,
148020
148021
  coverSwapOpenClose: config10.coverSwapOpenClose,
148022
+ disableClimateOnOff: config10.disableClimateOnOff,
148021
148023
  customServiceAreas: config10.customServiceAreas,
148022
148024
  customFanSpeedTags: config10.customFanSpeedTags,
148023
148025
  composedEntities: config10.composedEntities
@@ -148837,6 +148839,7 @@ function entityMappingApi(mappingStorage) {
148837
148839
  currentRoomEntity: body.currentRoomEntity,
148838
148840
  valetudoIdentifier: body.valetudoIdentifier,
148839
148841
  coverSwapOpenClose: body.coverSwapOpenClose,
148842
+ disableClimateOnOff: body.disableClimateOnOff,
148840
148843
  composedEntities: body.composedEntities
148841
148844
  };
148842
148845
  const config10 = await mappingStorage.setMapping(request);
@@ -149299,7 +149302,8 @@ function configToProfileEntry(config10) {
149299
149302
  customServiceAreas: config10.customServiceAreas,
149300
149303
  customFanSpeedTags: config10.customFanSpeedTags,
149301
149304
  valetudoIdentifier: config10.valetudoIdentifier,
149302
- coverSwapOpenClose: config10.coverSwapOpenClose
149305
+ coverSwapOpenClose: config10.coverSwapOpenClose,
149306
+ disableClimateOnOff: config10.disableClimateOnOff
149303
149307
  };
149304
149308
  }
149305
149309
  function mappingProfileApi(mappingStorage) {
@@ -149420,7 +149424,8 @@ function mappingProfileApi(mappingStorage) {
149420
149424
  customServiceAreas: entry.customServiceAreas,
149421
149425
  customFanSpeedTags: entry.customFanSpeedTags,
149422
149426
  valetudoIdentifier: entry.valetudoIdentifier,
149423
- coverSwapOpenClose: entry.coverSwapOpenClose
149427
+ coverSwapOpenClose: entry.coverSwapOpenClose,
149428
+ disableClimateOnOff: entry.disableClimateOnOff
149424
149429
  });
149425
149430
  applied++;
149426
149431
  } catch (e) {
@@ -153289,9 +153294,10 @@ var EntityMappingStorage = class extends Service {
153289
153294
  currentRoomEntity: request.currentRoomEntity?.trim() || void 0,
153290
153295
  valetudoIdentifier: request.valetudoIdentifier?.trim() || void 0,
153291
153296
  coverSwapOpenClose: request.coverSwapOpenClose || void 0,
153297
+ disableClimateOnOff: request.disableClimateOnOff || void 0,
153292
153298
  composedEntities: request.composedEntities?.filter((e) => e.entityId?.trim()) ?? void 0
153293
153299
  };
153294
- 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.composedEntities || config10.composedEntities.length === 0)) {
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)) {
153295
153301
  bridgeMap.delete(request.entityId);
153296
153302
  } else {
153297
153303
  bridgeMap.set(request.entityId, config10);
@@ -171910,7 +171916,7 @@ function ClimateDevice(homeAssistantEntity) {
171910
171916
  );
171911
171917
  }
171912
171918
  const supportsHumidity = attributes7.current_humidity != null || testBit(supportedFeatures, ClimateDeviceFeature.TARGET_HUMIDITY);
171913
- const supportsOnOff = testBit(supportedFeatures, ClimateDeviceFeature.TURN_ON) && testBit(supportedFeatures, ClimateDeviceFeature.TURN_OFF);
171919
+ const supportsOnOff = testBit(supportedFeatures, ClimateDeviceFeature.TURN_ON) && testBit(supportedFeatures, ClimateDeviceFeature.TURN_OFF) && homeAssistantEntity.mapping?.disableClimateOnOff !== true;
171914
171920
  const supportsFanMode = testBit(
171915
171921
  supportedFeatures,
171916
171922
  ClimateDeviceFeature.FAN_MODE
@@ -177602,9 +177608,8 @@ function buildSupportedModes2(attributes7, includeUnnamedRooms = false, customAr
177602
177608
  }
177603
177609
  return modes;
177604
177610
  }
177605
- function handleCustomServiceAreas(selectedAreas, customAreas, homeAssistant, serviceArea) {
177611
+ function handleCustomServiceAreas(selectedAreas, customAreas, homeAssistant) {
177606
177612
  const matched = selectedAreas.map((areaId) => customAreas[areaId - 1]).filter(Boolean);
177607
- serviceArea.state.selectedAreas = [];
177608
177613
  if (matched.length === 0) {
177609
177614
  logger194.warn(
177610
177615
  `Custom service areas: no match for selected IDs ${selectedAreas.join(", ")}`
@@ -177674,14 +177679,12 @@ var vacuumRvcRunModeConfig = {
177674
177679
  return handleCustomServiceAreas(
177675
177680
  selectedAreas,
177676
177681
  customAreas,
177677
- homeAssistant,
177678
- serviceArea
177682
+ homeAssistant
177679
177683
  );
177680
177684
  }
177681
177685
  const cleanAreaRooms = homeAssistant.state.mapping?.cleanAreaRooms;
177682
177686
  if (cleanAreaRooms && cleanAreaRooms.length > 0) {
177683
177687
  const haAreaIds = resolveCleanAreaIds(selectedAreas, cleanAreaRooms);
177684
- serviceArea.state.selectedAreas = [];
177685
177688
  if (haAreaIds.length > 0) {
177686
177689
  logger194.info(
177687
177690
  `CLEAN_AREA: cleaning HA areas: ${haAreaIds.join(", ")}`
@@ -177707,7 +177710,6 @@ var vacuumRvcRunModeConfig = {
177707
177710
  logger194.info(
177708
177711
  `Roborock: Pressing button entities for selected rooms: ${buttonEntityIds.join(", ")}`
177709
177712
  );
177710
- serviceArea.state.selectedAreas = [];
177711
177713
  for (let i = 1; i < buttonEntityIds.length; i++) {
177712
177714
  homeAssistant.callAction({
177713
177715
  action: "button.press",
@@ -177722,7 +177724,6 @@ var vacuumRvcRunModeConfig = {
177722
177724
  }
177723
177725
  const vacuumEntityId = homeAssistant.entityId;
177724
177726
  if (vacuumEntityId.startsWith("vacuum.valetudo_")) {
177725
- serviceArea.state.selectedAreas = [];
177726
177727
  return buildValetudoSegmentAction(
177727
177728
  vacuumEntityId,
177728
177729
  selectedAreas,
@@ -177745,7 +177746,6 @@ var vacuumRvcRunModeConfig = {
177745
177746
  logger194.info(
177746
177747
  `Starting cleaning with selected areas: ${roomIds.join(", ")}`
177747
177748
  );
177748
- serviceArea.state.selectedAreas = [];
177749
177749
  if (isDreameVacuum(attributes7)) {
177750
177750
  if (targetMapName) {
177751
177751
  const vacName = vacuumEntityId.replace("vacuum.", "");