@camstack/addon-provider-reolink 1.2.32 → 1.2.34

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
@@ -230953,6 +230953,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230953
230953
  });
230954
230954
  this.ctx.eventBus.emit(createEvent(EventCategory.BatteryOnWakeStarted, this.eventSource(), { deviceId: this.id }));
230955
230955
  const channel = this.getChannel();
230956
+ const isHubChild = this.isHubChild();
230957
+ const wakeTimeoutMs = isHubChild ? Math.max(timeoutMs, 2e4) : timeoutMs;
230956
230958
  try {
230957
230959
  await Promise.race([(async () => {
230958
230960
  await api.wakeUp(channel, {
@@ -230960,7 +230962,11 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230960
230962
  attempts: 2
230961
230963
  });
230962
230964
  await sleep$1(1500);
230963
- })(), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("wakeForStream timeout")), timeoutMs))]);
230965
+ if (isHubChild) {
230966
+ const dialId = buildCamStreamId("native", channel, "main", 1);
230967
+ await Promise.race([this.ensureRfc4571Server(dialId, channel, "main"), sleep$1(1e4)]);
230968
+ }
230969
+ })(), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("wakeForStream timeout")), wakeTimeoutMs))]);
230964
230970
  const CONFIRM_TIMEOUT_MS = 1e4;
230965
230971
  const CONFIRM_POLL_MS = 1e3;
230966
230972
  const confirmDeadline = Date.now() + CONFIRM_TIMEOUT_MS;
@@ -231002,6 +231008,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
231002
231008
  tags: { deviceId: this.id },
231003
231009
  meta: { confirmTimeoutMs: CONFIRM_TIMEOUT_MS }
231004
231010
  });
231011
+ this.emitWakeSettled();
231005
231012
  return {
231006
231013
  awoke: false,
231007
231014
  durationMs: Date.now() - startedAt
@@ -231019,6 +231026,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
231019
231026
  error: err instanceof Error ? err.message : String(err)
231020
231027
  }
231021
231028
  });
231029
+ this.emitWakeSettled();
231022
231030
  return {
231023
231031
  awoke: false,
231024
231032
  durationMs: Date.now() - startedAt
@@ -231184,6 +231192,26 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
231184
231192
  this.lastProactiveWakeAt = Date.now();
231185
231193
  }
231186
231194
  /**
231195
+ * Close the transient "waking" window on a wake that did NOT produce a
231196
+ * committed transition (ACK with no awake evidence, or a timeout).
231197
+ * `BatteryOnWakeStarted` opens it; normally the `sleeping: false` commit
231198
+ * closes it via `BatteryOnStatusChanged` — but a failed wake changes no
231199
+ * state, so nothing ever closes it, and listeners (the snapshot overlay,
231200
+ * the broker's "WAKING UP" placeholder) kept showing WAKING for the whole
231201
+ * 35 s TTL after the answer was already known (2026-08-15: the overlay
231202
+ * flapped sleeping↔waking on every retry — read as "random"). Re-asserting
231203
+ * the unchanged slice still emits the event (the runtime-state bridge
231204
+ * re-emits per WRITE), which is exactly the "wake is over, nothing came of
231205
+ * it" signal those windows listen for.
231206
+ */
231207
+ emitWakeSettled() {
231208
+ if (!this.isBattery) return;
231209
+ this.ctx.eventBus.emit(createEvent(EventCategory.BatteryOnStatusChanged, this.eventSource(), {
231210
+ deviceId: this.id,
231211
+ status: this.state.battery
231212
+ }));
231213
+ }
231214
+ /**
231187
231215
  * Gate for a PROACTIVE camera read, checked BEFORE `ensureApi()`.
231188
231216
  *
231189
231217
  * The login itself is the wake. On a sleeping UDP/battery camera the
@@ -236683,6 +236711,13 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236683
236711
  this.ensureApi().catch((err) => {
236684
236712
  this.ctx.logger.warn("Reolink Hub initial connect failed — will retry", { meta: { error: err instanceof Error ? err.message : String(err) } });
236685
236713
  });
236714
+ this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
236715
+ if (event.data.parentDeviceId !== this.id) return;
236716
+ (async () => {
236717
+ const byChannel = await this.loadAdoptedChildrenByChannel();
236718
+ for (const [ch, did] of byChannel) this.channelToDeviceId.set(ch, did);
236719
+ })().catch(() => {});
236720
+ }));
236686
236721
  this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceUnregistered }, (event) => {
236687
236722
  const data = event.data;
236688
236723
  if (data.parentDeviceId !== this.id) return;
@@ -236695,6 +236730,9 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236695
236730
  }));
236696
236731
  }
236697
236732
  eventUnsubscribers = [];
236733
+ /** Per-channel throttle for the unrouted-event warning (see
236734
+ * `routeSimpleEvent`) — a chatty unmapped channel must not flood. */
236735
+ unroutedChannelLoggedAt = /* @__PURE__ */ new Map();
236698
236736
  async removeDevice() {
236699
236737
  this.shutdownRequested = true;
236700
236738
  if (this.reconnectTimer) {
@@ -237251,7 +237289,18 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
237251
237289
  const channel = ev?.channel;
237252
237290
  if (typeof channel !== "number") return;
237253
237291
  const deviceId = this.channelToDeviceId.get(channel);
237254
- if (deviceId === void 0) return;
237292
+ if (deviceId === void 0) {
237293
+ const last = this.unroutedChannelLoggedAt.get(channel) ?? 0;
237294
+ const now = Date.now();
237295
+ if (now - last > 6e4) {
237296
+ this.unroutedChannelLoggedAt.set(channel, now);
237297
+ this.ctx.logger.warn("Reolink Hub: simpleEvent for an unmapped channel dropped", { meta: {
237298
+ channel,
237299
+ type: ev?.type ?? "unknown"
237300
+ } });
237301
+ }
237302
+ return;
237303
+ }
237255
237304
  this.ctx.devices.getAll().then((all) => all.find((d) => d.id === deviceId)).then((child) => {
237256
237305
  if (!(child instanceof ReolinkCamera)) return;
237257
237306
  child.handleSimpleEvent(ev);
package/dist/addon.mjs CHANGED
@@ -230933,6 +230933,8 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230933
230933
  });
230934
230934
  this.ctx.eventBus.emit(createEvent(EventCategory.BatteryOnWakeStarted, this.eventSource(), { deviceId: this.id }));
230935
230935
  const channel = this.getChannel();
230936
+ const isHubChild = this.isHubChild();
230937
+ const wakeTimeoutMs = isHubChild ? Math.max(timeoutMs, 2e4) : timeoutMs;
230936
230938
  try {
230937
230939
  await Promise.race([(async () => {
230938
230940
  await api.wakeUp(channel, {
@@ -230940,7 +230942,11 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230940
230942
  attempts: 2
230941
230943
  });
230942
230944
  await sleep$1(1500);
230943
- })(), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("wakeForStream timeout")), timeoutMs))]);
230945
+ if (isHubChild) {
230946
+ const dialId = buildCamStreamId("native", channel, "main", 1);
230947
+ await Promise.race([this.ensureRfc4571Server(dialId, channel, "main"), sleep$1(1e4)]);
230948
+ }
230949
+ })(), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("wakeForStream timeout")), wakeTimeoutMs))]);
230944
230950
  const CONFIRM_TIMEOUT_MS = 1e4;
230945
230951
  const CONFIRM_POLL_MS = 1e3;
230946
230952
  const confirmDeadline = Date.now() + CONFIRM_TIMEOUT_MS;
@@ -230982,6 +230988,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230982
230988
  tags: { deviceId: this.id },
230983
230989
  meta: { confirmTimeoutMs: CONFIRM_TIMEOUT_MS }
230984
230990
  });
230991
+ this.emitWakeSettled();
230985
230992
  return {
230986
230993
  awoke: false,
230987
230994
  durationMs: Date.now() - startedAt
@@ -230999,6 +231006,7 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230999
231006
  error: err instanceof Error ? err.message : String(err)
231000
231007
  }
231001
231008
  });
231009
+ this.emitWakeSettled();
231002
231010
  return {
231003
231011
  awoke: false,
231004
231012
  durationMs: Date.now() - startedAt
@@ -231164,6 +231172,26 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
231164
231172
  this.lastProactiveWakeAt = Date.now();
231165
231173
  }
231166
231174
  /**
231175
+ * Close the transient "waking" window on a wake that did NOT produce a
231176
+ * committed transition (ACK with no awake evidence, or a timeout).
231177
+ * `BatteryOnWakeStarted` opens it; normally the `sleeping: false` commit
231178
+ * closes it via `BatteryOnStatusChanged` — but a failed wake changes no
231179
+ * state, so nothing ever closes it, and listeners (the snapshot overlay,
231180
+ * the broker's "WAKING UP" placeholder) kept showing WAKING for the whole
231181
+ * 35 s TTL after the answer was already known (2026-08-15: the overlay
231182
+ * flapped sleeping↔waking on every retry — read as "random"). Re-asserting
231183
+ * the unchanged slice still emits the event (the runtime-state bridge
231184
+ * re-emits per WRITE), which is exactly the "wake is over, nothing came of
231185
+ * it" signal those windows listen for.
231186
+ */
231187
+ emitWakeSettled() {
231188
+ if (!this.isBattery) return;
231189
+ this.ctx.eventBus.emit(createEvent(EventCategory.BatteryOnStatusChanged, this.eventSource(), {
231190
+ deviceId: this.id,
231191
+ status: this.state.battery
231192
+ }));
231193
+ }
231194
+ /**
231167
231195
  * Gate for a PROACTIVE camera read, checked BEFORE `ensureApi()`.
231168
231196
  *
231169
231197
  * The login itself is the wake. On a sleeping UDP/battery camera the
@@ -236663,6 +236691,13 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236663
236691
  this.ensureApi().catch((err) => {
236664
236692
  this.ctx.logger.warn("Reolink Hub initial connect failed — will retry", { meta: { error: err instanceof Error ? err.message : String(err) } });
236665
236693
  });
236694
+ this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
236695
+ if (event.data.parentDeviceId !== this.id) return;
236696
+ (async () => {
236697
+ const byChannel = await this.loadAdoptedChildrenByChannel();
236698
+ for (const [ch, did] of byChannel) this.channelToDeviceId.set(ch, did);
236699
+ })().catch(() => {});
236700
+ }));
236666
236701
  this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceUnregistered }, (event) => {
236667
236702
  const data = event.data;
236668
236703
  if (data.parentDeviceId !== this.id) return;
@@ -236675,6 +236710,9 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236675
236710
  }));
236676
236711
  }
236677
236712
  eventUnsubscribers = [];
236713
+ /** Per-channel throttle for the unrouted-event warning (see
236714
+ * `routeSimpleEvent`) — a chatty unmapped channel must not flood. */
236715
+ unroutedChannelLoggedAt = /* @__PURE__ */ new Map();
236678
236716
  async removeDevice() {
236679
236717
  this.shutdownRequested = true;
236680
236718
  if (this.reconnectTimer) {
@@ -237231,7 +237269,18 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
237231
237269
  const channel = ev?.channel;
237232
237270
  if (typeof channel !== "number") return;
237233
237271
  const deviceId = this.channelToDeviceId.get(channel);
237234
- if (deviceId === void 0) return;
237272
+ if (deviceId === void 0) {
237273
+ const last = this.unroutedChannelLoggedAt.get(channel) ?? 0;
237274
+ const now = Date.now();
237275
+ if (now - last > 6e4) {
237276
+ this.unroutedChannelLoggedAt.set(channel, now);
237277
+ this.ctx.logger.warn("Reolink Hub: simpleEvent for an unmapped channel dropped", { meta: {
237278
+ channel,
237279
+ type: ev?.type ?? "unknown"
237280
+ } });
237281
+ }
237282
+ return;
237283
+ }
237235
237284
  this.ctx.devices.getAll().then((all) => all.find((d) => d.id === deviceId)).then((child) => {
237236
237285
  if (!(child instanceof ReolinkCamera)) return;
237237
237286
  child.handleSimpleEvent(ev);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.2.32",
3
+ "version": "1.2.34",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",