@camstack/addon-provider-gree 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
@@ -12759,7 +12759,26 @@ var DiscoveryCandidateSchema = object({
12759
12759
  * identity ahead of adoption. Rendering metadata (unit, precision)
12760
12760
  * flows live through the cap STATUS SLICE after adoption.
12761
12761
  */
12762
- sourceInfo: SourceInfoSchema.optional()
12762
+ sourceInfo: SourceInfoSchema.optional(),
12763
+ /**
12764
+ * Set when this candidate is a device the provider ALREADY owns.
12765
+ *
12766
+ * A scan cannot generally produce the identity a device was onboarded under
12767
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12768
+ * comparison never matches and an owned device looks addable. Re-adopting one
12769
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12770
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12771
+ * three child cameras offline for four hours.
12772
+ *
12773
+ * A provider that can recognise its own devices says so here. Absent means
12774
+ * "not recognised", which is not the same as "known to be new" — a provider
12775
+ * that cannot tell simply never sets it.
12776
+ */
12777
+ alreadyOnboarded: boolean().optional(),
12778
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12779
+ onboardedDeviceId: number().optional(),
12780
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12781
+ onboardedName: string().optional()
12763
12782
  });
12764
12783
  /**
12765
12784
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -32997,6 +33016,22 @@ var BaseDeviceProvider = class extends BaseAddon {
32997
33016
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
32998
33017
  * `getStatus().error`.
32999
33018
  */
33019
+ /**
33020
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33021
+ * Default: no-op — most providers have nothing to heal.
33022
+ *
33023
+ * This exists because a restored device self-hydrates from the DB: `create()`
33024
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33025
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33026
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33027
+ * been emptied failed all four bounded attempts against fields
33028
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33029
+ *
33030
+ * Implementations get every saved row, so a child can read its parent's blob.
33031
+ * A heal that throws is treated like any other restore failure: retried under
33032
+ * the bound, then reported — never swallowed.
33033
+ */
33034
+ async healSavedConfig(_saved, _allSaved) {}
33000
33035
  async onRestoreDevices(savedDevices) {
33001
33036
  const restored = /* @__PURE__ */ new Set();
33002
33037
  const failures = [];
@@ -33005,6 +33040,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33005
33040
  const Class = this.deviceClasses[saved.type];
33006
33041
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33007
33042
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33043
+ await this.healSavedConfig(saved, savedDevices);
33008
33044
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33009
33045
  restored.add(saved.id);
33010
33046
  };
package/dist/addon.mjs CHANGED
@@ -12758,7 +12758,26 @@ var DiscoveryCandidateSchema = object({
12758
12758
  * identity ahead of adoption. Rendering metadata (unit, precision)
12759
12759
  * flows live through the cap STATUS SLICE after adoption.
12760
12760
  */
12761
- sourceInfo: SourceInfoSchema.optional()
12761
+ sourceInfo: SourceInfoSchema.optional(),
12762
+ /**
12763
+ * Set when this candidate is a device the provider ALREADY owns.
12764
+ *
12765
+ * A scan cannot generally produce the identity a device was onboarded under
12766
+ * (Reolink keys on `mac-<mac>`, learned at adopt time), so a stableId
12767
+ * comparison never matches and an owned device looks addable. Re-adopting one
12768
+ * overwrites its config with scan-derived values — that is how a Home Hub's
12769
+ * Baichuan port was overwritten with its ONVIF port, taking the hub and its
12770
+ * three child cameras offline for four hours.
12771
+ *
12772
+ * A provider that can recognise its own devices says so here. Absent means
12773
+ * "not recognised", which is not the same as "known to be new" — a provider
12774
+ * that cannot tell simply never sets it.
12775
+ */
12776
+ alreadyOnboarded: boolean().optional(),
12777
+ /** Numeric id of the device this candidate was matched to. Set with `alreadyOnboarded`. */
12778
+ onboardedDeviceId: number().optional(),
12779
+ /** Operator-facing name of the matched device, so the UI can say WHICH one it is. */
12780
+ onboardedName: string().optional()
12762
12781
  });
12763
12782
  /**
12764
12783
  * Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
@@ -32996,6 +33015,22 @@ var BaseDeviceProvider = class extends BaseAddon {
32996
33015
  * failed — logged at ERROR with `tags.deviceId` and surfaced via
32997
33016
  * `getStatus().error`.
32998
33017
  */
33018
+ /**
33019
+ * Repair a row's PERSISTED config blob immediately before it is restored.
33020
+ * Default: no-op — most providers have nothing to heal.
33021
+ *
33022
+ * This exists because a restored device self-hydrates from the DB: `create()`
33023
+ * passes `{}` and `BaseDevice` parses the stored blob against the device
33024
+ * schema. A blob that lost a REQUIRED field therefore fails restore forever,
33025
+ * and no later pass revisits it — a hub-adopted Reolink camera whose blob had
33026
+ * been emptied failed all four bounded attempts against fields
33027
+ * (`host`, `password`) it inherits from its parent and never dials itself.
33028
+ *
33029
+ * Implementations get every saved row, so a child can read its parent's blob.
33030
+ * A heal that throws is treated like any other restore failure: retried under
33031
+ * the bound, then reported — never swallowed.
33032
+ */
33033
+ async healSavedConfig(_saved, _allSaved) {}
32999
33034
  async onRestoreDevices(savedDevices) {
33000
33035
  const restored = /* @__PURE__ */ new Set();
33001
33036
  const failures = [];
@@ -33004,6 +33039,7 @@ var BaseDeviceProvider = class extends BaseAddon {
33004
33039
  const Class = this.deviceClasses[saved.type];
33005
33040
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
33006
33041
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
33042
+ await this.healSavedConfig(saved, savedDevices);
33007
33043
  await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
33008
33044
  restored.add(saved.id);
33009
33045
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-gree",
3
- "version": "0.2.59",
3
+ "version": "0.2.61",
4
4
  "description": "Gree air-conditioner device-provider addon for CamStack — wraps the @apocaliss92/nodegree local-UDP client (LAN discovery + AES control), exposing climate-control and fan-control",
5
5
  "keywords": [
6
6
  "camstack",