@camstack/addon-provider-reolink 1.2.34 → 1.2.36

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,8 +230953,6 @@ 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;
230958
230956
  try {
230959
230957
  await Promise.race([(async () => {
230960
230958
  await api.wakeUp(channel, {
@@ -230962,20 +230960,19 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230962
230960
  attempts: 2
230963
230961
  });
230964
230962
  await sleep$1(1500);
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))]);
230963
+ })(), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("wakeForStream timeout")), timeoutMs))]);
230970
230964
  const CONFIRM_TIMEOUT_MS = 1e4;
230971
230965
  const CONFIRM_POLL_MS = 1e3;
230972
230966
  const confirmDeadline = Date.now() + CONFIRM_TIMEOUT_MS;
230973
230967
  const parent = await this.getParentHub();
230974
230968
  let confirmed = false;
230969
+ let observedAwake = false;
230975
230970
  while (Date.now() < confirmDeadline) {
230976
230971
  if (parent !== null) try {
230977
- if ((await (await parent.getApi()).getNvrChannelsSummary({ channels: [this.getChannel()] })).devices.find((d) => d.channel === this.getChannel())?.sleeping === false) {
230972
+ const entry = (await (await parent.getApi()).getNvrChannelsSummary({ channels: [this.getChannel()] })).devices.find((d) => d.channel === this.getChannel());
230973
+ if (entry !== void 0 && entry.online !== false) {
230978
230974
  confirmed = true;
230975
+ observedAwake = entry.sleeping === false;
230979
230976
  break;
230980
230977
  }
230981
230978
  } catch {}
@@ -230988,13 +230985,14 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230988
230985
  }
230989
230986
  if (state === "awake") {
230990
230987
  confirmed = true;
230988
+ observedAwake = true;
230991
230989
  break;
230992
230990
  }
230993
230991
  }
230994
230992
  await sleep$1(CONFIRM_POLL_MS);
230995
230993
  }
230996
230994
  const confirmSource = parent !== null ? "hub-summary" : "sleep-poll";
230997
- if (confirmed && this.commitSleepState(false, confirmSource)) {
230995
+ if (observedAwake && this.commitSleepState(false, confirmSource)) {
230998
230996
  this.ctx.logger.info("battery wakeForStream: wake confirmed — driving wake transition", {
230999
230997
  tags: { deviceId: this.id },
231000
230998
  meta: {
@@ -233632,8 +233630,50 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
233632
233630
  * drained the battery and, by bursting the socket, was itself what starved
233633
233631
  * GetEnc.
233634
233632
  */
233633
+ /**
233634
+ * The catalog a SLEEPING hub child can still offer: the parent's RTSP
233635
+ * relay entries, derived from the parent's config (host + credentials +
233636
+ * the Reolink Hub's per-channel relay scheme), with zero contact to the
233637
+ * child. This is the difference between a sleeping hub child being
233638
+ * playable and being a permanent "sleeping" tile: the broker gets a
233639
+ * dialable URL, the relay dial wakes the camera over RF (verified live
233640
+ * against the Home Hub, 2026-08-15), and the catalog never depended on
233641
+ * the child being awake to build.
233642
+ */
233643
+ async buildHubRelayDescriptors() {
233644
+ if (!this.isHubChild()) return [];
233645
+ const parentId = this.parentDeviceId;
233646
+ if (parentId === null) return [];
233647
+ const cfg = (await this.ctx.devices.getAll()).find((d) => d.id === parentId)?.config.values;
233648
+ const host = cfg?.host;
233649
+ if (typeof host !== "string" || host.length === 0) return [];
233650
+ const user = encodeURIComponent(String(cfg?.username ?? ""));
233651
+ const password = encodeURIComponent(String(cfg?.password ?? ""));
233652
+ const port = typeof cfg?.port === "number" && cfg.port > 0 ? cfg.port : 554;
233653
+ const channel = this.getChannel();
233654
+ const channelCount = this.channelCount();
233655
+ const relayChannel = channel + 1;
233656
+ const out = [];
233657
+ for (const profile of ["main", "sub"]) out.push({
233658
+ camStreamId: buildCamStreamId("rtsp", channel, profile, channelCount),
233659
+ kind: "pull-rtsp",
233660
+ label: `Hub relay (${profile})`,
233661
+ url: `rtsp://${user}:${password}@${host}:${port}/h264Preview_0${relayChannel}_${profile}`,
233662
+ autoEligible: true,
233663
+ deviceFeatures: [...this.features]
233664
+ });
233665
+ return out;
233666
+ }
233635
233667
  async buildStreamCatalogUncached() {
233636
233668
  if (this.isBattery && this.sleeping) {
233669
+ const relay = await this.buildHubRelayDescriptors().catch(() => []);
233670
+ if (relay.length > 0) {
233671
+ this.ctx.logger.debug("buildStreamCatalog: sleeping hub child — serving hub relay entries", {
233672
+ tags: { deviceId: this.id },
233673
+ meta: { entries: relay.map((r) => r.camStreamId) }
233674
+ });
233675
+ return relay;
233676
+ }
233637
233677
  this.ctx.logger.debug("buildStreamCatalog: battery cam sleeping, no cache — deferring to wake", { tags: { deviceId: this.id } });
233638
233678
  return [];
233639
233679
  }
package/dist/addon.mjs CHANGED
@@ -230933,8 +230933,6 @@ 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;
230938
230936
  try {
230939
230937
  await Promise.race([(async () => {
230940
230938
  await api.wakeUp(channel, {
@@ -230942,20 +230940,19 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230942
230940
  attempts: 2
230943
230941
  });
230944
230942
  await sleep$1(1500);
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))]);
230943
+ })(), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("wakeForStream timeout")), timeoutMs))]);
230950
230944
  const CONFIRM_TIMEOUT_MS = 1e4;
230951
230945
  const CONFIRM_POLL_MS = 1e3;
230952
230946
  const confirmDeadline = Date.now() + CONFIRM_TIMEOUT_MS;
230953
230947
  const parent = await this.getParentHub();
230954
230948
  let confirmed = false;
230949
+ let observedAwake = false;
230955
230950
  while (Date.now() < confirmDeadline) {
230956
230951
  if (parent !== null) try {
230957
- if ((await (await parent.getApi()).getNvrChannelsSummary({ channels: [this.getChannel()] })).devices.find((d) => d.channel === this.getChannel())?.sleeping === false) {
230952
+ const entry = (await (await parent.getApi()).getNvrChannelsSummary({ channels: [this.getChannel()] })).devices.find((d) => d.channel === this.getChannel());
230953
+ if (entry !== void 0 && entry.online !== false) {
230958
230954
  confirmed = true;
230955
+ observedAwake = entry.sleeping === false;
230959
230956
  break;
230960
230957
  }
230961
230958
  } catch {}
@@ -230968,13 +230965,14 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
230968
230965
  }
230969
230966
  if (state === "awake") {
230970
230967
  confirmed = true;
230968
+ observedAwake = true;
230971
230969
  break;
230972
230970
  }
230973
230971
  }
230974
230972
  await sleep$1(CONFIRM_POLL_MS);
230975
230973
  }
230976
230974
  const confirmSource = parent !== null ? "hub-summary" : "sleep-poll";
230977
- if (confirmed && this.commitSleepState(false, confirmSource)) {
230975
+ if (observedAwake && this.commitSleepState(false, confirmSource)) {
230978
230976
  this.ctx.logger.info("battery wakeForStream: wake confirmed — driving wake transition", {
230979
230977
  tags: { deviceId: this.id },
230980
230978
  meta: {
@@ -233612,8 +233610,50 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
233612
233610
  * drained the battery and, by bursting the socket, was itself what starved
233613
233611
  * GetEnc.
233614
233612
  */
233613
+ /**
233614
+ * The catalog a SLEEPING hub child can still offer: the parent's RTSP
233615
+ * relay entries, derived from the parent's config (host + credentials +
233616
+ * the Reolink Hub's per-channel relay scheme), with zero contact to the
233617
+ * child. This is the difference between a sleeping hub child being
233618
+ * playable and being a permanent "sleeping" tile: the broker gets a
233619
+ * dialable URL, the relay dial wakes the camera over RF (verified live
233620
+ * against the Home Hub, 2026-08-15), and the catalog never depended on
233621
+ * the child being awake to build.
233622
+ */
233623
+ async buildHubRelayDescriptors() {
233624
+ if (!this.isHubChild()) return [];
233625
+ const parentId = this.parentDeviceId;
233626
+ if (parentId === null) return [];
233627
+ const cfg = (await this.ctx.devices.getAll()).find((d) => d.id === parentId)?.config.values;
233628
+ const host = cfg?.host;
233629
+ if (typeof host !== "string" || host.length === 0) return [];
233630
+ const user = encodeURIComponent(String(cfg?.username ?? ""));
233631
+ const password = encodeURIComponent(String(cfg?.password ?? ""));
233632
+ const port = typeof cfg?.port === "number" && cfg.port > 0 ? cfg.port : 554;
233633
+ const channel = this.getChannel();
233634
+ const channelCount = this.channelCount();
233635
+ const relayChannel = channel + 1;
233636
+ const out = [];
233637
+ for (const profile of ["main", "sub"]) out.push({
233638
+ camStreamId: buildCamStreamId("rtsp", channel, profile, channelCount),
233639
+ kind: "pull-rtsp",
233640
+ label: `Hub relay (${profile})`,
233641
+ url: `rtsp://${user}:${password}@${host}:${port}/h264Preview_0${relayChannel}_${profile}`,
233642
+ autoEligible: true,
233643
+ deviceFeatures: [...this.features]
233644
+ });
233645
+ return out;
233646
+ }
233615
233647
  async buildStreamCatalogUncached() {
233616
233648
  if (this.isBattery && this.sleeping) {
233649
+ const relay = await this.buildHubRelayDescriptors().catch(() => []);
233650
+ if (relay.length > 0) {
233651
+ this.ctx.logger.debug("buildStreamCatalog: sleeping hub child — serving hub relay entries", {
233652
+ tags: { deviceId: this.id },
233653
+ meta: { entries: relay.map((r) => r.camStreamId) }
233654
+ });
233655
+ return relay;
233656
+ }
233617
233657
  this.ctx.logger.debug("buildStreamCatalog: battery cam sleeping, no cache — deferring to wake", { tags: { deviceId: this.id } });
233618
233658
  return [];
233619
233659
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.2.34",
3
+ "version": "1.2.36",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",