@camstack/addon-provider-dreame 0.2.60 → 0.2.61

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
@@ -63278,7 +63278,26 @@ var DiscoveryCandidateSchema = object({
63278
63278
  * identity ahead of adoption. Rendering metadata (unit, precision)
63279
63279
  * flows live through the cap STATUS SLICE after adoption.
63280
63280
  */
63281
- sourceInfo: SourceInfoSchema.optional()
63281
+ sourceInfo: SourceInfoSchema.optional(),
63282
+ /**
63283
+ * Set when this candidate is a device the provider ALREADY owns.
63284
+ *
63285
+ * A scan cannot generally produce the identity a device was onboarded under
63286
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
63287
+ * comparison never matches and an owned device looks addable. Re-adopting one
63288
+ * overwrites its config with scan-derived values — that is how a Home Hub's
63289
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
63290
+ * three child cameras offline for four hours.
63291
+ *
63292
+ * A provider that can recognise its own devices says so here. Absent means
63293
+ * "not recognised", which is not the same as "known to be new" — a provider
63294
+ * that cannot tell simply never sets it.
63295
+ */
63296
+ alreadyOnboarded: boolean().optional(),
63297
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
63298
+ onboardedDeviceId: number().optional(),
63299
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
63300
+ onboardedName: string().optional()
63282
63301
  });
63283
63302
  /**
63284
63303
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -83541,6 +83560,22 @@ var BaseDeviceProvider = class extends BaseAddon {
83541
83560
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
83542
83561
  * `getStatus().error`.
83543
83562
  */
83563
+ /**
83564
+ * Repair a row's PERSISTED config blob immediately before it is restored.
83565
+ * Default: no-op — most providers have nothing to heal.
83566
+ *
83567
+ * This exists because a restored device self-hydrates from the DB: `create()`
83568
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
83569
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
83570
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
83571
+ * been emptied failed all four bounded attempts against fields
83572
+ * (`host`, `password`) it inherits from its parent and never dials itself.
83573
+ *
83574
+ * Implementations get every saved row, so a child can read its parent's blob.
83575
+ * A heal that throws is treated like any other restore failure: retried under
83576
+ * the bound, then reported — never swallowed.
83577
+ */
83578
+ async healSavedConfig(_saved, _allSaved) {}
83544
83579
  async onRestoreDevices(savedDevices) {
83545
83580
  const restored = /* @__PURE__ */ new Set();
83546
83581
  const failures = [];
@@ -83549,6 +83584,7 @@ var BaseDeviceProvider = class extends BaseAddon {
83549
83584
  const Class = this.deviceClasses[saved.type];
83550
83585
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
83551
83586
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
83587
+ await this.healSavedConfig(saved, savedDevices);
83552
83588
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
83553
83589
  restored.add(saved.id);
83554
83590
  };
package/dist/addon.mjs CHANGED
@@ -63278,7 +63278,26 @@ var DiscoveryCandidateSchema = object({
63278
63278
  * identity ahead of adoption. Rendering metadata (unit, precision)
63279
63279
  * flows live through the cap STATUS SLICE after adoption.
63280
63280
  */
63281
- sourceInfo: SourceInfoSchema.optional()
63281
+ sourceInfo: SourceInfoSchema.optional(),
63282
+ /**
63283
+ * Set when this candidate is a device the provider ALREADY owns.
63284
+ *
63285
+ * A scan cannot generally produce the identity a device was onboarded under
63286
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
63287
+ * comparison never matches and an owned device looks addable. Re-adopting one
63288
+ * overwrites its config with scan-derived values — that is how a Home Hub's
63289
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
63290
+ * three child cameras offline for four hours.
63291
+ *
63292
+ * A provider that can recognise its own devices says so here. Absent means
63293
+ * "not recognised", which is not the same as "known to be new" — a provider
63294
+ * that cannot tell simply never sets it.
63295
+ */
63296
+ alreadyOnboarded: boolean().optional(),
63297
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
63298
+ onboardedDeviceId: number().optional(),
63299
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
63300
+ onboardedName: string().optional()
63282
63301
  });
63283
63302
  /**
63284
63303
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -83541,6 +83560,22 @@ var BaseDeviceProvider = class extends BaseAddon {
83541
83560
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
83542
83561
  * `getStatus().error`.
83543
83562
  */
83563
+ /**
83564
+ * Repair a row's PERSISTED config blob immediately before it is restored.
83565
+ * Default: no-op — most providers have nothing to heal.
83566
+ *
83567
+ * This exists because a restored device self-hydrates from the DB: `create()`
83568
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
83569
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
83570
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
83571
+ * been emptied failed all four bounded attempts against fields
83572
+ * (`host`, `password`) it inherits from its parent and never dials itself.
83573
+ *
83574
+ * Implementations get every saved row, so a child can read its parent's blob.
83575
+ * A heal that throws is treated like any other restore failure: retried under
83576
+ * the bound, then reported — never swallowed.
83577
+ */
83578
+ async healSavedConfig(_saved, _allSaved) {}
83544
83579
  async onRestoreDevices(savedDevices) {
83545
83580
  const restored = /* @__PURE__ */ new Set();
83546
83581
  const failures = [];
@@ -83549,6 +83584,7 @@ var BaseDeviceProvider = class extends BaseAddon {
83549
83584
  const Class = this.deviceClasses[saved.type];
83550
83585
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
83551
83586
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
83587
+ await this.healSavedConfig(saved, savedDevices);
83552
83588
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
83553
83589
  restored.add(saved.id);
83554
83590
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreame",
3
- "version": "0.2.60",
3
+ "version": "0.2.61",
4
4
  "description": "Dreame robot-vacuum / lawn-mower device-provider addon for CamStack — wraps the @apocaliss92/nodedreame Dreamehome cloud client",
5
5
  "keywords": [
6
6
  "camstack",