@camstack/addon-provider-gree 0.2.72 → 0.2.73

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
@@ -33471,6 +33471,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33471
33471
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33472
33472
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33473
33473
  }
33474
+ /**
33475
+ * A top-level persisted row with NO configuration is a leftover, not a
33476
+ * device — quarantine it instead of dialling it.
33477
+ *
33478
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33479
+ * already in flight (D376), and then dialled at every single boot: eight
33480
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33481
+ * each one the same Zod refusal on `host` and `password`. The bound was
33482
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33483
+ * about the row ever stopped costing attempts and log lines.
33484
+ *
33485
+ * No retry can add a configuration that is not there. The row is reported
33486
+ * once, with its `deviceId`, and set aside.
33487
+ *
33488
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33489
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33490
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33491
+ * genuinely create configuration-free top-level devices turn this off.
33492
+ */
33493
+ quarantineConfiglessTopLevelRestores = true;
33494
+ isUnrestorableShell(saved) {
33495
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33496
+ if (saved.parentDeviceId !== null) return false;
33497
+ const config = saved.config;
33498
+ if (config === void 0) return false;
33499
+ return Object.keys(config).length === 0;
33500
+ }
33501
+ quarantineShell(saved) {
33502
+ this._permanentRestoreFailures.set(saved.id, {
33503
+ deviceId: saved.id,
33504
+ stableId: saved.stableId,
33505
+ type: saved.type,
33506
+ attempts: 0,
33507
+ lastError: "persisted row has no configuration",
33508
+ failedAt: Date.now()
33509
+ });
33510
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33511
+ tags: {
33512
+ deviceId: saved.id,
33513
+ stableId: saved.stableId
33514
+ },
33515
+ meta: {
33516
+ type: saved.type,
33517
+ provider: this.providerName
33518
+ }
33519
+ });
33520
+ }
33474
33521
  cancelRestoreRetries() {
33475
33522
  this._restoreRetryScheduler?.cancel();
33476
33523
  this._restoreRetryScheduler = null;
@@ -33655,6 +33702,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33655
33702
  });
33656
33703
  return;
33657
33704
  }
33705
+ if (this.isUnrestorableShell(saved)) {
33706
+ this.quarantineShell(saved);
33707
+ return;
33708
+ }
33658
33709
  try {
33659
33710
  await attemptRestore(saved);
33660
33711
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -33470,6 +33470,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33470
33470
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33471
33471
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33472
33472
  }
33473
+ /**
33474
+ * A top-level persisted row with NO configuration is a leftover, not a
33475
+ * device — quarantine it instead of dialling it.
33476
+ *
33477
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33478
+ * already in flight (D376), and then dialled at every single boot: eight
33479
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33480
+ * each one the same Zod refusal on `host` and `password`. The bound was
33481
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33482
+ * about the row ever stopped costing attempts and log lines.
33483
+ *
33484
+ * No retry can add a configuration that is not there. The row is reported
33485
+ * once, with its `deviceId`, and set aside.
33486
+ *
33487
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33488
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33489
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33490
+ * genuinely create configuration-free top-level devices turn this off.
33491
+ */
33492
+ quarantineConfiglessTopLevelRestores = true;
33493
+ isUnrestorableShell(saved) {
33494
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33495
+ if (saved.parentDeviceId !== null) return false;
33496
+ const config = saved.config;
33497
+ if (config === void 0) return false;
33498
+ return Object.keys(config).length === 0;
33499
+ }
33500
+ quarantineShell(saved) {
33501
+ this._permanentRestoreFailures.set(saved.id, {
33502
+ deviceId: saved.id,
33503
+ stableId: saved.stableId,
33504
+ type: saved.type,
33505
+ attempts: 0,
33506
+ lastError: "persisted row has no configuration",
33507
+ failedAt: Date.now()
33508
+ });
33509
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33510
+ tags: {
33511
+ deviceId: saved.id,
33512
+ stableId: saved.stableId
33513
+ },
33514
+ meta: {
33515
+ type: saved.type,
33516
+ provider: this.providerName
33517
+ }
33518
+ });
33519
+ }
33473
33520
  cancelRestoreRetries() {
33474
33521
  this._restoreRetryScheduler?.cancel();
33475
33522
  this._restoreRetryScheduler = null;
@@ -33654,6 +33701,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33654
33701
  });
33655
33702
  return;
33656
33703
  }
33704
+ if (this.isUnrestorableShell(saved)) {
33705
+ this.quarantineShell(saved);
33706
+ return;
33707
+ }
33657
33708
  try {
33658
33709
  await attemptRestore(saved);
33659
33710
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-gree",
3
- "version": "0.2.72",
3
+ "version": "0.2.73",
4
4
  "description": "Gree air-conditioner device-provider addon for CamStack — wraps the @apocaliss92/nodegree local-UDP client (LAN discovery + AES control), exposing climate-control and fan-control",
5
5
  "keywords": [
6
6
  "camstack",