@camstack/addon-provider-hikvision 1.2.68 → 1.2.69

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
@@ -13044,7 +13044,26 @@ var DiscoveryCandidateSchema = object({
13044
13044
  * identity ahead of adoption. Rendering metadata (unit, precision)
13045
13045
  * flows live through the cap STATUS SLICE after adoption.
13046
13046
  */
13047
- sourceInfo: SourceInfoSchema.optional()
13047
+ sourceInfo: SourceInfoSchema.optional(),
13048
+ /**
13049
+ * Set when this candidate is a device the provider ALREADY owns.
13050
+ *
13051
+ * A scan cannot generally produce the identity a device was onboarded under
13052
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
13053
+ * comparison never matches and an owned device looks addable. Re-adopting one
13054
+ * overwrites its config with scan-derived values — that is how a Home Hub's
13055
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
13056
+ * three child cameras offline for four hours.
13057
+ *
13058
+ * A provider that can recognise its own devices says so here. Absent means
13059
+ * "not recognised", which is not the same as "known to be new" — a provider
13060
+ * that cannot tell simply never sets it.
13061
+ */
13062
+ alreadyOnboarded: boolean().optional(),
13063
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
13064
+ onboardedDeviceId: number().optional(),
13065
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
13066
+ onboardedName: string().optional()
13048
13067
  });
13049
13068
  /**
13050
13069
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33751,6 +33770,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33751
33770
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33752
33771
  * `getStatus().error`.
33753
33772
  */
33773
+ /**
33774
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33775
+ * Default: no-op — most providers have nothing to heal.
33776
+ *
33777
+ * This exists because a restored device self-hydrates from the DB: `create()`
33778
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33779
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33780
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33781
+ * been emptied failed all four bounded attempts against fields
33782
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33783
+ *
33784
+ * Implementations get every saved row, so a child can read its parent's blob.
33785
+ * A heal that throws is treated like any other restore failure: retried under
33786
+ * the bound, then reported — never swallowed.
33787
+ */
33788
+ async healSavedConfig(_saved, _allSaved) {}
33754
33789
  async onRestoreDevices(savedDevices) {
33755
33790
  const restored = /* @__PURE__ */ new Set();
33756
33791
  const failures = [];
@@ -33759,6 +33794,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33759
33794
  const Class = this.deviceClasses[saved.type];
33760
33795
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33761
33796
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33797
+ await this.healSavedConfig(saved, savedDevices);
33762
33798
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33763
33799
  restored.add(saved.id);
33764
33800
  };
package/dist/addon.mjs CHANGED
@@ -13045,7 +13045,26 @@ var DiscoveryCandidateSchema = object({
13045
13045
  * identity ahead of adoption. Rendering metadata (unit, precision)
13046
13046
  * flows live through the cap STATUS SLICE after adoption.
13047
13047
  */
13048
- sourceInfo: SourceInfoSchema.optional()
13048
+ sourceInfo: SourceInfoSchema.optional(),
13049
+ /**
13050
+ * Set when this candidate is a device the provider ALREADY owns.
13051
+ *
13052
+ * A scan cannot generally produce the identity a device was onboarded under
13053
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
13054
+ * comparison never matches and an owned device looks addable. Re-adopting one
13055
+ * overwrites its config with scan-derived values — that is how a Home Hub's
13056
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
13057
+ * three child cameras offline for four hours.
13058
+ *
13059
+ * A provider that can recognise its own devices says so here. Absent means
13060
+ * "not recognised", which is not the same as "known to be new" — a provider
13061
+ * that cannot tell simply never sets it.
13062
+ */
13063
+ alreadyOnboarded: boolean().optional(),
13064
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
13065
+ onboardedDeviceId: number().optional(),
13066
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
13067
+ onboardedName: string().optional()
13049
13068
  });
13050
13069
  /**
13051
13070
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33752,6 +33771,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33752
33771
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33753
33772
  * `getStatus().error`.
33754
33773
  */
33774
+ /**
33775
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33776
+ * Default: no-op — most providers have nothing to heal.
33777
+ *
33778
+ * This exists because a restored device self-hydrates from the DB: `create()`
33779
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33780
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33781
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33782
+ * been emptied failed all four bounded attempts against fields
33783
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33784
+ *
33785
+ * Implementations get every saved row, so a child can read its parent's blob.
33786
+ * A heal that throws is treated like any other restore failure: retried under
33787
+ * the bound, then reported — never swallowed.
33788
+ */
33789
+ async healSavedConfig(_saved, _allSaved) {}
33755
33790
  async onRestoreDevices(savedDevices) {
33756
33791
  const restored = /* @__PURE__ */ new Set();
33757
33792
  const failures = [];
@@ -33760,6 +33795,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33760
33795
  const Class = this.deviceClasses[saved.type];
33761
33796
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33762
33797
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33798
+ await this.healSavedConfig(saved, savedDevices);
33763
33799
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33764
33800
  restored.add(saved.id);
33765
33801
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-hikvision",
3
- "version": "1.2.68",
3
+ "version": "1.2.69",
4
4
  "description": "Hikvision camera device provider addon for CamStack — ISAPI over HTTP(S) with digest auth (snapshot, alarm stream, RTSP discovery)",
5
5
  "keywords": [
6
6
  "camstack",