@mega-yfue/eufy-sdk 0.5.0-beta.77 → 0.5.0-beta.78

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.
@@ -734,6 +734,10 @@ export interface LiveStreamConsumer extends LiveStreamHandle {
734
734
  export interface AbortableCall {
735
735
  signal?: AbortSignal;
736
736
  }
737
+ /** Select an optical view; absence selects view 0. */
738
+ export interface MediaSourceSelection {
739
+ view?: number;
740
+ }
737
741
  export interface SharedSourceHints {
738
742
  /**
739
743
  * Power source, a runtime device fact (`"battery"` incl. solar, or `"wired"`) — never a device-family
@@ -759,6 +763,8 @@ export interface SharedSourceHints {
759
763
  * because an unbound model has no live client (hence `?.` at the call site).
760
764
  */
761
765
  export interface MediaProvider {
766
+ /** Optical view identifiers supported by this bound media route. */
767
+ views?: readonly number[];
762
768
  /**
763
769
  * Return the latest validated push thumbnail retained in memory. This passive operation performs no
764
770
  * network, storage, P2P, live-media, or transcoding work at call time. It rejects with
@@ -785,7 +791,7 @@ export interface MediaProvider {
785
791
  timeoutMs?: number;
786
792
  collectMs?: number;
787
793
  skipKeyframes?: number;
788
- } & SharedSourceHints & AbortableCall): Promise<{
794
+ } & SharedSourceHints & AbortableCall & MediaSourceSelection): Promise<{
789
795
  jpeg: Buffer;
790
796
  width: number;
791
797
  height: number;
@@ -817,7 +823,7 @@ export interface MediaProvider {
817
823
  * stream?.stop(); // detach this consumer
818
824
  * ```
819
825
  */
820
- live(opts?: SharedSourceHints & AbortableCall & Record<string, unknown>): Promise<LiveStreamConsumer>;
826
+ live(opts?: SharedSourceHints & AbortableCall & MediaSourceSelection & Record<string, unknown>): Promise<LiveStreamConsumer>;
821
827
  /**
822
828
  * Record `seconds` of video → an mp4/h264 buffer.
823
829
  *
@@ -843,7 +849,7 @@ export interface MediaProvider {
843
849
  openReadable?(opts?: {
844
850
  objectMode?: boolean;
845
851
  arrivalTime?: boolean;
846
- } & SharedSourceHints & AbortableCall): Promise<import("node:stream").Readable>;
852
+ } & SharedSourceHints & AbortableCall & MediaSourceSelection): Promise<import("node:stream").Readable>;
847
853
  /**
848
854
  * Continuously record the live feed as fragmented-MP4 (CMAF). The caller-owned
849
855
  * {@link FragmentRecordingHandle} yields an init segment then keyframe-bounded media fragments,
package/dist/index.js CHANGED
@@ -3656,6 +3656,7 @@ var isHomeBase = (ctx) => has(HOMEBASE_TYPES, ctx.deviceType);
3656
3656
  var isStation9000 = (ctx) => ctx.deviceType === DeviceType.STATION_9000;
3657
3657
  var isNvrT8N00 = (ctx) => ctx.model === "T8N00";
3658
3658
  var isNvrMediaCamera = (ctx) => ctx.model === "T8E00" || ctx.model === "T8P00";
3659
+ var isNvrDualLensCamera = (ctx) => ctx.model === "T8E00";
3659
3660
  var isDoorbellT8223 = (ctx) => ctx.model === "T8223";
3660
3661
  var isAiotVacuum = (ctx) => ctx.category !== "eufy_home_tuya";
3661
3662
  var isTuyaVacuum = (ctx) => ctx.category === "eufy_home_tuya";
@@ -5591,6 +5592,11 @@ function refuseWhenDisabled(ctx, read) {
5591
5592
  if (CAMERA_MEMBERS.enabled.readReflectsWrite(ctx) && read("enabled")?.value === false)
5592
5593
  throw new CameraDisabledError(ctx.name ?? ctx.serial);
5593
5594
  }
5595
+ function refuseUnsupportedView(media, opts) {
5596
+ const view = opts?.view ?? 0;
5597
+ if (!(media.views ?? [0]).includes(view))
5598
+ throw new RangeError(`camera: unsupported optical view ${view}`);
5599
+ }
5594
5600
  var CAMERA_MEMBERS = {
5595
5601
  /** Reads the latest cached report and exposes no setter. */
5596
5602
  wifiRssi: {
@@ -5861,6 +5867,7 @@ var CAMERA_MEMBERS = {
5861
5867
  description: "Camera status LED. Video doorbells report the same state under their button-ring LED parameter.",
5862
5868
  write: (v, ctx) => statusLedCommand(asBool(v), ctx)
5863
5869
  },
5870
+ views: provided("media", (m) => m.views && (() => m.views), "Optical view identifiers supported by this camera's media route."),
5864
5871
  /**
5865
5872
  * Media is not a property: it returns DATA rather than moving state, its options are richer than a
5866
5873
  * value, and it exists only on a device bound to a provider. Each is declared once, with its
@@ -5881,10 +5888,12 @@ var CAMERA_MEMBERS = {
5881
5888
  snapshotStored: provided("media", (m, { ctx }) => isCameraCodec(ctx) && m.snapshotStored && (() => m.snapshotStored()), "Latest validated push thumbnail retained in memory.", ["snapshot"]),
5882
5889
  snapshotLive: provided("media", (m, { ctx, read }) => isCameraCodec(ctx) && (async (opts) => {
5883
5890
  refuseWhenDisabled(ctx, read);
5891
+ refuseUnsupportedView(m, opts);
5884
5892
  return m.snapshotLive({ powered: poweredOf(ctx), ...opts });
5885
5893
  }), "Fresh still decoded from a short live burst."),
5886
5894
  live: provided("media", (m, { ctx, read }) => async (opts) => {
5887
5895
  refuseWhenDisabled(ctx, read);
5896
+ refuseUnsupportedView(m, opts);
5888
5897
  return m.live({ powered: poweredOf(ctx), ...opts });
5889
5898
  }, "Open a managed live stream."),
5890
5899
  record: provided("media", (m, { ctx, read }) => isCameraCodec(ctx) && (async (...args) => {
@@ -5893,6 +5902,7 @@ var CAMERA_MEMBERS = {
5893
5902
  }), "Record N seconds \u2192 an mp4/h264 buffer."),
5894
5903
  openReadable: provided("media", (m, { ctx, read }) => isCameraCodec(ctx) && m.openReadable && (async (opts) => {
5895
5904
  refuseWhenDisabled(ctx, read);
5905
+ refuseUnsupportedView(m, opts);
5896
5906
  return m.openReadable({ powered: poweredOf(ctx), ...opts });
5897
5907
  }), "Open a node:stream Readable of the live feed."),
5898
5908
  recordFragments: provided("media", (m, { ctx, read }) => isCameraCodec(ctx) && m.recordFragments && ((opts) => {
@@ -23501,7 +23511,7 @@ var RtcLiveStream = class extends EventEmitter7 {
23501
23511
  audio_chn: nvr ? this.opts.channel : -1,
23502
23512
  streamtype: 2,
23503
23513
  stitch_mode: 1,
23504
- chn_list: [{ index: 0, chn: this.opts.channel, sensor: 0, isUps: 0, isClicked: true }],
23514
+ chn_list: [{ index: 0, chn: this.opts.channel, sensor: this.opts.view ?? 0, isUps: 0, isClicked: true }],
23505
23515
  pip_cord: { x1: 0, y1: 0, x2: 0, y2: 0 },
23506
23516
  station_video_type: 6,
23507
23517
  play_id: 1
@@ -24843,22 +24853,28 @@ var RtcCommandRouter = class {
24843
24853
  return this.callMediaProviderFor(route, route.stationSn, (session) => new RtcCallLiveStream(session, this.deps.logger), `record is not available for ${route.stationSn} over a portal call`, 45e3);
24844
24854
  }
24845
24855
  /** NVR media uses a dedicated call per camera, independent of station commands. */
24846
- nvrMediaProviderFor(route) {
24847
- return this.callMediaProviderFor(route, route.cameraSn, (session, accountId) => new RtcLiveStream({
24848
- session,
24849
- seg: new SegmentCounter(),
24850
- stationSn: route.stationSn,
24851
- channel: route.channel,
24852
- accountId,
24853
- logger: this.deps.logger,
24854
- nvr: true
24855
- }), "record is not available over an NVR portal call");
24856
+ nvrMediaProviderFor(route, views) {
24857
+ return {
24858
+ ...this.callMediaProviderFor(route, route.cameraSn, (session, accountId, view) => new RtcLiveStream({
24859
+ session,
24860
+ seg: new SegmentCounter(),
24861
+ stationSn: route.stationSn,
24862
+ channel: route.channel,
24863
+ accountId,
24864
+ logger: this.deps.logger,
24865
+ nvr: true,
24866
+ view
24867
+ }), "record is not available over an NVR portal call"),
24868
+ views
24869
+ };
24856
24870
  }
24857
24871
  /** Share a dedicated-call source and its consumer operations under one route key. */
24858
24872
  callMediaProviderFor(route, key, makeStream, recordError, warmTimeoutMs) {
24859
24873
  const sourceFor = (opts = {}) => {
24860
24874
  opts.signal?.throwIfAborted();
24861
- let source = this.callSources.get(key);
24875
+ const view = opts.view ?? 0;
24876
+ const sourceKey = view === 0 ? key : `${key}:view:${view}`;
24877
+ let source = this.callSources.get(sourceKey);
24862
24878
  if (!source) {
24863
24879
  source = new SharedLiveSource({
24864
24880
  makeStream: () => {
@@ -24866,15 +24882,15 @@ var RtcCommandRouter = class {
24866
24882
  if (!identity)
24867
24883
  throw new Error(`rtc: not logged in, cannot call ${route.stationSn}`);
24868
24884
  const accountId = route.adminUserId || identity.userId;
24869
- return makeStream(this.newSession(route, accountId, identity), accountId);
24885
+ return makeStream(this.newSession(route, accountId, identity), accountId, view);
24870
24886
  },
24871
- label: key,
24887
+ label: sourceKey,
24872
24888
  logger: this.deps.logger,
24873
24889
  powered: opts.powered,
24874
24890
  preBufferSeconds: opts.preBufferSeconds,
24875
24891
  warmTimeoutMs
24876
24892
  });
24877
- this.callSources.set(key, source);
24893
+ this.callSources.set(sourceKey, source);
24878
24894
  }
24879
24895
  return source;
24880
24896
  };
@@ -29447,7 +29463,7 @@ var EufyMega = class extends EventEmitter14 {
29447
29463
  try {
29448
29464
  return await media.snapshotLive(opts);
29449
29465
  } catch (error) {
29450
- if (!(error instanceof LiveSnapshotUnavailableError))
29466
+ if (!(error instanceof LiveSnapshotUnavailableError) || (opts?.view ?? 0) !== 0)
29451
29467
  throw error;
29452
29468
  const retained = await retainedStill().catch(() => void 0);
29453
29469
  const geometry = retained && jpegGeometry(retained);
@@ -29536,7 +29552,7 @@ var EufyMega = class extends EventEmitter14 {
29536
29552
  if (route.signalingMode === "call") {
29537
29553
  if (!camera || !isNvrMediaCamera(camera))
29538
29554
  return void 0;
29539
- return this.rtc.nvrMediaProviderFor({ ...route, cameraSn: sn, channel });
29555
+ return this.rtc.nvrMediaProviderFor({ ...route, cameraSn: sn, channel }, isNvrDualLensCamera(camera) ? [0, 1] : [0]);
29540
29556
  }
29541
29557
  return this.rtc.mediaProviderFor({ ...route, cameraSn: sn, channel });
29542
29558
  }