@camstack/addon-matter-broker 0.2.59 → 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
@@ -12836,7 +12836,26 @@ var DiscoveryCandidateSchema = object({
12836
12836
  * identity ahead of adoption. Rendering metadata (unit, precision)
12837
12837
  * flows live through the cap STATUS SLICE after adoption.
12838
12838
  */
12839
- sourceInfo: SourceInfoSchema.optional()
12839
+ sourceInfo: SourceInfoSchema.optional(),
12840
+ /**
12841
+ * Set when this candidate is a device the provider ALREADY owns.
12842
+ *
12843
+ * A scan cannot generally produce the identity a device was onboarded under
12844
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12845
+ * comparison never matches and an owned device looks addable. Re-adopting one
12846
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12847
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12848
+ * three child cameras offline for four hours.
12849
+ *
12850
+ * A provider that can recognise its own devices says so here. Absent means
12851
+ * "not recognised", which is not the same as "known to be new" — a provider
12852
+ * that cannot tell simply never sets it.
12853
+ */
12854
+ alreadyOnboarded: boolean().optional(),
12855
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12856
+ onboardedDeviceId: number().optional(),
12857
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12858
+ onboardedName: string$2().optional()
12840
12859
  });
12841
12860
  /**
12842
12861
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33091,6 +33110,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33091
33110
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33092
33111
  * `getStatus().error`.
33093
33112
  */
33113
+ /**
33114
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33115
+ * Default: no-op — most providers have nothing to heal.
33116
+ *
33117
+ * This exists because a restored device self-hydrates from the DB: `create()`
33118
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33119
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33120
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33121
+ * been emptied failed all four bounded attempts against fields
33122
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33123
+ *
33124
+ * Implementations get every saved row, so a child can read its parent's blob.
33125
+ * A heal that throws is treated like any other restore failure: retried under
33126
+ * the bound, then reported — never swallowed.
33127
+ */
33128
+ async healSavedConfig(_saved, _allSaved) {}
33094
33129
  async onRestoreDevices(savedDevices) {
33095
33130
  const restored = /* @__PURE__ */ new Set();
33096
33131
  const failures = [];
@@ -33099,6 +33134,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33099
33134
  const Class = this.deviceClasses[saved.type];
33100
33135
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33101
33136
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33137
+ await this.healSavedConfig(saved, savedDevices);
33102
33138
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33103
33139
  restored.add(saved.id);
33104
33140
  };
package/dist/addon.mjs CHANGED
@@ -12834,7 +12834,26 @@ var DiscoveryCandidateSchema = object({
12834
12834
  * identity ahead of adoption. Rendering metadata (unit, precision)
12835
12835
  * flows live through the cap STATUS SLICE after adoption.
12836
12836
  */
12837
- sourceInfo: SourceInfoSchema.optional()
12837
+ sourceInfo: SourceInfoSchema.optional(),
12838
+ /**
12839
+ * Set when this candidate is a device the provider ALREADY owns.
12840
+ *
12841
+ * A scan cannot generally produce the identity a device was onboarded under
12842
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12843
+ * comparison never matches and an owned device looks addable. Re-adopting one
12844
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12845
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12846
+ * three child cameras offline for four hours.
12847
+ *
12848
+ * A provider that can recognise its own devices says so here. Absent means
12849
+ * "not recognised", which is not the same as "known to be new" — a provider
12850
+ * that cannot tell simply never sets it.
12851
+ */
12852
+ alreadyOnboarded: boolean().optional(),
12853
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12854
+ onboardedDeviceId: number().optional(),
12855
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12856
+ onboardedName: string$2().optional()
12838
12857
  });
12839
12858
  /**
12840
12859
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -33089,6 +33108,22 @@ var BaseDeviceProvider = class extends BaseAddon {
33089
33108
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
33090
33109
  * `getStatus().error`.
33091
33110
  */
33111
+ /**
33112
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33113
+ * Default: no-op — most providers have nothing to heal.
33114
+ *
33115
+ * This exists because a restored device self-hydrates from the DB: `create()`
33116
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33117
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33118
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33119
+ * been emptied failed all four bounded attempts against fields
33120
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33121
+ *
33122
+ * Implementations get every saved row, so a child can read its parent's blob.
33123
+ * A heal that throws is treated like any other restore failure: retried under
33124
+ * the bound, then reported — never swallowed.
33125
+ */
33126
+ async healSavedConfig(_saved, _allSaved) {}
33092
33127
  async onRestoreDevices(savedDevices) {
33093
33128
  const restored = /* @__PURE__ */ new Set();
33094
33129
  const failures = [];
@@ -33097,6 +33132,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33097
33132
  const Class = this.deviceClasses[saved.type];
33098
33133
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33099
33134
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33135
+ await this.healSavedConfig(saved, savedDevices);
33100
33136
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33101
33137
  restored.add(saved.id);
33102
33138
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-matter-broker",
3
- "version": "0.2.59",
3
+ "version": "0.2.61",
4
4
  "description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
5
5
  "keywords": [
6
6
  "camstack",