@camstack/addon-provider-reolink 1.2.5 → 1.2.6

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
@@ -222620,6 +222620,33 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
222620
222620
  markWakeIssued() {
222621
222621
  this.lastProactiveWakeAt = Date.now();
222622
222622
  }
222623
+ /**
222624
+ * Gate for a PROACTIVE camera read, checked BEFORE `ensureApi()`.
222625
+ *
222626
+ * The login itself is the wake. On a sleeping UDP/battery camera the
222627
+ * lib's discovery + handshake nudges the firmware awake (same reason
222628
+ * `refreshParentSettingsSnapshot` refuses to call `ensureApi` while
222629
+ * asleep), so gating only the explicit `wakeUp()` call is useless —
222630
+ * by the time we reach it the camera is already up. Production logs
222631
+ * showed exactly that: a background read produced a full
222632
+ * `Connecting to Reolink` → BCUDP discovery → `battery sleep state
222633
+ * committed` (awake) → whole refresh cascade, on a camera that had
222634
+ * been asleep for six minutes.
222635
+ *
222636
+ * Returns `false` when the caller must serve cache / bail out without
222637
+ * touching the socket. Stamps the cooldown when it does let a wake
222638
+ * through, so "at most one proactive wake per
222639
+ * `PROACTIVE_WAKE_COOLDOWN_MS`" holds across ALL proactive callers
222640
+ * rather than per-caller.
222641
+ *
222642
+ * Demand-driven paths never call this — see `canProactivelyWake`.
222643
+ */
222644
+ allowProactiveCameraAccess(reason) {
222645
+ if (!this.isBattery || !this.sleeping) return true;
222646
+ if (!this.canProactivelyWake(reason)) return false;
222647
+ this.markWakeIssued();
222648
+ return true;
222649
+ }
222623
222650
  updateBatteryCache(info) {
222624
222651
  this.setCapSlice(batteryCapability, this.mapBatteryInfo(info));
222625
222652
  }
@@ -222865,6 +222892,10 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
222865
222892
  }
222866
222893
  async fetchSnapshotWithSingleFlight() {
222867
222894
  if (this.snapshotInFlight) return this.snapshotInFlight;
222895
+ if (!this.allowProactiveCameraAccess("snapshot")) {
222896
+ this.ctx.logger.debug("snapshot: skipped — sleeping battery cam inside wake cooldown", { tags: { deviceId: this.id } });
222897
+ return null;
222898
+ }
222868
222899
  const promise = (async () => {
222869
222900
  let api;
222870
222901
  try {
@@ -222882,23 +222913,16 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
222882
222913
  } catch {
222883
222914
  return true;
222884
222915
  }
222885
- })()) {
222886
- if (!this.canProactivelyWake("snapshot")) {
222887
- this.ctx.logger.debug("snapshot: skipped — sleeping battery cam inside wake cooldown", { tags: { deviceId: this.id } });
222888
- return null;
222889
- }
222890
- this.markWakeIssued();
222891
- try {
222892
- await api.wakeUp(this.getChannel(), {
222893
- waitAfterWakeMs: 1500,
222894
- attempts: 2
222895
- });
222896
- } catch (err) {
222897
- this.ctx.logger.debug("snapshot: pre-wake failed (will still try getSnapshot)", {
222898
- tags: { deviceId: this.id },
222899
- meta: { error: err instanceof Error ? err.message : String(err) }
222900
- });
222901
- }
222916
+ })()) try {
222917
+ await api.wakeUp(this.getChannel(), {
222918
+ waitAfterWakeMs: 1500,
222919
+ attempts: 2
222920
+ });
222921
+ } catch (err) {
222922
+ this.ctx.logger.debug("snapshot: pre-wake failed (will still try getSnapshot)", {
222923
+ tags: { deviceId: this.id },
222924
+ meta: { error: err instanceof Error ? err.message : String(err) }
222925
+ });
222902
222926
  }
222903
222927
  const tryOnce = async (timeoutMs) => {
222904
222928
  const buf = await api.getSnapshot(this.getChannel(), {
@@ -225262,6 +225286,10 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
225262
225286
  if (this.batteryUpdateInFlight) return this.batteryUpdateInFlight;
225263
225287
  const cycle = (async () => {
225264
225288
  try {
225289
+ if (!this.allowProactiveCameraAccess("battery-update")) {
225290
+ this.ctx.logger.debug("battery-update: cam sleeping — skipping cycle without connecting", { tags: { deviceId: this.id } });
225291
+ return;
225292
+ }
225265
225293
  let api;
225266
225294
  try {
225267
225295
  api = await this.ensureApi();
@@ -227113,6 +227141,13 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
227113
227141
  }
227114
227142
  if (this.api) return this.api;
227115
227143
  if (this.loginPromise) return this.loginPromise;
227144
+ if (this.isBattery && this.sleeping) {
227145
+ const frames = (/* @__PURE__ */ new Error("wake-attribution")).stack?.split("\n").slice(2, 7).join(" | ");
227146
+ this.ctx.logger.info("battery cam login while sleeping — this wakes the camera", {
227147
+ tags: { deviceId: this.id },
227148
+ meta: { caller: frames ?? "unavailable" }
227149
+ });
227150
+ }
227116
227151
  const host = this.config.get("host");
227117
227152
  const port = this.config.get("port");
227118
227153
  const username = this.config.get("username");
package/dist/addon.mjs CHANGED
@@ -222600,6 +222600,33 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
222600
222600
  markWakeIssued() {
222601
222601
  this.lastProactiveWakeAt = Date.now();
222602
222602
  }
222603
+ /**
222604
+ * Gate for a PROACTIVE camera read, checked BEFORE `ensureApi()`.
222605
+ *
222606
+ * The login itself is the wake. On a sleeping UDP/battery camera the
222607
+ * lib's discovery + handshake nudges the firmware awake (same reason
222608
+ * `refreshParentSettingsSnapshot` refuses to call `ensureApi` while
222609
+ * asleep), so gating only the explicit `wakeUp()` call is useless —
222610
+ * by the time we reach it the camera is already up. Production logs
222611
+ * showed exactly that: a background read produced a full
222612
+ * `Connecting to Reolink` → BCUDP discovery → `battery sleep state
222613
+ * committed` (awake) → whole refresh cascade, on a camera that had
222614
+ * been asleep for six minutes.
222615
+ *
222616
+ * Returns `false` when the caller must serve cache / bail out without
222617
+ * touching the socket. Stamps the cooldown when it does let a wake
222618
+ * through, so "at most one proactive wake per
222619
+ * `PROACTIVE_WAKE_COOLDOWN_MS`" holds across ALL proactive callers
222620
+ * rather than per-caller.
222621
+ *
222622
+ * Demand-driven paths never call this — see `canProactivelyWake`.
222623
+ */
222624
+ allowProactiveCameraAccess(reason) {
222625
+ if (!this.isBattery || !this.sleeping) return true;
222626
+ if (!this.canProactivelyWake(reason)) return false;
222627
+ this.markWakeIssued();
222628
+ return true;
222629
+ }
222603
222630
  updateBatteryCache(info) {
222604
222631
  this.setCapSlice(batteryCapability, this.mapBatteryInfo(info));
222605
222632
  }
@@ -222845,6 +222872,10 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
222845
222872
  }
222846
222873
  async fetchSnapshotWithSingleFlight() {
222847
222874
  if (this.snapshotInFlight) return this.snapshotInFlight;
222875
+ if (!this.allowProactiveCameraAccess("snapshot")) {
222876
+ this.ctx.logger.debug("snapshot: skipped — sleeping battery cam inside wake cooldown", { tags: { deviceId: this.id } });
222877
+ return null;
222878
+ }
222848
222879
  const promise = (async () => {
222849
222880
  let api;
222850
222881
  try {
@@ -222862,23 +222893,16 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
222862
222893
  } catch {
222863
222894
  return true;
222864
222895
  }
222865
- })()) {
222866
- if (!this.canProactivelyWake("snapshot")) {
222867
- this.ctx.logger.debug("snapshot: skipped — sleeping battery cam inside wake cooldown", { tags: { deviceId: this.id } });
222868
- return null;
222869
- }
222870
- this.markWakeIssued();
222871
- try {
222872
- await api.wakeUp(this.getChannel(), {
222873
- waitAfterWakeMs: 1500,
222874
- attempts: 2
222875
- });
222876
- } catch (err) {
222877
- this.ctx.logger.debug("snapshot: pre-wake failed (will still try getSnapshot)", {
222878
- tags: { deviceId: this.id },
222879
- meta: { error: err instanceof Error ? err.message : String(err) }
222880
- });
222881
- }
222896
+ })()) try {
222897
+ await api.wakeUp(this.getChannel(), {
222898
+ waitAfterWakeMs: 1500,
222899
+ attempts: 2
222900
+ });
222901
+ } catch (err) {
222902
+ this.ctx.logger.debug("snapshot: pre-wake failed (will still try getSnapshot)", {
222903
+ tags: { deviceId: this.id },
222904
+ meta: { error: err instanceof Error ? err.message : String(err) }
222905
+ });
222882
222906
  }
222883
222907
  const tryOnce = async (timeoutMs) => {
222884
222908
  const buf = await api.getSnapshot(this.getChannel(), {
@@ -225242,6 +225266,10 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
225242
225266
  if (this.batteryUpdateInFlight) return this.batteryUpdateInFlight;
225243
225267
  const cycle = (async () => {
225244
225268
  try {
225269
+ if (!this.allowProactiveCameraAccess("battery-update")) {
225270
+ this.ctx.logger.debug("battery-update: cam sleeping — skipping cycle without connecting", { tags: { deviceId: this.id } });
225271
+ return;
225272
+ }
225245
225273
  let api;
225246
225274
  try {
225247
225275
  api = await this.ensureApi();
@@ -227093,6 +227121,13 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
227093
227121
  }
227094
227122
  if (this.api) return this.api;
227095
227123
  if (this.loginPromise) return this.loginPromise;
227124
+ if (this.isBattery && this.sleeping) {
227125
+ const frames = (/* @__PURE__ */ new Error("wake-attribution")).stack?.split("\n").slice(2, 7).join(" | ");
227126
+ this.ctx.logger.info("battery cam login while sleeping — this wakes the camera", {
227127
+ tags: { deviceId: this.id },
227128
+ meta: { caller: frames ?? "unavailable" }
227129
+ });
227130
+ }
227096
227131
  const host = this.config.get("host");
227097
227132
  const port = this.config.get("port");
227098
227133
  const username = this.config.get("username");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.2.5",
3
+ "version": "1.2.6",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",