@camstack/addon-provider-rademacher 0.2.64 → 0.2.66

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
@@ -8538,7 +8538,8 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
8538
8538
  var CameraSwitchUnavailableReasonSchema = _enum([
8539
8539
  "no-provider",
8540
8540
  "source-unreachable",
8541
- "not-configured"
8541
+ "not-configured",
8542
+ "device-disabled"
8542
8543
  ]);
8543
8544
  /**
8544
8545
  * One switch, resolved for one camera.
@@ -24561,12 +24562,20 @@ var BatteryStatusSchema = object({
24561
24562
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
24562
24563
  * Reolink-specific for the Solar Panel 2 accessory (will become
24563
24564
  * common on other battery cams). `'none'` means running on battery
24564
- * alone.
24565
+ * alone — a NEGATIVE the firmware actually reported.
24566
+ *
24567
+ * `'unknown'` is the absence of an answer: the provider has not read the
24568
+ * power fields yet, or the firmware reported neither. Before it existed,
24569
+ * two unreadable fields produced `'none'`, and a camera nobody had reached
24570
+ * was drawn "on battery alone" — the same D315 defect `percentage` closed
24571
+ * by going nullable. Consumers SKIP it (no charger state published, no
24572
+ * glyph, no HomeKit ChargingState) rather than coerce it either way.
24565
24573
  */
24566
24574
  charging: _enum([
24567
24575
  "dc",
24568
24576
  "solar",
24569
- "none"
24577
+ "none",
24578
+ "unknown"
24570
24579
  ]),
24571
24580
  /**
24572
24581
  * True when the camera firmware has gone into low-power mode. Battery
@@ -24656,7 +24665,7 @@ onStatusChanged: { data: object({
24656
24665
  kind: "push",
24657
24666
  empty: {
24658
24667
  percentage: 0,
24659
- charging: "none",
24668
+ charging: "unknown",
24660
24669
  sleeping: false,
24661
24670
  lastUpdated: 0
24662
24671
  }
@@ -34064,13 +34073,30 @@ var BaseDeviceProvider = class extends BaseAddon {
34064
34073
  async onRestoreDevices(savedDevices) {
34065
34074
  const restored = /* @__PURE__ */ new Set();
34066
34075
  const failures = [];
34076
+ const liveStableIds = new Set((await this.ctx.kernel.devices.getAll()).map((device) => device.stableId));
34067
34077
  const attemptRestore = async (saved) => {
34068
34078
  if (restored.has(saved.id)) return;
34079
+ if (liveStableIds.has(saved.stableId)) {
34080
+ this.ctx.logger.debug("Restore row already live — not dialled again", { tags: {
34081
+ deviceId: saved.id,
34082
+ stableId: saved.stableId
34083
+ } });
34084
+ restored.add(saved.id);
34085
+ return;
34086
+ }
34069
34087
  const Class = this.deviceClasses[saved.type];
34070
34088
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
34071
34089
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
34072
34090
  await this.healSavedConfig(saved, savedDevices);
34073
- await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
34091
+ try {
34092
+ await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
34093
+ } catch (err) {
34094
+ if (!(await this.ctx.kernel.devices.getAll()).some((device) => device.stableId === saved.stableId)) throw err;
34095
+ this.ctx.logger.debug("Restore row came to life during its own dial — kept", { tags: {
34096
+ deviceId: saved.id,
34097
+ stableId: saved.stableId
34098
+ } });
34099
+ }
34074
34100
  restored.add(saved.id);
34075
34101
  };
34076
34102
  const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
package/dist/addon.mjs CHANGED
@@ -8537,7 +8537,8 @@ var CameraSwitchAuthoritySchema = discriminatedUnion("kind", [
8537
8537
  var CameraSwitchUnavailableReasonSchema = _enum([
8538
8538
  "no-provider",
8539
8539
  "source-unreachable",
8540
- "not-configured"
8540
+ "not-configured",
8541
+ "device-disabled"
8541
8542
  ]);
8542
8543
  /**
8543
8544
  * One switch, resolved for one camera.
@@ -24560,12 +24561,20 @@ var BatteryStatusSchema = object({
24560
24561
  * Charging source. `'dc'` covers wall/USB adapters; `'solar'` is
24561
24562
  * Reolink-specific for the Solar Panel 2 accessory (will become
24562
24563
  * common on other battery cams). `'none'` means running on battery
24563
- * alone.
24564
+ * alone — a NEGATIVE the firmware actually reported.
24565
+ *
24566
+ * `'unknown'` is the absence of an answer: the provider has not read the
24567
+ * power fields yet, or the firmware reported neither. Before it existed,
24568
+ * two unreadable fields produced `'none'`, and a camera nobody had reached
24569
+ * was drawn "on battery alone" — the same D315 defect `percentage` closed
24570
+ * by going nullable. Consumers SKIP it (no charger state published, no
24571
+ * glyph, no HomeKit ChargingState) rather than coerce it either way.
24564
24572
  */
24565
24573
  charging: _enum([
24566
24574
  "dc",
24567
24575
  "solar",
24568
- "none"
24576
+ "none",
24577
+ "unknown"
24569
24578
  ]),
24570
24579
  /**
24571
24580
  * True when the camera firmware has gone into low-power mode. Battery
@@ -24655,7 +24664,7 @@ onStatusChanged: { data: object({
24655
24664
  kind: "push",
24656
24665
  empty: {
24657
24666
  percentage: 0,
24658
- charging: "none",
24667
+ charging: "unknown",
24659
24668
  sleeping: false,
24660
24669
  lastUpdated: 0
24661
24670
  }
@@ -34063,13 +34072,30 @@ var BaseDeviceProvider = class extends BaseAddon {
34063
34072
  async onRestoreDevices(savedDevices) {
34064
34073
  const restored = /* @__PURE__ */ new Set();
34065
34074
  const failures = [];
34075
+ const liveStableIds = new Set((await this.ctx.kernel.devices.getAll()).map((device) => device.stableId));
34066
34076
  const attemptRestore = async (saved) => {
34067
34077
  if (restored.has(saved.id)) return;
34078
+ if (liveStableIds.has(saved.stableId)) {
34079
+ this.ctx.logger.debug("Restore row already live — not dialled again", { tags: {
34080
+ deviceId: saved.id,
34081
+ stableId: saved.stableId
34082
+ } });
34083
+ restored.add(saved.id);
34084
+ return;
34085
+ }
34068
34086
  const Class = this.deviceClasses[saved.type];
34069
34087
  if (!Class) throw new Error(`no device class registered for type "${saved.type}"`);
34070
34088
  if (saved.parentDeviceId !== null && !restored.has(saved.parentDeviceId)) throw new Error(`parent device ${saved.parentDeviceId} not restored`);
34071
34089
  await this.healSavedConfig(saved, savedDevices);
34072
- await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
34090
+ try {
34091
+ await this.ctx.kernel.devices.create(saved.stableId, Class, {}, saved.parentDeviceId);
34092
+ } catch (err) {
34093
+ if (!(await this.ctx.kernel.devices.getAll()).some((device) => device.stableId === saved.stableId)) throw err;
34094
+ this.ctx.logger.debug("Restore row came to life during its own dial — kept", { tags: {
34095
+ deviceId: saved.id,
34096
+ stableId: saved.stableId
34097
+ } });
34098
+ }
34073
34099
  restored.add(saved.id);
34074
34100
  };
34075
34101
  const topLevel = savedDevices.filter((saved) => saved.parentDeviceId === null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.2.64",
3
+ "version": "0.2.66",
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",