@camstack/addon-provider-homematic 1.2.74 → 1.2.75

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/dist/addon.js CHANGED
@@ -33543,6 +33543,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33543
33543
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33544
33544
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33545
33545
  }
33546
+ /**
33547
+ * A top-level persisted row with NO configuration is a leftover, not a
33548
+ * device — quarantine it instead of dialling it.
33549
+ *
33550
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33551
+ * already in flight (D376), and then dialled at every single boot: eight
33552
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33553
+ * each one the same Zod refusal on `host` and `password`. The bound was
33554
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33555
+ * about the row ever stopped costing attempts and log lines.
33556
+ *
33557
+ * No retry can add a configuration that is not there. The row is reported
33558
+ * once, with its `deviceId`, and set aside.
33559
+ *
33560
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33561
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33562
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33563
+ * genuinely create configuration-free top-level devices turn this off.
33564
+ */
33565
+ quarantineConfiglessTopLevelRestores = true;
33566
+ isUnrestorableShell(saved) {
33567
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33568
+ if (saved.parentDeviceId !== null) return false;
33569
+ const config = saved.config;
33570
+ if (config === void 0) return false;
33571
+ return Object.keys(config).length === 0;
33572
+ }
33573
+ quarantineShell(saved) {
33574
+ this._permanentRestoreFailures.set(saved.id, {
33575
+ deviceId: saved.id,
33576
+ stableId: saved.stableId,
33577
+ type: saved.type,
33578
+ attempts: 0,
33579
+ lastError: "persisted row has no configuration",
33580
+ failedAt: Date.now()
33581
+ });
33582
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33583
+ tags: {
33584
+ deviceId: saved.id,
33585
+ stableId: saved.stableId
33586
+ },
33587
+ meta: {
33588
+ type: saved.type,
33589
+ provider: this.providerName
33590
+ }
33591
+ });
33592
+ }
33546
33593
  cancelRestoreRetries() {
33547
33594
  this._restoreRetryScheduler?.cancel();
33548
33595
  this._restoreRetryScheduler = null;
@@ -33727,6 +33774,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33727
33774
  });
33728
33775
  return;
33729
33776
  }
33777
+ if (this.isUnrestorableShell(saved)) {
33778
+ this.quarantineShell(saved);
33779
+ return;
33780
+ }
33730
33781
  try {
33731
33782
  await attemptRestore(saved);
33732
33783
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -33544,6 +33544,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33544
33544
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33545
33545
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33546
33546
  }
33547
+ /**
33548
+ * A top-level persisted row with NO configuration is a leftover, not a
33549
+ * device — quarantine it instead of dialling it.
33550
+ *
33551
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33552
+ * already in flight (D376), and then dialled at every single boot: eight
33553
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33554
+ * each one the same Zod refusal on `host` and `password`. The bound was
33555
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33556
+ * about the row ever stopped costing attempts and log lines.
33557
+ *
33558
+ * No retry can add a configuration that is not there. The row is reported
33559
+ * once, with its `deviceId`, and set aside.
33560
+ *
33561
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33562
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33563
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33564
+ * genuinely create configuration-free top-level devices turn this off.
33565
+ */
33566
+ quarantineConfiglessTopLevelRestores = true;
33567
+ isUnrestorableShell(saved) {
33568
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33569
+ if (saved.parentDeviceId !== null) return false;
33570
+ const config = saved.config;
33571
+ if (config === void 0) return false;
33572
+ return Object.keys(config).length === 0;
33573
+ }
33574
+ quarantineShell(saved) {
33575
+ this._permanentRestoreFailures.set(saved.id, {
33576
+ deviceId: saved.id,
33577
+ stableId: saved.stableId,
33578
+ type: saved.type,
33579
+ attempts: 0,
33580
+ lastError: "persisted row has no configuration",
33581
+ failedAt: Date.now()
33582
+ });
33583
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33584
+ tags: {
33585
+ deviceId: saved.id,
33586
+ stableId: saved.stableId
33587
+ },
33588
+ meta: {
33589
+ type: saved.type,
33590
+ provider: this.providerName
33591
+ }
33592
+ });
33593
+ }
33547
33594
  cancelRestoreRetries() {
33548
33595
  this._restoreRetryScheduler?.cancel();
33549
33596
  this._restoreRetryScheduler = null;
@@ -33728,6 +33775,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33728
33775
  });
33729
33776
  return;
33730
33777
  }
33778
+ if (this.isUnrestorableShell(saved)) {
33779
+ this.quarantineShell(saved);
33780
+ return;
33781
+ }
33731
33782
  try {
33732
33783
  await attemptRestore(saved);
33733
33784
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homematic",
3
- "version": "1.2.74",
3
+ "version": "1.2.75",
4
4
  "description": "Homematic / HomematicIP (CCU3 / RaspberryMatic) device-provider addon for CamStack — wraps the nodehomematic library",
5
5
  "keywords": [
6
6
  "camstack",