@camstack/addon-provider-rademacher 0.2.72 → 0.2.74

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
@@ -34453,6 +34453,53 @@ var BaseDeviceProvider = class extends BaseAddon {
34453
34453
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
34454
34454
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
34455
34455
  }
34456
+ /**
34457
+ * A top-level persisted row with NO configuration is a leftover, not a
34458
+ * device — quarantine it instead of dialling it.
34459
+ *
34460
+ * Device 614 was deleted, resurrected as a config-less shell by a write
34461
+ * already in flight (D376), and then dialled at every single boot: eight
34462
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
34463
+ * each one the same Zod refusal on `host` and `password`. The bound was
34464
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
34465
+ * about the row ever stopped costing attempts and log lines.
34466
+ *
34467
+ * No retry can add a configuration that is not there. The row is reported
34468
+ * once, with its `deviceId`, and set aside.
34469
+ *
34470
+ * Top-level only: an accessory child legitimately carries an empty blob (it
34471
+ * lives off its parent), and a hub-adopted child with an emptied blob is
34472
+ * repaired by `healSavedConfig` from its parent instead. Providers that
34473
+ * genuinely create configuration-free top-level devices turn this off.
34474
+ */
34475
+ quarantineConfiglessTopLevelRestores = true;
34476
+ isUnrestorableShell(saved) {
34477
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
34478
+ if (saved.parentDeviceId !== null) return false;
34479
+ const config = saved.config;
34480
+ if (config === void 0) return false;
34481
+ return Object.keys(config).length === 0;
34482
+ }
34483
+ quarantineShell(saved) {
34484
+ this._permanentRestoreFailures.set(saved.id, {
34485
+ deviceId: saved.id,
34486
+ stableId: saved.stableId,
34487
+ type: saved.type,
34488
+ attempts: 0,
34489
+ lastError: "persisted row has no configuration",
34490
+ failedAt: Date.now()
34491
+ });
34492
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
34493
+ tags: {
34494
+ deviceId: saved.id,
34495
+ stableId: saved.stableId
34496
+ },
34497
+ meta: {
34498
+ type: saved.type,
34499
+ provider: this.providerName
34500
+ }
34501
+ });
34502
+ }
34456
34503
  cancelRestoreRetries() {
34457
34504
  this._restoreRetryScheduler?.cancel();
34458
34505
  this._restoreRetryScheduler = null;
@@ -34637,6 +34684,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34637
34684
  });
34638
34685
  return;
34639
34686
  }
34687
+ if (this.isUnrestorableShell(saved)) {
34688
+ this.quarantineShell(saved);
34689
+ return;
34690
+ }
34640
34691
  try {
34641
34692
  await attemptRestore(saved);
34642
34693
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -34452,6 +34452,53 @@ var BaseDeviceProvider = class extends BaseAddon {
34452
34452
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
34453
34453
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
34454
34454
  }
34455
+ /**
34456
+ * A top-level persisted row with NO configuration is a leftover, not a
34457
+ * device — quarantine it instead of dialling it.
34458
+ *
34459
+ * Device 614 was deleted, resurrected as a config-less shell by a write
34460
+ * already in flight (D376), and then dialled at every single boot: eight
34461
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
34462
+ * each one the same Zod refusal on `host` and `password`. The bound was
34463
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
34464
+ * about the row ever stopped costing attempts and log lines.
34465
+ *
34466
+ * No retry can add a configuration that is not there. The row is reported
34467
+ * once, with its `deviceId`, and set aside.
34468
+ *
34469
+ * Top-level only: an accessory child legitimately carries an empty blob (it
34470
+ * lives off its parent), and a hub-adopted child with an emptied blob is
34471
+ * repaired by `healSavedConfig` from its parent instead. Providers that
34472
+ * genuinely create configuration-free top-level devices turn this off.
34473
+ */
34474
+ quarantineConfiglessTopLevelRestores = true;
34475
+ isUnrestorableShell(saved) {
34476
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
34477
+ if (saved.parentDeviceId !== null) return false;
34478
+ const config = saved.config;
34479
+ if (config === void 0) return false;
34480
+ return Object.keys(config).length === 0;
34481
+ }
34482
+ quarantineShell(saved) {
34483
+ this._permanentRestoreFailures.set(saved.id, {
34484
+ deviceId: saved.id,
34485
+ stableId: saved.stableId,
34486
+ type: saved.type,
34487
+ attempts: 0,
34488
+ lastError: "persisted row has no configuration",
34489
+ failedAt: Date.now()
34490
+ });
34491
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
34492
+ tags: {
34493
+ deviceId: saved.id,
34494
+ stableId: saved.stableId
34495
+ },
34496
+ meta: {
34497
+ type: saved.type,
34498
+ provider: this.providerName
34499
+ }
34500
+ });
34501
+ }
34455
34502
  cancelRestoreRetries() {
34456
34503
  this._restoreRetryScheduler?.cancel();
34457
34504
  this._restoreRetryScheduler = null;
@@ -34636,6 +34683,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34636
34683
  });
34637
34684
  return;
34638
34685
  }
34686
+ if (this.isUnrestorableShell(saved)) {
34687
+ this.quarantineShell(saved);
34688
+ return;
34689
+ }
34639
34690
  try {
34640
34691
  await attemptRestore(saved);
34641
34692
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.2.72",
3
+ "version": "0.2.74",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",