@camstack/addon-provider-rtsp 1.2.60 → 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
@@ -12812,7 +12812,26 @@ var DiscoveryCandidateSchema = object({
12812
12812
  * identity ahead of adoption. Rendering metadata (unit, precision)
12813
12813
  * flows live through the cap STATUS SLICE after adoption.
12814
12814
  */
12815
- sourceInfo: SourceInfoSchema.optional()
12815
+ sourceInfo: SourceInfoSchema.optional(),
12816
+ /**
12817
+ * Set when this candidate is a device the provider ALREADY owns.
12818
+ *
12819
+ * A scan cannot generally produce the identity a device was onboarded under
12820
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12821
+ * comparison never matches and an owned device looks addable. Re-adopting one
12822
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12823
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12824
+ * three child cameras offline for four hours.
12825
+ *
12826
+ * A provider that can recognise its own devices says so here. Absent means
12827
+ * "not recognised", which is not the same as "known to be new" — a provider
12828
+ * that cannot tell simply never sets it.
12829
+ */
12830
+ alreadyOnboarded: boolean().optional(),
12831
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12832
+ onboardedDeviceId: number().optional(),
12833
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12834
+ onboardedName: string().optional()
12816
12835
  });
12817
12836
  /**
12818
12837
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33162,6 +33181,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33162
33181
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33163
33182
  * `getStatus().error`.
33164
33183
  */
33184
+ /**
33185
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33186
+ * Default: no-op — most providers have nothing to heal.
33187
+ *
33188
+ * This exists because a restored device self-hydrates from the DB: `create()`
33189
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33190
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33191
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33192
+ * been emptied failed all four bounded attempts against fields
33193
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33194
+ *
33195
+ * Implementations get every saved row, so a child can read its parent's blob.
33196
+ * A heal that throws is treated like any other restore failure: retried under
33197
+ * the bound, then reported — never swallowed.
33198
+ */
33199
+ async healSavedConfig(_saved, _allSaved) {}
33165
33200
  async onRestoreDevices(savedDevices) {
33166
33201
  const restored = /* @__PURE__ */ new Set();
33167
33202
  const failures = [];
@@ -33170,6 +33205,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33170
33205
  const Class = this.deviceClasses[saved.type];
33171
33206
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33172
33207
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33208
+ await this.healSavedConfig(saved, savedDevices);
33173
33209
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33174
33210
  restored.add(saved.id);
33175
33211
  };
package/dist/addon.mjs CHANGED
@@ -12788,7 +12788,26 @@ var DiscoveryCandidateSchema = object({
12788
12788
  * identity ahead of adoption. Rendering metadata (unit, precision)
12789
12789
  * flows live through the cap STATUS SLICE after adoption.
12790
12790
  */
12791
- sourceInfo: SourceInfoSchema.optional()
12791
+ sourceInfo: SourceInfoSchema.optional(),
12792
+ /**
12793
+ * Set when this candidate is a device the provider ALREADY owns.
12794
+ *
12795
+ * A scan cannot generally produce the identity a device was onboarded under
12796
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12797
+ * comparison never matches and an owned device looks addable. Re-adopting one
12798
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12799
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12800
+ * three child cameras offline for four hours.
12801
+ *
12802
+ * A provider that can recognise its own devices says so here. Absent means
12803
+ * "not recognised", which is not the same as "known to be new" — a provider
12804
+ * that cannot tell simply never sets it.
12805
+ */
12806
+ alreadyOnboarded: boolean().optional(),
12807
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12808
+ onboardedDeviceId: number().optional(),
12809
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12810
+ onboardedName: string().optional()
12792
12811
  });
12793
12812
  /**
12794
12813
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33138,6 +33157,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33138
33157
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33139
33158
  * `getStatus().error`.
33140
33159
  */
33160
+ /**
33161
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33162
+ * Default: no-op — most providers have nothing to heal.
33163
+ *
33164
+ * This exists because a restored device self-hydrates from the DB: `create()`
33165
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33166
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33167
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33168
+ * been emptied failed all four bounded attempts against fields
33169
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33170
+ *
33171
+ * Implementations get every saved row, so a child can read its parent's blob.
33172
+ * A heal that throws is treated like any other restore failure: retried under
33173
+ * the bound, then reported — never swallowed.
33174
+ */
33175
+ async healSavedConfig(_saved, _allSaved) {}
33141
33176
  async onRestoreDevices(savedDevices) {
33142
33177
  const restored = /* @__PURE__ */ new Set();
33143
33178
  const failures = [];
@@ -33146,6 +33181,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33146
33181
  const Class = this.deviceClasses[saved.type];
33147
33182
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33148
33183
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33184
+ await this.healSavedConfig(saved, savedDevices);
33149
33185
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33150
33186
  restored.add(saved.id);
33151
33187
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rtsp",
3
- "version": "1.2.60",
3
+ "version": "1.2.61",
4
4
  "description": "Generic RTSP camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",