@riddix/hamh 2.1.0-alpha.848 → 2.1.0-alpha.850

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.
@@ -136608,7 +136608,20 @@ var PluginRegistry = class {
136608
136608
  }
136609
136609
  };
136610
136610
 
136611
+ // src/plugins/types.ts
136612
+ var SECRET_UNCHANGED = "__unchanged__";
136613
+
136611
136614
  // src/api/plugin-api.ts
136615
+ function redactSecrets(config11, schema6) {
136616
+ if (!schema6) return config11;
136617
+ const out = { ...config11 };
136618
+ for (const [key, prop] of Object.entries(schema6.properties)) {
136619
+ if (prop.secret && out[key] != null && out[key] !== "") {
136620
+ out[key] = SECRET_UNCHANGED;
136621
+ }
136622
+ }
136623
+ return out;
136624
+ }
136612
136625
  var MAX_UPLOAD_BYTES = 50 * 1024 * 1024;
136613
136626
  var BLOCKED_PREFIXES = [
136614
136627
  "/bin",
@@ -136647,7 +136660,10 @@ function pluginApi(bridgeService, storageLocation) {
136647
136660
  version: meta.version,
136648
136661
  source: meta.source,
136649
136662
  enabled: meta.enabled,
136650
- config: meta.config,
136663
+ config: redactSecrets(
136664
+ meta.config,
136665
+ bridge.getPluginConfigSchema?.(meta.name)
136666
+ ),
136651
136667
  circuitBreaker: info.circuitBreakers[meta.name],
136652
136668
  devices: info.devices.filter((d) => d.pluginName === meta.name).map((d) => ({
136653
136669
  id: d.device.id,
@@ -156746,6 +156762,9 @@ var PluginManager = class {
156746
156762
  } else if (this.runner.getState(name).failures === 0) {
156747
156763
  instance.started = true;
156748
156764
  }
156765
+ if (instance.plugin.getCurrentConfig) {
156766
+ instance.metadata.config = instance.plugin.getCurrentConfig();
156767
+ }
156749
156768
  }
156750
156769
  }
156751
156770
  /**
@@ -156846,6 +156865,17 @@ var PluginManager = class {
156846
156865
  async updateConfig(pluginName, config11) {
156847
156866
  const instance = this.instances.get(pluginName);
156848
156867
  if (!instance) return false;
156868
+ config11 = { ...config11 };
156869
+ const schema6 = instance.plugin.getConfigSchema?.();
156870
+ if (schema6) {
156871
+ for (const [key, prop] of Object.entries(schema6.properties)) {
156872
+ if (prop.secret && config11[key] === SECRET_UNCHANGED) {
156873
+ const stored = instance.metadata.config[key];
156874
+ if (stored == null) delete config11[key];
156875
+ else config11[key] = stored;
156876
+ }
156877
+ }
156878
+ }
156849
156879
  instance.metadata.config = config11;
156850
156880
  this.registry?.updateConfig(pluginName, config11);
156851
156881
  if (instance.plugin.onConfigChanged) {
@@ -161085,7 +161115,7 @@ function thermostatPreInitialize(self) {
161085
161115
  );
161086
161116
  self.state.thermostatRunningState = runningStateAllOff;
161087
161117
  if (self.features.autoMode) {
161088
- self.state.minSetpointDeadBand = self.state.minSetpointDeadBand ?? 0;
161118
+ self.state.minSetpointDeadBand = 0;
161089
161119
  } else {
161090
161120
  if (self.state.minSetpointDeadBand !== void 0)
161091
161121
  self.state.minSetpointDeadBand = void 0;
@@ -161782,24 +161812,25 @@ function applyClimateFreezeForKeepModeOnIdle(computed, entity, entityId, keepMod
161782
161812
  }
161783
161813
  return computed;
161784
161814
  }
161815
+ function isTemperatureRangeActive(entity) {
161816
+ const hasFeature = testBit(
161817
+ entity.attributes.supported_features ?? 0,
161818
+ ClimateDeviceFeature.TARGET_TEMPERATURE_RANGE
161819
+ );
161820
+ const currentMode = entity.state;
161821
+ const isRangeMode = currentMode === ClimateHvacMode.heat_cool || currentMode === ClimateHvacMode.auto;
161822
+ return hasFeature && isRangeMode;
161823
+ }
161785
161824
  var config5 = {
161786
- // Temperature range (target_temp_low/high) only works in heat_cool mode.
161787
- // In heat or cool mode, HA expects a single "temperature" value.
161788
- // We must check BOTH the feature flag AND the current HVAC mode.
161789
- supportsTemperatureRange: (entity) => {
161790
- const hasFeature = testBit(
161791
- entity.attributes.supported_features ?? 0,
161792
- ClimateDeviceFeature.TARGET_TEMPERATURE_RANGE
161793
- );
161794
- const currentMode = entity.state;
161795
- const isRangeMode = currentMode === ClimateHvacMode.heat_cool || currentMode === ClimateHvacMode.auto;
161796
- return hasFeature && isRangeMode;
161797
- },
161825
+ supportsTemperatureRange: isTemperatureRangeActive,
161798
161826
  getMinTemperature: (entity, agent) => getTemp(agent, entity, "min_temp"),
161799
161827
  getMaxTemperature: (entity, agent) => getTemp(agent, entity, "max_temp"),
161800
161828
  getCurrentTemperature: (entity, agent) => getTemp(agent, entity, "current_temperature"),
161801
- getTargetHeatingTemperature: (entity, agent) => getTemp(agent, entity, "target_temp_low") ?? getTemp(agent, entity, "target_temperature") ?? getTemp(agent, entity, "temperature"),
161802
- getTargetCoolingTemperature: (entity, agent) => getTemp(agent, entity, "target_temp_high") ?? getTemp(agent, entity, "target_temperature") ?? getTemp(agent, entity, "temperature"),
161829
+ // Outside range mode the range attributes are stale: an integration can park
161830
+ // target_temp_low/high while the mode it actually runs on is "temperature".
161831
+ // Reading them then reports the parked value as the setpoint (#435).
161832
+ getTargetHeatingTemperature: (entity, agent) => isTemperatureRangeActive(entity) ? getTemp(agent, entity, "target_temp_low") ?? getTemp(agent, entity, "target_temperature") ?? getTemp(agent, entity, "temperature") : getTemp(agent, entity, "temperature") ?? getTemp(agent, entity, "target_temperature") ?? getTemp(agent, entity, "target_temp_low"),
161833
+ getTargetCoolingTemperature: (entity, agent) => isTemperatureRangeActive(entity) ? getTemp(agent, entity, "target_temp_high") ?? getTemp(agent, entity, "target_temperature") ?? getTemp(agent, entity, "temperature") : getTemp(agent, entity, "temperature") ?? getTemp(agent, entity, "target_temperature") ?? getTemp(agent, entity, "target_temp_high"),
161803
161834
  getSystemMode: (entity, agent) => {
161804
161835
  const homeAssistant = agent.get(HomeAssistantEntityBehavior);
161805
161836
  const computed = computeSystemMode(entity, agent);
@@ -161991,13 +162022,16 @@ function ClimateDevice(homeAssistantEntity, includeBasicInformation = true) {
161991
162022
  const rawMaxLimit = toMatterTemp(attributes9.max_temp) ?? 5e3;
161992
162023
  const minLimit = Math.min(rawMinLimit, rawMaxLimit);
161993
162024
  const maxLimit = Math.max(rawMinLimit, rawMaxLimit);
162025
+ const rangeActive = isTemperatureRangeActive(
162026
+ homeAssistantEntity.entity.state
162027
+ );
161994
162028
  const initialState = {
161995
162029
  // Pass actual current_temperature for initial state.
161996
162030
  // If unavailable (null/undefined), update() will fall back to the
161997
162031
  // target setpoint so controllers don't display 0°C.
161998
162032
  localTemperature: toMatterTemp(attributes9.current_temperature),
161999
- occupiedHeatingSetpoint: toMatterTemp(attributes9.target_temp_low) ?? toMatterTemp(attributes9.temperature) ?? 2e3,
162000
- occupiedCoolingSetpoint: toMatterTemp(attributes9.target_temp_high) ?? toMatterTemp(attributes9.temperature) ?? 2400,
162033
+ occupiedHeatingSetpoint: rangeActive ? toMatterTemp(attributes9.target_temp_low) ?? toMatterTemp(attributes9.temperature) ?? 2e3 : toMatterTemp(attributes9.temperature) ?? toMatterTemp(attributes9.target_temp_low) ?? 2e3,
162034
+ occupiedCoolingSetpoint: rangeActive ? toMatterTemp(attributes9.target_temp_high) ?? toMatterTemp(attributes9.temperature) ?? 2400 : toMatterTemp(attributes9.temperature) ?? toMatterTemp(attributes9.target_temp_high) ?? 2400,
162001
162035
  // Use HA's actual min/max limits, fall back to wide range (0-50°C) if not
162002
162036
  // provided. Ordered above so min <= max always holds.
162003
162037
  minHeatSetpointLimit: minLimit,
@@ -173985,6 +174019,9 @@ var CameraPlugin = class {
173985
174019
  async onShutdown() {
173986
174020
  await this.teardown();
173987
174021
  }
174022
+ getCurrentConfig() {
174023
+ return { ...this.config };
174024
+ }
173988
174025
  getConfigSchema() {
173989
174026
  return {
173990
174027
  title: "Camera",
@@ -174000,7 +174037,8 @@ var CameraPlugin = class {
174000
174037
  type: "string",
174001
174038
  title: "Long-lived access token",
174002
174039
  description: "Leave empty to use the bridge's Home Assistant connection.",
174003
- required: false
174040
+ required: false,
174041
+ secret: true
174004
174042
  },
174005
174043
  cameras: {
174006
174044
  type: "string",
@@ -174243,7 +174281,7 @@ var EntityIsolationServiceImpl = class {
174243
174281
  * Returns: { bridgeId: "ed5b4f8d...", entityName: "Küchenlicht" }
174244
174282
  */
174245
174283
  parseEndpointPath(errorMessage) {
174246
- const match = errorMessage.match(/([a-f0-9]{32})\.aggregator\.([^.]+)\./i);
174284
+ const match = errorMessage.match(/([a-f0-9]{32})\.aggregator\.([^.\s>]+)/i);
174247
174285
  if (match) {
174248
174286
  return {
174249
174287
  bridgeId: match[1],
@@ -174271,6 +174309,9 @@ var EntityIsolationServiceImpl = class {
174271
174309
  if (msg.includes("Endpoint storage inaccessible")) {
174272
174310
  return "Endpoint storage inaccessible";
174273
174311
  }
174312
+ if (msg.includes("Error initializing part")) {
174313
+ return "Endpoint construction failure";
174314
+ }
174274
174315
  if (msg.includes("aggregator.")) {
174275
174316
  return "Runtime error in endpoint";
174276
174317
  }
@@ -177900,7 +177941,7 @@ function formatPatchError(action, error) {
177900
177941
  return `${action}:${error instanceof Error ? error.message : String(error)}`;
177901
177942
  }
177902
177943
 
177903
- // src/commands/start/start-handler.ts
177944
+ // src/commands/start/start-error-matchers.ts
177904
177945
  function extractErrorMessage(error) {
177905
177946
  if (error instanceof Error) return error.message;
177906
177947
  if (typeof error === "object" && error !== null) {
@@ -177913,14 +177954,17 @@ function extractErrorMessage(error) {
177913
177954
  }
177914
177955
  return String(error);
177915
177956
  }
177957
+ var AGGREGATOR_PATH_RE = /[0-9a-f]{32}\.aggregator\b/i;
177916
177958
  function shouldSuppressError(error) {
177917
177959
  const msg = extractErrorMessage(error);
177918
- return msg.includes("Connection lost") || msg.includes("Endpoint storage inaccessible") || msg.includes("Invalid intervalMs") || msg.includes("generalDiagnostics") || msg.includes("Behaviors have errors") || msg.includes("TransactionDestroyedError") || msg.includes("DestroyedDependencyError") || msg.includes("UninitializedDependencyError") || msg.includes("mutex-closed") || msg.includes("not a node and is not owned") || msg.includes("aggregator.");
177960
+ return msg.includes("Connection lost") || msg.includes("Endpoint storage inaccessible") || msg.includes("Invalid intervalMs") || msg.includes("generalDiagnostics") || msg.includes("Behaviors have errors") || msg.includes("TransactionDestroyedError") || msg.includes("DestroyedDependencyError") || msg.includes("UninitializedDependencyError") || msg.includes("mutex-closed") || msg.includes("not a node and is not owned") || msg.includes("aggregator.") || AGGREGATOR_PATH_RE.test(msg);
177919
177961
  }
177920
177962
  function isIsolatableError(error) {
177921
177963
  const msg = error instanceof Error ? error.message : String(error);
177922
- return msg.includes("Invalid intervalMs") || msg.includes("Behaviors have errors") || msg.includes("TransactionDestroyedError") || msg.includes("DestroyedDependencyError") || msg.includes("UninitializedDependencyError") || msg.includes("Endpoint storage inaccessible") || msg.includes("aggregator.");
177964
+ return msg.includes("Invalid intervalMs") || msg.includes("Behaviors have errors") || msg.includes("TransactionDestroyedError") || msg.includes("DestroyedDependencyError") || msg.includes("UninitializedDependencyError") || msg.includes("Endpoint storage inaccessible") || msg.includes("aggregator.") || AGGREGATOR_PATH_RE.test(msg);
177923
177965
  }
177966
+
177967
+ // src/commands/start/start-handler.ts
177924
177968
  process.on("uncaughtException", (error) => {
177925
177969
  if (shouldSuppressError(error)) {
177926
177970
  return;