@camstack/addon-provider-reolink 1.2.33 → 1.2.35

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
@@ -233626,8 +233626,50 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
233626
233626
  * drained the battery and, by bursting the socket, was itself what starved
233627
233627
  * GetEnc.
233628
233628
  */
233629
+ /**
233630
+ * The catalog a SLEEPING hub child can still offer: the parent's RTSP
233631
+ * relay entries, derived from the parent's config (host + credentials +
233632
+ * the Reolink Hub's per-channel relay scheme), with zero contact to the
233633
+ * child. This is the difference between a sleeping hub child being
233634
+ * playable and being a permanent "sleeping" tile: the broker gets a
233635
+ * dialable URL, the relay dial wakes the camera over RF (verified live
233636
+ * against the Home Hub, 2026-08-15), and the catalog never depended on
233637
+ * the child being awake to build.
233638
+ */
233639
+ async buildHubRelayDescriptors() {
233640
+ if (!this.isHubChild()) return [];
233641
+ const parentId = this.parentDeviceId;
233642
+ if (parentId === null) return [];
233643
+ const cfg = (await this.ctx.devices.getAll()).find((d) => d.id === parentId)?.config.values;
233644
+ const host = cfg?.host;
233645
+ if (typeof host !== "string" || host.length === 0) return [];
233646
+ const user = encodeURIComponent(String(cfg?.username ?? ""));
233647
+ const password = encodeURIComponent(String(cfg?.password ?? ""));
233648
+ const port = typeof cfg?.port === "number" && cfg.port > 0 ? cfg.port : 554;
233649
+ const channel = this.getChannel();
233650
+ const channelCount = this.channelCount();
233651
+ const relayChannel = channel + 1;
233652
+ const out = [];
233653
+ for (const profile of ["main", "sub"]) out.push({
233654
+ camStreamId: buildCamStreamId("rtsp", channel, profile, channelCount),
233655
+ kind: "pull-rtsp",
233656
+ label: `Hub relay (${profile})`,
233657
+ url: `rtsp://${user}:${password}@${host}:${port}/h264Preview_0${relayChannel}_${profile}`,
233658
+ autoEligible: true,
233659
+ deviceFeatures: [...this.features]
233660
+ });
233661
+ return out;
233662
+ }
233629
233663
  async buildStreamCatalogUncached() {
233630
233664
  if (this.isBattery && this.sleeping) {
233665
+ const relay = await this.buildHubRelayDescriptors().catch(() => []);
233666
+ if (relay.length > 0) {
233667
+ this.ctx.logger.debug("buildStreamCatalog: sleeping hub child — serving hub relay entries", {
233668
+ tags: { deviceId: this.id },
233669
+ meta: { entries: relay.map((r) => r.camStreamId) }
233670
+ });
233671
+ return relay;
233672
+ }
233631
233673
  this.ctx.logger.debug("buildStreamCatalog: battery cam sleeping, no cache — deferring to wake", { tags: { deviceId: this.id } });
233632
233674
  return [];
233633
233675
  }
@@ -236705,6 +236747,13 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236705
236747
  this.ensureApi().catch((err) => {
236706
236748
  this.ctx.logger.warn("Reolink Hub initial connect failed — will retry", { meta: { error: err instanceof Error ? err.message : String(err) } });
236707
236749
  });
236750
+ this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
236751
+ if (event.data.parentDeviceId !== this.id) return;
236752
+ (async () => {
236753
+ const byChannel = await this.loadAdoptedChildrenByChannel();
236754
+ for (const [ch, did] of byChannel) this.channelToDeviceId.set(ch, did);
236755
+ })().catch(() => {});
236756
+ }));
236708
236757
  this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceUnregistered }, (event) => {
236709
236758
  const data = event.data;
236710
236759
  if (data.parentDeviceId !== this.id) return;
@@ -236717,6 +236766,9 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236717
236766
  }));
236718
236767
  }
236719
236768
  eventUnsubscribers = [];
236769
+ /** Per-channel throttle for the unrouted-event warning (see
236770
+ * `routeSimpleEvent`) — a chatty unmapped channel must not flood. */
236771
+ unroutedChannelLoggedAt = /* @__PURE__ */ new Map();
236720
236772
  async removeDevice() {
236721
236773
  this.shutdownRequested = true;
236722
236774
  if (this.reconnectTimer) {
@@ -237273,7 +237325,18 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
237273
237325
  const channel = ev?.channel;
237274
237326
  if (typeof channel !== "number") return;
237275
237327
  const deviceId = this.channelToDeviceId.get(channel);
237276
- if (deviceId === void 0) return;
237328
+ if (deviceId === void 0) {
237329
+ const last = this.unroutedChannelLoggedAt.get(channel) ?? 0;
237330
+ const now = Date.now();
237331
+ if (now - last > 6e4) {
237332
+ this.unroutedChannelLoggedAt.set(channel, now);
237333
+ this.ctx.logger.warn("Reolink Hub: simpleEvent for an unmapped channel dropped", { meta: {
237334
+ channel,
237335
+ type: ev?.type ?? "unknown"
237336
+ } });
237337
+ }
237338
+ return;
237339
+ }
237277
237340
  this.ctx.devices.getAll().then((all) => all.find((d) => d.id === deviceId)).then((child) => {
237278
237341
  if (!(child instanceof ReolinkCamera)) return;
237279
237342
  child.handleSimpleEvent(ev);
package/dist/addon.mjs CHANGED
@@ -233606,8 +233606,50 @@ var ReolinkCamera = class ReolinkCamera extends BaseDevice {
233606
233606
  * drained the battery and, by bursting the socket, was itself what starved
233607
233607
  * GetEnc.
233608
233608
  */
233609
+ /**
233610
+ * The catalog a SLEEPING hub child can still offer: the parent's RTSP
233611
+ * relay entries, derived from the parent's config (host + credentials +
233612
+ * the Reolink Hub's per-channel relay scheme), with zero contact to the
233613
+ * child. This is the difference between a sleeping hub child being
233614
+ * playable and being a permanent "sleeping" tile: the broker gets a
233615
+ * dialable URL, the relay dial wakes the camera over RF (verified live
233616
+ * against the Home Hub, 2026-08-15), and the catalog never depended on
233617
+ * the child being awake to build.
233618
+ */
233619
+ async buildHubRelayDescriptors() {
233620
+ if (!this.isHubChild()) return [];
233621
+ const parentId = this.parentDeviceId;
233622
+ if (parentId === null) return [];
233623
+ const cfg = (await this.ctx.devices.getAll()).find((d) => d.id === parentId)?.config.values;
233624
+ const host = cfg?.host;
233625
+ if (typeof host !== "string" || host.length === 0) return [];
233626
+ const user = encodeURIComponent(String(cfg?.username ?? ""));
233627
+ const password = encodeURIComponent(String(cfg?.password ?? ""));
233628
+ const port = typeof cfg?.port === "number" && cfg.port > 0 ? cfg.port : 554;
233629
+ const channel = this.getChannel();
233630
+ const channelCount = this.channelCount();
233631
+ const relayChannel = channel + 1;
233632
+ const out = [];
233633
+ for (const profile of ["main", "sub"]) out.push({
233634
+ camStreamId: buildCamStreamId("rtsp", channel, profile, channelCount),
233635
+ kind: "pull-rtsp",
233636
+ label: `Hub relay (${profile})`,
233637
+ url: `rtsp://${user}:${password}@${host}:${port}/h264Preview_0${relayChannel}_${profile}`,
233638
+ autoEligible: true,
233639
+ deviceFeatures: [...this.features]
233640
+ });
233641
+ return out;
233642
+ }
233609
233643
  async buildStreamCatalogUncached() {
233610
233644
  if (this.isBattery && this.sleeping) {
233645
+ const relay = await this.buildHubRelayDescriptors().catch(() => []);
233646
+ if (relay.length > 0) {
233647
+ this.ctx.logger.debug("buildStreamCatalog: sleeping hub child — serving hub relay entries", {
233648
+ tags: { deviceId: this.id },
233649
+ meta: { entries: relay.map((r) => r.camStreamId) }
233650
+ });
233651
+ return relay;
233652
+ }
233611
233653
  this.ctx.logger.debug("buildStreamCatalog: battery cam sleeping, no cache — deferring to wake", { tags: { deviceId: this.id } });
233612
233654
  return [];
233613
233655
  }
@@ -236685,6 +236727,13 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236685
236727
  this.ensureApi().catch((err) => {
236686
236728
  this.ctx.logger.warn("Reolink Hub initial connect failed — will retry", { meta: { error: err instanceof Error ? err.message : String(err) } });
236687
236729
  });
236730
+ this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceRegistered }, (event) => {
236731
+ if (event.data.parentDeviceId !== this.id) return;
236732
+ (async () => {
236733
+ const byChannel = await this.loadAdoptedChildrenByChannel();
236734
+ for (const [ch, did] of byChannel) this.channelToDeviceId.set(ch, did);
236735
+ })().catch(() => {});
236736
+ }));
236688
236737
  this.eventUnsubscribers.push(this.ctx.eventBus.subscribe({ category: EventCategory.DeviceUnregistered }, (event) => {
236689
236738
  const data = event.data;
236690
236739
  if (data.parentDeviceId !== this.id) return;
@@ -236697,6 +236746,9 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
236697
236746
  }));
236698
236747
  }
236699
236748
  eventUnsubscribers = [];
236749
+ /** Per-channel throttle for the unrouted-event warning (see
236750
+ * `routeSimpleEvent`) — a chatty unmapped channel must not flood. */
236751
+ unroutedChannelLoggedAt = /* @__PURE__ */ new Map();
236700
236752
  async removeDevice() {
236701
236753
  this.shutdownRequested = true;
236702
236754
  if (this.reconnectTimer) {
@@ -237253,7 +237305,18 @@ var ReolinkHub = class ReolinkHub extends BaseDevice {
237253
237305
  const channel = ev?.channel;
237254
237306
  if (typeof channel !== "number") return;
237255
237307
  const deviceId = this.channelToDeviceId.get(channel);
237256
- if (deviceId === void 0) return;
237308
+ if (deviceId === void 0) {
237309
+ const last = this.unroutedChannelLoggedAt.get(channel) ?? 0;
237310
+ const now = Date.now();
237311
+ if (now - last > 6e4) {
237312
+ this.unroutedChannelLoggedAt.set(channel, now);
237313
+ this.ctx.logger.warn("Reolink Hub: simpleEvent for an unmapped channel dropped", { meta: {
237314
+ channel,
237315
+ type: ev?.type ?? "unknown"
237316
+ } });
237317
+ }
237318
+ return;
237319
+ }
237257
237320
  this.ctx.devices.getAll().then((all) => all.find((d) => d.id === deviceId)).then((child) => {
237258
237321
  if (!(child instanceof ReolinkCamera)) return;
237259
237322
  child.handleSimpleEvent(ev);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-reolink",
3
- "version": "1.2.33",
3
+ "version": "1.2.35",
4
4
  "description": "Reolink camera device provider addon for CamStack — native Baichuan protocol",
5
5
  "keywords": [
6
6
  "camstack",