@camstack/addon-provider-onvif 1.2.59 → 1.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
@@ -12546,7 +12546,26 @@ var DiscoveryCandidateSchema = object({
12546
12546
  * identity ahead of adoption. Rendering metadata (unit, precision)
12547
12547
  * flows live through the cap STATUS SLICE after adoption.
12548
12548
  */
12549
- sourceInfo: SourceInfoSchema.optional()
12549
+ sourceInfo: SourceInfoSchema.optional(),
12550
+ /**
12551
+ * Set when this candidate is a device the provider ALREADY owns.
12552
+ *
12553
+ * A scan cannot generally produce the identity a device was onboarded under
12554
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12555
+ * comparison never matches and an owned device looks addable. Re-adopting one
12556
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12557
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12558
+ * three child cameras offline for four hours.
12559
+ *
12560
+ * A provider that can recognise its own devices says so here. Absent means
12561
+ * "not recognised", which is not the same as "known to be new" — a provider
12562
+ * that cannot tell simply never sets it.
12563
+ */
12564
+ alreadyOnboarded: boolean().optional(),
12565
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12566
+ onboardedDeviceId: number().optional(),
12567
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12568
+ onboardedName: string().optional()
12550
12569
  });
12551
12570
  /**
12552
12571
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -29782,6 +29801,22 @@ var BaseDeviceProvider = class extends BaseAddon {
29782
29801
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
29783
29802
  * `getStatus().error`.
29784
29803
  */
29804
+ /**
29805
+ * Repair a row's PERSISTED config blob immediately before it is restored.
29806
+ * Default: no-op — most providers have nothing to heal.
29807
+ *
29808
+ * This exists because a restored device self-hydrates from the DB: `create()`
29809
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
29810
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
29811
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
29812
+ * been emptied failed all four bounded attempts against fields
29813
+ * (`host`, `password`) it inherits from its parent and never dials itself.
29814
+ *
29815
+ * Implementations get every saved row, so a child can read its parent's blob.
29816
+ * A heal that throws is treated like any other restore failure: retried under
29817
+ * the bound, then reported — never swallowed.
29818
+ */
29819
+ async healSavedConfig(_saved, _allSaved) {}
29785
29820
  async onRestoreDevices(savedDevices) {
29786
29821
  const restored = /* @__PURE__ */ new Set();
29787
29822
  const failures = [];
@@ -29790,6 +29825,7 @@ var BaseDeviceProvider = class extends BaseAddon {
29790
29825
  const Class = this.deviceClasses[saved.type];
29791
29826
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
29792
29827
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
29828
+ await this.healSavedConfig(saved, savedDevices);
29793
29829
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
29794
29830
  restored.add(saved.id);
29795
29831
  };
package/dist/addon.mjs CHANGED
@@ -12547,7 +12547,26 @@ var DiscoveryCandidateSchema = object({
12547
12547
  * identity ahead of adoption. Rendering metadata (unit, precision)
12548
12548
  * flows live through the cap STATUS SLICE after adoption.
12549
12549
  */
12550
- sourceInfo: SourceInfoSchema.optional()
12550
+ sourceInfo: SourceInfoSchema.optional(),
12551
+ /**
12552
+ * Set when this candidate is a device the provider ALREADY owns.
12553
+ *
12554
+ * A scan cannot generally produce the identity a device was onboarded under
12555
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12556
+ * comparison never matches and an owned device looks addable. Re-adopting one
12557
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12558
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12559
+ * three child cameras offline for four hours.
12560
+ *
12561
+ * A provider that can recognise its own devices says so here. Absent means
12562
+ * "not recognised", which is not the same as "known to be new" — a provider
12563
+ * that cannot tell simply never sets it.
12564
+ */
12565
+ alreadyOnboarded: boolean().optional(),
12566
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12567
+ onboardedDeviceId: number().optional(),
12568
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12569
+ onboardedName: string().optional()
12551
12570
  });
12552
12571
  /**
12553
12572
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -29783,6 +29802,22 @@ var BaseDeviceProvider = class extends BaseAddon {
29783
29802
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
29784
29803
  * `getStatus().error`.
29785
29804
  */
29805
+ /**
29806
+ * Repair a row's PERSISTED config blob immediately before it is restored.
29807
+ * Default: no-op — most providers have nothing to heal.
29808
+ *
29809
+ * This exists because a restored device self-hydrates from the DB: `create()`
29810
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
29811
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
29812
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
29813
+ * been emptied failed all four bounded attempts against fields
29814
+ * (`host`, `password`) it inherits from its parent and never dials itself.
29815
+ *
29816
+ * Implementations get every saved row, so a child can read its parent's blob.
29817
+ * A heal that throws is treated like any other restore failure: retried under
29818
+ * the bound, then reported — never swallowed.
29819
+ */
29820
+ async healSavedConfig(_saved, _allSaved) {}
29786
29821
  async onRestoreDevices(savedDevices) {
29787
29822
  const restored = /* @__PURE__ */ new Set();
29788
29823
  const failures = [];
@@ -29791,6 +29826,7 @@ var BaseDeviceProvider = class extends BaseAddon {
29791
29826
  const Class = this.deviceClasses[saved.type];
29792
29827
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
29793
29828
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
29829
+ await this.healSavedConfig(saved, savedDevices);
29794
29830
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
29795
29831
  restored.add(saved.id);
29796
29832
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-onvif",
3
- "version": "1.2.59",
3
+ "version": "1.2.61",
4
4
  "description": "ONVIF camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",