@riddix/hamh 2.1.0-alpha.792 → 2.1.0-alpha.794

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.
@@ -125304,6 +125304,12 @@ var init_bridge_config_schema = __esm({
125304
125304
  description: "Append a suffix to every entity serial number on this bridge. Useful for forcing controllers like Aqara to treat devices as new and bypass cached device data. Leave empty for default behavior.",
125305
125305
  maxLength: 16
125306
125306
  },
125307
+ uniqueIdSuffix: {
125308
+ title: "Unique ID Suffix",
125309
+ type: "string",
125310
+ description: "Mixed into the unique ID of every bridged device on this bridge (standard bridge mode). Controllers like Alexa cache device records keyed on the unique ID, so setting or changing this can help mint fresh identities. Applies after a bridge restart, then re-discover in the controller. Leave empty for default behavior.",
125311
+ maxLength: 16
125312
+ },
125307
125313
  sessionMaxAgeHours: {
125308
125314
  title: "Session Rotation Max Age (hours)",
125309
125315
  type: "number",
@@ -147720,6 +147726,9 @@ var BridgeDataProvider = class extends Service {
147720
147726
  get serialNumberSuffix() {
147721
147727
  return this.data.serialNumberSuffix;
147722
147728
  }
147729
+ get uniqueIdSuffix() {
147730
+ return this.data.uniqueIdSuffix;
147731
+ }
147723
147732
  get sessionMaxAgeHours() {
147724
147733
  return this.data.sessionMaxAgeHours;
147725
147734
  }
@@ -147753,6 +147762,7 @@ var BridgeDataProvider = class extends Service {
147753
147762
  icon: this.icon,
147754
147763
  priority: this.priority,
147755
147764
  serialNumberSuffix: this.serialNumberSuffix,
147765
+ uniqueIdSuffix: this.uniqueIdSuffix,
147756
147766
  sessionMaxAgeHours: this.sessionMaxAgeHours,
147757
147767
  status: status3.code,
147758
147768
  statusReason: status3.reason,
@@ -149965,6 +149975,7 @@ init_esm7();
149965
149975
  import crypto6 from "node:crypto";
149966
149976
  init_home_assistant_entity_behavior();
149967
149977
  var logger191 = Logger.get("BasicInformationServer");
149978
+ var appliedUniqueIds = /* @__PURE__ */ new Map();
149968
149979
  var BasicInformationServer2 = class extends BridgedDeviceBasicInformationServer {
149969
149980
  async initialize() {
149970
149981
  await super.initialize();
@@ -150004,12 +150015,24 @@ var BasicInformationServer2 = class extends BridgedDeviceBasicInformationServer
150004
150015
  serialNumber,
150005
150016
  // UniqueId helps controllers (especially Alexa) identify devices across
150006
150017
  // multiple fabric connections. Using MD5 hash of entity_id for stability.
150007
- uniqueId: crypto6.createHash("md5").update(entity.entity_id).digest("hex").substring(0, 32)
150018
+ // uniqueIdSuffix mints a fresh identity so stale controller cloud
150019
+ // records keyed on it can be shed (#385).
150020
+ uniqueId: this.frozenUniqueId(entity.entity_id)
150008
150021
  });
150009
150022
  logger191.debug(
150010
150023
  `[${entity.entity_id}] basicInfo vendor=${this.state.vendorName} product=${this.state.productName} label=${this.state.productLabel} serial=${this.state.serialNumber} node=${this.state.nodeLabel}`
150011
150024
  );
150012
150025
  }
150026
+ frozenUniqueId(entityId) {
150027
+ const provider = this.env.get(BridgeDataProvider);
150028
+ const key = `${provider.id}:${entityId}`;
150029
+ let uniqueId = appliedUniqueIds.get(key);
150030
+ if (uniqueId == null) {
150031
+ uniqueId = crypto6.createHash("md5").update(entityId + (provider.uniqueIdSuffix ?? "")).digest("hex").substring(0, 32);
150032
+ appliedUniqueIds.set(key, uniqueId);
150033
+ }
150034
+ return uniqueId;
150035
+ }
150013
150036
  };
150014
150037
  function ellipse(maxLength, value) {
150015
150038
  return trimToLength(value, maxLength, "...");
@@ -150216,6 +150239,47 @@ var HaElectricalPowerMeasurementServer = ElectricalPowerMeasurementServerBase.se
150216
150239
  ]
150217
150240
  });
150218
150241
 
150242
+ // src/matter/behaviors/fan-speed-memory.ts
150243
+ init_esm7();
150244
+
150245
+ // ../../node_modules/.pnpm/@matter+main@0.17.3/node_modules/@matter/main/dist/esm/model.js
150246
+ init_nodejs();
150247
+ init_esm2();
150248
+
150249
+ // src/matter/behaviors/fan-speed-memory.ts
150250
+ var FanSpeedMemoryBehavior = class extends Behavior {
150251
+ static id = "fanSpeedMemory";
150252
+ static schema = new DatatypeModel(
150253
+ { name: "FanSpeedMemoryState", type: "struct" },
150254
+ FieldElement({
150255
+ name: "lastPercent",
150256
+ type: "uint8",
150257
+ quality: "N",
150258
+ default: 0
150259
+ }),
150260
+ FieldElement({
150261
+ name: "lastSpeed",
150262
+ type: "uint8",
150263
+ quality: "N",
150264
+ default: 0
150265
+ }),
150266
+ FieldElement({
150267
+ name: "lastAuto",
150268
+ type: "bool",
150269
+ quality: "N",
150270
+ default: false
150271
+ })
150272
+ );
150273
+ };
150274
+ ((FanSpeedMemoryBehavior2) => {
150275
+ class State {
150276
+ lastPercent = 0;
150277
+ lastSpeed = 0;
150278
+ lastAuto = false;
150279
+ }
150280
+ FanSpeedMemoryBehavior2.State = State;
150281
+ })(FanSpeedMemoryBehavior || (FanSpeedMemoryBehavior = {}));
150282
+
150219
150283
  // src/matter/endpoints/composed/composed-air-purifier-endpoint.ts
150220
150284
  init_home_assistant_entity_behavior();
150221
150285
 
@@ -150979,10 +151043,26 @@ var FeaturedBase5 = FanControlServer.with(
150979
151043
  windSupport: { naturalWind: true, sleepWind: true }
150980
151044
  });
150981
151045
  var FanControlServerBase = class extends FeaturedBase5 {
150982
- // Track last non-zero fan speed for restore on turn-on (#225)
151046
+ // Fallback last-speed tracking for endpoints without FanSpeedMemoryBehavior
151047
+ // (climate companion, humidifier). matter.js builds behavior instances per
151048
+ // transaction, so these fields only live within one interaction; fans use
151049
+ // the persisted memory behavior instead (#225/#387).
150983
151050
  lastNonZeroPercent = 0;
150984
151051
  lastNonZeroSpeed = 0;
150985
151052
  lastIsAutoMode = false;
151053
+ // Remembered speed, read at decision time. Behavior state survives
151054
+ // transactions and restarts, instance fields do not (#387).
151055
+ remembered() {
151056
+ if (this.agent.has(FanSpeedMemoryBehavior)) {
151057
+ const s = this.agent.get(FanSpeedMemoryBehavior).state;
151058
+ return { percent: s.lastPercent, speed: s.lastSpeed, auto: s.lastAuto };
151059
+ }
151060
+ return {
151061
+ percent: this.lastNonZeroPercent,
151062
+ speed: this.lastNonZeroSpeed,
151063
+ auto: this.lastIsAutoMode
151064
+ };
151065
+ }
150986
151066
  async initialize() {
150987
151067
  if (this.features.multiSpeed) {
150988
151068
  if (this.state.speedMax == null || this.state.speedMax < minSpeedMax) {
@@ -151000,6 +151080,13 @@ var FanControlServerBase = class extends FeaturedBase5 {
151000
151080
  this.lastNonZeroSpeed = ss;
151001
151081
  }
151002
151082
  }
151083
+ if (this.agent.has(FanSpeedMemoryBehavior)) {
151084
+ const memory = await this.agent.load(FanSpeedMemoryBehavior);
151085
+ if (memory.state.lastPercent === 0 && this.lastNonZeroPercent > 0) {
151086
+ memory.state.lastPercent = this.lastNonZeroPercent;
151087
+ memory.state.lastSpeed = this.lastNonZeroSpeed;
151088
+ }
151089
+ }
151003
151090
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
151004
151091
  this.update(homeAssistant.entity);
151005
151092
  this.reactTo(homeAssistant.onChange, this.update);
@@ -151095,6 +151182,7 @@ var FanControlServerBase = class extends FeaturedBase5 {
151095
151182
  this.lastNonZeroPercent = percentage;
151096
151183
  this.lastNonZeroSpeed = speed;
151097
151184
  this.lastIsAutoMode = config11.isInAutoMode(entity.state, this.agent);
151185
+ this.persistSpeed(this.lastIsAutoMode);
151098
151186
  }
151099
151187
  try {
151100
151188
  applyPatchState(this.state, {
@@ -151207,20 +151295,21 @@ var FanControlServerBase = class extends FeaturedBase5 {
151207
151295
  }
151208
151296
  const restoreOnPowerOn = homeAssistant.state.mapping?.fanRestoreSpeedOnPowerOn === true;
151209
151297
  const wasOff = homeAssistant.entity.state?.state === "off" || this.agent.has(OnOffBehavior) && !this.agent.get(OnOffBehavior).state.onOff;
151298
+ const remembered = this.remembered();
151210
151299
  if (wasOff) {
151211
151300
  logger198.debug(
151212
- `[${homeAssistant.entityId}] power-on write ${percentage}%: restore flag=${restoreOnPowerOn}, last speed=${this.lastNonZeroPercent}`
151301
+ `[${homeAssistant.entityId}] power-on write ${percentage}%: restore flag=${restoreOnPowerOn}, last speed=${remembered.percent}`
151213
151302
  );
151214
151303
  }
151215
- if (restoreOnPowerOn && wasOff && this.lastNonZeroPercent > 0 && percentage > 0) {
151216
- percentage = this.lastNonZeroPercent;
151304
+ if (restoreOnPowerOn && wasOff && remembered.percent > 0 && percentage > 0) {
151305
+ percentage = remembered.percent;
151217
151306
  try {
151218
151307
  applyPatchState(this.state, {
151219
- percentSetting: this.lastNonZeroPercent,
151220
- percentCurrent: this.lastNonZeroPercent,
151221
- ...this.features.multiSpeed && this.lastNonZeroSpeed > 0 ? {
151222
- speedSetting: this.lastNonZeroSpeed,
151223
- speedCurrent: this.lastNonZeroSpeed
151308
+ percentSetting: remembered.percent,
151309
+ percentCurrent: remembered.percent,
151310
+ ...this.features.multiSpeed && remembered.speed > 0 ? {
151311
+ speedSetting: remembered.speed,
151312
+ speedCurrent: remembered.speed
151224
151313
  } : {}
151225
151314
  });
151226
151315
  } catch {
@@ -151279,6 +151368,23 @@ var FanControlServerBase = class extends FeaturedBase5 {
151279
151368
  Math.ceil(speedMax * (percentage / 100))
151280
151369
  );
151281
151370
  }
151371
+ this.persistSpeed(false);
151372
+ }
151373
+ // Write the remembered speed through to the memory behavior. Instance
151374
+ // fields die with the transaction, this is what later interactions and
151375
+ // restarts read (#387).
151376
+ persistSpeed(auto) {
151377
+ if (!this.agent.has(FanSpeedMemoryBehavior)) {
151378
+ return;
151379
+ }
151380
+ try {
151381
+ applyPatchState(this.agent.get(FanSpeedMemoryBehavior).state, {
151382
+ lastPercent: this.lastNonZeroPercent,
151383
+ lastSpeed: this.lastNonZeroSpeed,
151384
+ ...auto !== void 0 ? { lastAuto: auto } : {}
151385
+ });
151386
+ } catch {
151387
+ }
151282
151388
  }
151283
151389
  targetAirflowDirectionChanged(airflowDirection, _oldValue, context) {
151284
151390
  if (transactionIsOffline(context)) {
@@ -151341,25 +151447,26 @@ var FanControlServerBase = class extends FeaturedBase5 {
151341
151447
  if (transactionIsOffline(context)) {
151342
151448
  return;
151343
151449
  }
151344
- if (onOff && this.lastNonZeroPercent > 0) {
151450
+ const remembered = this.remembered();
151451
+ if (onOff && remembered.percent > 0) {
151345
151452
  this.agent.asLocalActor(() => {
151346
151453
  try {
151347
151454
  applyPatchState(this.state, {
151348
- percentSetting: this.lastNonZeroPercent,
151349
- ...this.features.multiSpeed && this.lastNonZeroSpeed > 0 ? { speedSetting: this.lastNonZeroSpeed } : {}
151455
+ percentSetting: remembered.percent,
151456
+ ...this.features.multiSpeed && remembered.speed > 0 ? { speedSetting: remembered.speed } : {}
151350
151457
  });
151351
151458
  } catch {
151352
151459
  }
151353
151460
  });
151354
151461
  const homeAssistant = this.agent.get(HomeAssistantEntityBehavior);
151355
151462
  if (homeAssistant.isAvailable) {
151356
- if (this.lastIsAutoMode) {
151463
+ if (remembered.auto) {
151357
151464
  homeAssistant.callAction(
151358
151465
  this.state.config.setAutoMode(void 0, this.agent)
151359
151466
  );
151360
151467
  } else {
151361
151468
  homeAssistant.callAction(
151362
- this.state.config.turnOn(this.lastNonZeroPercent, this.agent)
151469
+ this.state.config.turnOn(remembered.percent, this.agent)
151363
151470
  );
151364
151471
  }
151365
151472
  }
@@ -151593,7 +151700,8 @@ var ComposedAirPurifierEndpoint = class _ComposedAirPurifierEndpoint extends End
151593
151700
  IdentifyServer2,
151594
151701
  HomeAssistantEntityBehavior,
151595
151702
  FanOnOffServer,
151596
- FanFanControlServer.with(...features3)
151703
+ FanFanControlServer.with(...features3),
151704
+ FanSpeedMemoryBehavior
151597
151705
  );
151598
151706
  const hasFilterLife = airPurifierAttributes.filter_life != null || airPurifierAttributes.filter_life_remaining != null || airPurifierAttributes.filter_life_level != null || !!config11.mapping?.filterLifeEntity;
151599
151707
  if (hasFilterLife) {
@@ -153630,7 +153738,8 @@ function AirPurifierEndpoint(homeAssistantEntity) {
153630
153738
  BasicInformationServer2,
153631
153739
  HomeAssistantEntityBehavior,
153632
153740
  FanOnOffServer,
153633
- FanFanControlServer.with(...features3)
153741
+ FanFanControlServer.with(...features3),
153742
+ FanSpeedMemoryBehavior
153634
153743
  );
153635
153744
  if (hasFilterLifeSupport(attributes9, mapping)) {
153636
153745
  const deviceWithFilter = baseDevice.with(
@@ -155378,6 +155487,7 @@ function FanDevice2(homeAssistantEntity) {
155378
155487
  GroupsServer,
155379
155488
  FanOnOffServer,
155380
155489
  FanFanControlServer.with(...features3),
155490
+ FanSpeedMemoryBehavior,
155381
155491
  DefaultPowerSourceServer
155382
155492
  ) : FanDevice.with(
155383
155493
  IdentifyServer2,
@@ -155385,7 +155495,8 @@ function FanDevice2(homeAssistantEntity) {
155385
155495
  HomeAssistantEntityBehavior,
155386
155496
  GroupsServer,
155387
155497
  FanOnOffServer,
155388
- FanFanControlServer.with(...features3)
155498
+ FanFanControlServer.with(...features3),
155499
+ FanSpeedMemoryBehavior
155389
155500
  );
155390
155501
  return device.set({ homeAssistantEntity });
155391
155502
  }
@@ -169052,6 +169163,7 @@ export {
169052
169163
  @matter/types/dist/esm/clusters/web-rtc-transport-definitions.js:
169053
169164
  @matter/types/dist/esm/clusters/index.js:
169054
169165
  @matter/main/dist/esm/clusters.js:
169166
+ @matter/main/dist/esm/model.js:
169055
169167
  @matter/main/dist/esm/forwards/behaviors/boolean-state.js:
169056
169168
  @matter/main/dist/esm/forwards/behaviors/smoke-co-alarm.js:
169057
169169
  @matter/main/dist/esm/forwards/behaviors/operational-state.js: