@camstack/addon-provider-rademacher 0.2.59 → 0.2.60

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
@@ -13741,7 +13741,26 @@ var DiscoveryCandidateSchema = object({
13741
13741
  * identity ahead of adoption. Rendering metadata (unit, precision)
13742
13742
  * flows live through the cap STATUS SLICE after adoption.
13743
13743
  */
13744
- sourceInfo: SourceInfoSchema.optional()
13744
+ sourceInfo: SourceInfoSchema.optional(),
13745
+ /**
13746
+ * Set when this candidate is a device the provider ALREADY owns.
13747
+ *
13748
+ * A scan cannot generally produce the identity a device was onboarded under
13749
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
13750
+ * comparison never matches and an owned device looks addable. Re-adopting one
13751
+ * overwrites its config with scan-derived values — that is how a Home Hub's
13752
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
13753
+ * three child cameras offline for four hours.
13754
+ *
13755
+ * A provider that can recognise its own devices says so here. Absent means
13756
+ * "not recognised", which is not the same as "known to be new" — a provider
13757
+ * that cannot tell simply never sets it.
13758
+ */
13759
+ alreadyOnboarded: boolean().optional(),
13760
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
13761
+ onboardedDeviceId: number().optional(),
13762
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
13763
+ onboardedName: string().optional()
13745
13764
  });
13746
13765
  /**
13747
13766
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33979,6 +33998,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33979
33998
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33980
33999
  * `getStatus().error`.
33981
34000
  */
34001
+ /**
34002
+ * Repair a row's PERSISTED config blob immediately before it is restored.
34003
+ * Default: no-op — most providers have nothing to heal.
34004
+ *
34005
+ * This exists because a restored device self-hydrates from the DB: `create()`
34006
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
34007
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
34008
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
34009
+ * been emptied failed all four bounded attempts against fields
34010
+ * (`host`, `password`) it inherits from its parent and never dials itself.
34011
+ *
34012
+ * Implementations get every saved row, so a child can read its parent's blob.
34013
+ * A heal that throws is treated like any other restore failure: retried under
34014
+ * the bound, then reported — never swallowed.
34015
+ */
34016
+ async healSavedConfig(_saved, _allSaved) {}
33982
34017
  async onRestoreDevices(savedDevices) {
33983
34018
  const restored = /* @__PURE__ */ new Set();
33984
34019
  const failures = [];
@@ -33987,6 +34022,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33987
34022
  const Class = this.deviceClasses[saved.type];
33988
34023
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33989
34024
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
34025
+ await this.healSavedConfig(saved, savedDevices);
33990
34026
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33991
34027
  restored.add(saved.id);
33992
34028
  };
package/dist/addon.mjs CHANGED
@@ -13740,7 +13740,26 @@ var DiscoveryCandidateSchema = object({
13740
13740
  * identity ahead of adoption. Rendering metadata (unit, precision)
13741
13741
  * flows live through the cap STATUS SLICE after adoption.
13742
13742
  */
13743
- sourceInfo: SourceInfoSchema.optional()
13743
+ sourceInfo: SourceInfoSchema.optional(),
13744
+ /**
13745
+ * Set when this candidate is a device the provider ALREADY owns.
13746
+ *
13747
+ * A scan cannot generally produce the identity a device was onboarded under
13748
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
13749
+ * comparison never matches and an owned device looks addable. Re-adopting one
13750
+ * overwrites its config with scan-derived values — that is how a Home Hub's
13751
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
13752
+ * three child cameras offline for four hours.
13753
+ *
13754
+ * A provider that can recognise its own devices says so here. Absent means
13755
+ * "not recognised", which is not the same as "known to be new" — a provider
13756
+ * that cannot tell simply never sets it.
13757
+ */
13758
+ alreadyOnboarded: boolean().optional(),
13759
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
13760
+ onboardedDeviceId: number().optional(),
13761
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
13762
+ onboardedName: string().optional()
13744
13763
  });
13745
13764
  /**
13746
13765
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33978,6 +33997,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33978
33997
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33979
33998
  * `getStatus().error`.
33980
33999
  */
34000
+ /**
34001
+ * Repair a row's PERSISTED config blob immediately before it is restored.
34002
+ * Default: no-op — most providers have nothing to heal.
34003
+ *
34004
+ * This exists because a restored device self-hydrates from the DB: `create()`
34005
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
34006
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
34007
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
34008
+ * been emptied failed all four bounded attempts against fields
34009
+ * (`host`, `password`) it inherits from its parent and never dials itself.
34010
+ *
34011
+ * Implementations get every saved row, so a child can read its parent's blob.
34012
+ * A heal that throws is treated like any other restore failure: retried under
34013
+ * the bound, then reported — never swallowed.
34014
+ */
34015
+ async healSavedConfig(_saved, _allSaved) {}
33981
34016
  async onRestoreDevices(savedDevices) {
33982
34017
  const restored = /* @__PURE__ */ new Set();
33983
34018
  const failures = [];
@@ -33986,6 +34021,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33986
34021
  const Class = this.deviceClasses[saved.type];
33987
34022
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33988
34023
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
34024
+ await this.healSavedConfig(saved, savedDevices);
33989
34025
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33990
34026
  restored.add(saved.id);
33991
34027
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.2.59",
3
+ "version": "0.2.60",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",