@apocaliss92/scrypted-reolink-native 0.1.25 → 0.1.26

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/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apocaliss92/scrypted-reolink-native",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "Use any reolink camera with Scrypted, even older/unsupported models without HTTP protocol support",
5
5
  "author": "@apocaliss92",
6
6
  "license": "Apache",
@@ -248,11 +248,10 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
248
248
  username: config.username,
249
249
  password: config.password,
250
250
  uid: config.uid,
251
- logger: logger as Console,
251
+ logger,
252
252
  debugOptions: config.debugOptions,
253
253
  },
254
254
  transport: config.transport,
255
- logger: logger as Console,
256
255
  });
257
256
 
258
257
  await api.login();
@@ -374,18 +374,12 @@ export class ReolinkNativeBatteryCamera extends CommonCameraMixin {
374
374
  }
375
375
  }
376
376
 
377
- async withBaichuanRetry<T>(fn: () => Promise<T>): Promise<T> {
378
- return await fn();
379
- }
380
-
381
377
  protected async withBaichuanClient<T>(fn: (api: ReolinkBaichuanApi) => Promise<T>): Promise<T> {
382
378
  const client = await this.ensureClient();
383
379
  return fn(client);
384
380
  }
385
381
 
386
382
  async createStreamClient(): Promise<ReolinkBaichuanApi> {
387
- // Reuse the main Baichuan client connection instead of creating a new one
388
- // This ensures we use a single session for everything (general + streams)
389
383
  return await this.ensureClient();
390
384
  }
391
385
  }
package/src/camera.ts CHANGED
@@ -31,8 +31,8 @@ export class ReolinkNativeCamera extends CommonCameraMixin {
31
31
 
32
32
 
33
33
  constructor(
34
- nativeId: string,
35
- public plugin: ReolinkNativePlugin,
34
+ nativeId: string,
35
+ public plugin: ReolinkNativePlugin,
36
36
  nvrDevice?: ReolinkNativeNvrDevice,
37
37
  multiFocalDevice?: ReolinkNativeMultiFocalDevice
38
38
  ) {
@@ -67,27 +67,6 @@ export class ReolinkNativeCamera extends CommonCameraMixin {
67
67
  }
68
68
  }
69
69
 
70
- async withBaichuanRetry<T>(fn: () => Promise<T>): Promise<T> {
71
- try {
72
- return await fn();
73
- }
74
- catch (e) {
75
- if (!this.isRecoverableBaichuanError(e)) {
76
- throw e;
77
- }
78
-
79
- // Reset client and clear cache on recoverable error
80
- await this.resetBaichuanClient(e);
81
-
82
- // Important: callers must re-acquire the client inside fn.
83
- try {
84
- return await fn();
85
- } catch (retryError) {
86
- throw retryError;
87
- }
88
- }
89
- }
90
-
91
70
 
92
71
  async init() {
93
72
  this.startPeriodicTasks();
@@ -97,6 +76,7 @@ export class ReolinkNativeCamera extends CommonCameraMixin {
97
76
 
98
77
  async createStreamClient(): Promise<ReolinkBaichuanApi> {
99
78
  const { ipAddress, username, password } = this.storageSettings.values;
79
+ const logger = this.getBaichuanLogger();
100
80
 
101
81
  const debugOptions = this.getBaichuanDebugOptions();
102
82
  const api = await createBaichuanApi(
@@ -105,11 +85,10 @@ export class ReolinkNativeCamera extends CommonCameraMixin {
105
85
  host: ipAddress,
106
86
  username: username,
107
87
  password: password,
108
- logger: this.console,
88
+ logger,
109
89
  debugOptions
110
90
  },
111
91
  transport: 'tcp',
112
- logger: this.console,
113
92
  },
114
93
  );
115
94
  await api.login();
package/src/common.ts CHANGED
@@ -1,14 +1,15 @@
1
1
  import type { DeviceCapabilities, PtzCommand, PtzPreset, ReolinkBaichuanApi, ReolinkSimpleEvent, ReolinkSupportedStream, StreamSamplingSelection } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
2
2
  import sdk, { BinarySensor, Brightness, Camera, Device, DeviceProvider, Intercom, MediaObject, MediaStreamUrl, ObjectDetectionTypes, ObjectDetector, ObjectsDetected, OnOff, PanTiltZoom, PanTiltZoomCommand, RequestMediaStreamOptions, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface, ScryptedMimeTypes, Setting, Settings, SettingValue, VideoCamera, VideoTextOverlay, VideoTextOverlays } from "@scrypted/sdk";
3
3
  import { StorageSettings } from "@scrypted/sdk/storage-settings";
4
+ import path from 'path';
4
5
  import type { UrlMediaStreamOptions } from "../../scrypted/plugins/rtsp/src/rtsp";
5
6
  import { BaseBaichuanClass, type BaichuanConnectionCallbacks, type BaichuanConnectionConfig } from "./baichuan-base";
6
7
  import { normalizeUid, type BaichuanTransport } from "./connect";
7
8
  import { convertDebugLogsToApiOptions, DebugLogDisplayNames, DebugLogOption, getApiRelevantDebugLogs, getDebugLogChoices } from "./debug-options";
8
9
  import { ReolinkBaichuanIntercom } from "./intercom";
9
10
  import ReolinkNativePlugin from "./main";
10
- import { ReolinkNativeNvrDevice } from "./nvr";
11
11
  import { ReolinkNativeMultiFocalDevice } from "./multiFocal";
12
+ import { ReolinkNativeNvrDevice } from "./nvr";
12
13
  import { ReolinkPtzPresets } from "./presets";
13
14
  import {
14
15
  createRfc4571MediaObjectFromStreamManager,
@@ -17,10 +18,9 @@ import {
17
18
  selectStreamOption,
18
19
  StreamManager
19
20
  } from "./stream-utils";
20
- import { getDeviceInterfaces, updateDeviceInfo } from "./utils";
21
- import path from 'path';
21
+ import { floodlightSuffix, getDeviceInterfaces, pirSuffix, sirenSuffix, updateDeviceInfo } from "./utils";
22
22
 
23
- export type CameraType = 'battery' | 'regular';
23
+ export type CameraType = 'battery' | 'regular' | 'multi-focal' | 'multi-focal-battery';
24
24
 
25
25
  export interface CommonCameraMixinOptions {
26
26
  type: CameraType;
@@ -197,6 +197,12 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
197
197
  await this.credentialsChanged();
198
198
  }
199
199
  },
200
+ debugEvents: {
201
+ title: 'Debug Events',
202
+ type: 'boolean',
203
+ immediate: true,
204
+ hide: true,
205
+ },
200
206
  username: {
201
207
  type: 'string',
202
208
  title: 'Username',
@@ -220,7 +226,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
220
226
  json: true,
221
227
  hide: true,
222
228
  },
223
- operationChannels: {
229
+ multifocalInfo: {
224
230
  json: true,
225
231
  hide: true,
226
232
  },
@@ -515,7 +521,6 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
515
521
  // Abstract init method that subclasses must implement
516
522
  abstract init(): Promise<void>;
517
523
 
518
- abstract withBaichuanRetry<T>(fn: () => Promise<T>): Promise<T>;
519
524
  protected withBaichuanClient?<T>(fn: (api: ReolinkBaichuanApi) => Promise<T>): Promise<T>;
520
525
  motionTimeout?: NodeJS.Timeout;
521
526
  doorbellBinaryTimeout?: NodeJS.Timeout;
@@ -532,13 +537,15 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
532
537
  public options: CommonCameraMixinOptions
533
538
  ) {
534
539
  super(nativeId);
535
- this.protocol = !options.nvrDevice && !options.multiFocalDevice && options.type === 'battery' ? 'udp' : 'tcp';
536
540
 
537
541
  // Store NVR device reference if provided
538
542
  this.nvrDevice = options.nvrDevice;
539
543
  this.multiFocalDevice = options.multiFocalDevice;
540
544
  this.thisDevice = sdk.systemManager.getDeviceById<Settings>(this.id);
541
545
 
546
+ const isBattery = options.type === 'battery' || options.type === 'multi-focal-battery';
547
+ this.protocol = isBattery ? 'udp' : 'tcp';
548
+
542
549
  setTimeout(async () => {
543
550
  await this.parentInit();
544
551
  }, 2000);
@@ -601,7 +608,31 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
601
608
  return this.name || 'Camera';
602
609
  }
603
610
 
604
- private async runDiagnostics(): Promise<void> {
611
+ async withBaichuanRetry<T>(fn: () => Promise<T>): Promise<T> {
612
+ if (this.protocol === 'udp') {
613
+ return await fn();
614
+ } else {
615
+ try {
616
+ return await fn();
617
+ } catch (e) {
618
+ if (!this.isRecoverableBaichuanError(e)) {
619
+ throw e;
620
+ }
621
+
622
+ // Reset client and clear cache on recoverable error
623
+ await this.resetBaichuanClient(e);
624
+
625
+ // Important: callers must re-acquire the client inside fn.
626
+ try {
627
+ return await fn();
628
+ } catch (retryError) {
629
+ throw retryError;
630
+ }
631
+ }
632
+ }
633
+ }
634
+
635
+ async runDiagnostics(): Promise<void> {
605
636
  const logger = this.getBaichuanLogger();
606
637
  const outputPath = this.storageSettings.values.diagnosticsOutputPath || process.env.SCRYPTED_PLUGIN_VOLUME || "";
607
638
 
@@ -654,8 +685,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
654
685
  }
655
686
 
656
687
  public getAbilities(): DeviceCapabilities {
657
- if (this.options.multiFocalDevice) {
658
- return this.options.multiFocalDevice.getInterfaces(this.storageSettings.values.rtspChannel).capabilities;
688
+ if (this.multiFocalDevice) {
689
+ return this.multiFocalDevice.getInterfaces(this.storageSettings.values.rtspChannel).capabilities;
659
690
  } else {
660
691
  return this.storageSettings.values.capabilities;
661
692
  }
@@ -690,9 +721,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
690
721
 
691
722
  // Event subscription methods
692
723
  unsubscribedToEvents(): void {
693
- // Use base class unsubscribe
694
724
  this.unsubscribeFromEvents().catch(() => {
695
- // ignore
696
725
  });
697
726
 
698
727
  if (this.motionDetected) {
@@ -1024,20 +1053,17 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1024
1053
  this.binaryState = false;
1025
1054
  }
1026
1055
 
1027
- // Report devices (siren, floodlight, PIR)
1028
1056
  async reportDevices(): Promise<void> {
1029
- if (!this.nativeId || !this.name) {
1030
- return;
1031
- }
1057
+ const abilities = this.getAbilities();
1032
1058
 
1033
- const { hasSiren, hasFloodlight, hasPir } = this.getAbilities();
1059
+ const { hasSiren, hasFloodlight, hasPir } = abilities;
1034
1060
 
1035
1061
  const devices: Device[] = [];
1036
1062
 
1037
1063
  if (hasSiren) {
1038
- const sirenNativeId = `${this.nativeId}-siren`;
1064
+ const sirenNativeId = `${this.nativeId}${sirenSuffix}`;
1039
1065
  devices.push({
1040
- providerNativeId: this.plugin?.nativeId,
1066
+ providerNativeId: this.nativeId,
1041
1067
  name: `${this.name} Siren`,
1042
1068
  nativeId: sirenNativeId,
1043
1069
  info: {
@@ -1049,9 +1075,9 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1049
1075
  }
1050
1076
 
1051
1077
  if (hasFloodlight) {
1052
- const floodlightNativeId = `${this.nativeId}-floodlight`;
1078
+ const floodlightNativeId = `${this.nativeId}${floodlightSuffix}`;
1053
1079
  devices.push({
1054
- providerNativeId: this.plugin?.nativeId,
1080
+ providerNativeId: this.nativeId,
1055
1081
  name: `${this.name} Floodlight`,
1056
1082
  nativeId: floodlightNativeId,
1057
1083
  info: {
@@ -1063,9 +1089,9 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1063
1089
  }
1064
1090
 
1065
1091
  if (hasPir) {
1066
- const pirNativeId = `${this.nativeId}-pir`;
1092
+ const pirNativeId = `${this.nativeId}${pirSuffix}`;
1067
1093
  devices.push({
1068
- providerNativeId: this.plugin?.nativeId,
1094
+ providerNativeId: this.nativeId,
1069
1095
  name: `${this.name} PIR`,
1070
1096
  nativeId: pirNativeId,
1071
1097
  info: {
@@ -1116,8 +1142,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1116
1142
  async updateDeviceInfo(): Promise<void> {
1117
1143
  const logger = this.getBaichuanLogger();
1118
1144
 
1119
- if (this.options.multiFocalDevice) {
1120
- this.info = this.options.multiFocalDevice.info;
1145
+ if (this.multiFocalDevice) {
1146
+ this.info = this.multiFocalDevice.info;
1121
1147
  return;
1122
1148
  }
1123
1149
 
@@ -1130,9 +1156,9 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1130
1156
  device: this,
1131
1157
  ipAddress,
1132
1158
  deviceData,
1159
+ logger,
1133
1160
  });
1134
1161
 
1135
- logger.log(`Device info updated: ${JSON.stringify(deviceData)}`);
1136
1162
  } catch (e) {
1137
1163
  logger.warn('Failed to fetch device info', e);
1138
1164
  }
@@ -1140,24 +1166,24 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1140
1166
 
1141
1167
  // Device provider methods
1142
1168
  async getDevice(nativeId: string): Promise<any> {
1143
- if (nativeId.endsWith('-siren')) {
1169
+ if (nativeId.endsWith(sirenSuffix)) {
1144
1170
  this.siren ||= new ReolinkCameraSiren(this, nativeId);
1145
1171
  return this.siren;
1146
- } else if (nativeId.endsWith('-floodlight')) {
1172
+ } else if (nativeId.endsWith(floodlightSuffix)) {
1147
1173
  this.floodlight ||= new ReolinkCameraFloodlight(this, nativeId);
1148
1174
  return this.floodlight;
1149
- } else if (nativeId.endsWith('-pir')) {
1175
+ } else if (nativeId.endsWith(pirSuffix)) {
1150
1176
  this.pirSensor ||= new ReolinkCameraPirSensor(this, nativeId);
1151
1177
  return this.pirSensor;
1152
1178
  }
1153
1179
  }
1154
1180
 
1155
1181
  async releaseDevice(id: string, nativeId: string): Promise<void> {
1156
- if (nativeId.endsWith('-siren')) {
1182
+ if (nativeId.endsWith(sirenSuffix)) {
1157
1183
  this.siren = undefined;
1158
- } else if (nativeId.endsWith('-floodlight')) {
1184
+ } else if (nativeId.endsWith(floodlightSuffix)) {
1159
1185
  this.floodlight = undefined;
1160
- } else if (nativeId.endsWith('-pir')) {
1186
+ } else if (nativeId.endsWith(pirSuffix)) {
1161
1187
  this.pirSensor = undefined;
1162
1188
  }
1163
1189
  }
@@ -1357,7 +1383,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1357
1383
  }
1358
1384
 
1359
1385
  if (streams.length) {
1360
- logger.log('Fetched video stream options', { streams });
1386
+ logger.log('Fetched video stream options', streams.map((s) => s.name).join(', '));
1387
+ logger.debug(JSON.stringify(streams));
1361
1388
  this.cachedVideoStreamOptions = streams;
1362
1389
  return streams;
1363
1390
  }
@@ -1451,41 +1478,41 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1451
1478
  const channel = this.storageSettings.values.rtspChannel;
1452
1479
 
1453
1480
  try {
1454
- if (this.options.multiFocalDevice) {
1455
- // do nothing for now
1456
- } else {
1457
- const { capabilities, abilities, support, presets, objects } = await this.withBaichuanRetry(async () => {
1458
- const api = await this.ensureClient();
1459
- return await api.getDeviceCapabilities(channel);
1481
+ const { capabilities, abilities, support, presets, objects } = await this.withBaichuanRetry(async () => {
1482
+ const api = await this.ensureClient();
1483
+ return await api.getDeviceCapabilities(channel);
1484
+ });
1485
+ this.classes = objects;
1486
+ this.presets = presets;
1487
+ this.ptzPresets.setCachedPtzPresets(presets);
1488
+
1489
+ try {
1490
+ const { interfaces, type } = getDeviceInterfaces({
1491
+ capabilities,
1492
+ logger: this.console,
1460
1493
  });
1461
- this.classes = objects;
1462
- this.presets = presets;
1463
- this.ptzPresets.setCachedPtzPresets(presets);
1464
1494
 
1465
- try {
1466
- const { interfaces, type } = getDeviceInterfaces({
1467
- capabilities,
1468
- logger: this.console,
1469
- });
1470
-
1471
- const device: Device = {
1472
- nativeId: this.nativeId,
1473
- providerNativeId: this.nvrDevice?.nativeId ?? this.plugin?.nativeId,
1474
- name: this.name,
1475
- interfaces,
1476
- type,
1477
- info: this.info,
1478
- };
1479
-
1480
- logger.log(`Updating device interfaces: ${JSON.stringify(device)}`);
1481
-
1482
- await sdk.deviceManager.onDeviceDiscovered(device);
1483
- } catch (e) {
1484
- logger.error('Failed to update device interfaces', e);
1485
- }
1495
+ const device: Device = {
1496
+ nativeId: this.nativeId,
1497
+ providerNativeId: this.nvrDevice?.nativeId ??
1498
+ this.multiFocalDevice?.nativeId ??
1499
+ this.plugin?.nativeId,
1500
+ name: this.name,
1501
+ interfaces,
1502
+ type,
1503
+ info: this.info,
1504
+ };
1505
+
1506
+ await sdk.deviceManager.onDeviceDiscovered(device);
1486
1507
 
1487
- logger.log(`Refreshed device capabilities: ${JSON.stringify({ capabilities, abilities, support, presets })}`);
1508
+ logger.log(`Device interfaces updated`);
1509
+ logger.debug(`${JSON.stringify(device)}`);
1510
+ } catch (e) {
1511
+ logger.error('Failed to update device interfaces', e);
1488
1512
  }
1513
+
1514
+ logger.log(`Refreshed device capabilities: ${JSON.stringify(capabilities)}`);
1515
+ logger.debug(`Refreshed device capabilities: ${JSON.stringify({ abilities, support, presets, objects })}`);
1489
1516
  }
1490
1517
  catch (e) {
1491
1518
  logger.error('Failed to refresh abilities', e);
@@ -1505,65 +1532,28 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1505
1532
  logger.warn('Failed to update device info during init', e);
1506
1533
  }
1507
1534
 
1508
- try {
1509
- await this.refreshDeviceState();
1510
- }
1511
- catch (e) {
1512
- logger.warn('Failed to connect/refresh during init', e);
1513
- }
1514
-
1515
- try {
1516
- await this.reportDevices();
1517
- }
1518
- catch (e) {
1519
- logger.warn('Failed to report devices during init', e);
1520
- }
1521
-
1522
- const { hasIntercom, hasPtz } = this.getAbilities();
1523
-
1524
- if (hasIntercom) {
1525
- this.intercom = new ReolinkBaichuanIntercom(this);
1526
- }
1527
-
1528
- if (hasPtz) {
1529
- const choices = (this.presets || []).map((preset: any) => preset.id + '=' + preset.name);
1530
-
1531
- this.storageSettings.settings.presets.choices = choices;
1532
- this.storageSettings.settings.ptzSelectedPreset.choices = choices;
1533
-
1534
- this.storageSettings.settings.presets.hide = false;
1535
- this.storageSettings.settings.ptzMoveDurationMs.hide = false;
1536
- this.storageSettings.settings.ptzZoomStep.hide = false;
1537
- this.storageSettings.settings.ptzCreatePreset.hide = false;
1538
- this.storageSettings.settings.ptzSelectedPreset.hide = false;
1539
- this.storageSettings.settings.ptzUpdateSelectedPreset.hide = false;
1540
- this.storageSettings.settings.ptzDeleteSelectedPreset.hide = false;
1541
-
1542
- this.updatePtzCaps();
1535
+ if (!this.multiFocalDevice) {
1536
+ try {
1537
+ await this.refreshDeviceState();
1538
+ await this.reportDevices();
1539
+ }
1540
+ catch (e) {
1541
+ logger.warn('Failed to connect/refresh during init', e);
1542
+ }
1543
1543
  }
1544
1544
 
1545
- const isBattery = this.options.type === 'battery';
1546
1545
  const { username, password } = this.storageSettings.values;
1546
+ const isCamera = this.options.type === 'regular' || this.options.type === 'battery';
1547
+ const isBatteryCamera = this.options.type === 'battery';
1548
+ const isBatteryMultiFocal = this.options.type === 'multi-focal-battery';
1549
+ const isBattery = isBatteryCamera || isBatteryMultiFocal;
1547
1550
 
1548
- this.streamManager = new StreamManager({
1549
- createStreamClient: () => this.createStreamClient(),
1550
- getLogger: () => logger as Console,
1551
- credentials: {
1552
- username,
1553
- password
1554
- },
1555
- // For battery cameras, we use a shared connection
1556
- sharedConnection: isBattery,
1557
- });
1558
-
1559
-
1560
- // this.storageSettings.settings.snapshotCacheMinutes.hide = !isBattery;
1561
1551
  this.storageSettings.settings.uid.hide = !isBattery;
1562
1552
  this.storageSettings.settings.batteryUpdateIntervalMinutes.hide = !isBattery;
1563
1553
  this.storageSettings.settings.lowThresholdBatteryRecording.hide = !isBattery;
1564
1554
  this.storageSettings.settings.highThresholdBatteryRecording.hide = !isBattery;
1565
1555
 
1566
- if (isBattery && !this.storageSettings.values.mixinsSetup) {
1556
+ if (isBatteryCamera && !this.storageSettings.values.mixinsSetup) {
1567
1557
  try {
1568
1558
  const device = sdk.systemManager.getDeviceById<Settings>(this.id);
1569
1559
  if (device) {
@@ -1585,30 +1575,54 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
1585
1575
  logger.warn('Failed to subscribe to Baichuan events', e);
1586
1576
  }
1587
1577
 
1578
+ if (isCamera) {
1579
+ this.streamManager = new StreamManager({
1580
+ createStreamClient: () => this.createStreamClient(),
1581
+ getLogger: () => logger as Console,
1582
+ credentials: {
1583
+ username,
1584
+ password
1585
+ },
1586
+ sharedConnection: isBattery,
1587
+ });
1588
+
1589
+ const { hasIntercom, hasPtz } = this.getAbilities();
1588
1590
 
1589
- if (this.nvrDevice) {
1590
- this.storageSettings.settings.username.hide = true;
1591
- this.storageSettings.settings.password.hide = true;
1592
- this.storageSettings.settings.ipAddress.hide = true;
1593
- this.storageSettings.settings.uid.hide = true;
1591
+ if (hasIntercom) {
1592
+ this.intercom = new ReolinkBaichuanIntercom(this);
1593
+ }
1594
1594
 
1595
- this.storageSettings.settings.username.defaultValue = this.nvrDevice.storageSettings.values.username;
1596
- this.storageSettings.settings.password.defaultValue = this.nvrDevice.storageSettings.values.password;
1597
- this.storageSettings.settings.ipAddress.defaultValue = this.nvrDevice.storageSettings.values.ipAddress;
1595
+ if (hasPtz) {
1596
+ const choices = (this.presets || []).map((preset: any) => preset.id + '=' + preset.name);
1597
+
1598
+ this.storageSettings.settings.presets.choices = choices;
1599
+ this.storageSettings.settings.ptzSelectedPreset.choices = choices;
1600
+
1601
+ this.storageSettings.settings.presets.hide = false;
1602
+ this.storageSettings.settings.ptzMoveDurationMs.hide = false;
1603
+ this.storageSettings.settings.ptzZoomStep.hide = false;
1604
+ this.storageSettings.settings.ptzCreatePreset.hide = false;
1605
+ this.storageSettings.settings.ptzSelectedPreset.hide = false;
1606
+ this.storageSettings.settings.ptzUpdateSelectedPreset.hide = false;
1607
+ this.storageSettings.settings.ptzDeleteSelectedPreset.hide = false;
1608
+
1609
+ this.updatePtzCaps();
1610
+ }
1598
1611
  }
1599
1612
 
1600
- if (this.multiFocalDevice) {
1613
+ if (this.nvrDevice || this.multiFocalDevice) {
1601
1614
  this.storageSettings.settings.username.hide = true;
1602
1615
  this.storageSettings.settings.password.hide = true;
1603
1616
  this.storageSettings.settings.ipAddress.hide = true;
1604
1617
  this.storageSettings.settings.uid.hide = true;
1605
1618
 
1606
- this.storageSettings.settings.username.defaultValue = this.multiFocalDevice.storageSettings.values.username;
1607
- this.storageSettings.settings.password.defaultValue = this.multiFocalDevice.storageSettings.values.password;
1608
- this.storageSettings.settings.ipAddress.defaultValue = this.multiFocalDevice.storageSettings.values.ipAddress;
1619
+ this.storageSettings.settings.username.defaultValue = this.nvrDevice.storageSettings.values.username;
1620
+ this.storageSettings.settings.password.defaultValue = this.nvrDevice.storageSettings.values.password;
1621
+ this.storageSettings.settings.ipAddress.defaultValue = this.nvrDevice.storageSettings.values.ipAddress;
1609
1622
  }
1610
1623
 
1611
1624
  await this.init();
1625
+
1612
1626
  this.initComplete = true;
1613
1627
  }
1614
1628
  }
package/src/connect.ts CHANGED
@@ -19,16 +19,16 @@ export function normalizeUid(uid?: string): string | undefined {
19
19
  export async function createBaichuanApi(props: {
20
20
  inputs: BaichuanConnectInputs,
21
21
  transport: BaichuanTransport,
22
- logger: Console,
23
22
  }): Promise<ReolinkBaichuanApi> {
24
- const { inputs, transport, logger } = props;
23
+ const { inputs, transport } = props;
24
+ const { logger } = inputs;
25
25
  const { ReolinkBaichuanApi } = await import("@apocaliss92/reolink-baichuan-js");
26
26
 
27
27
  const base: BaichuanClientOptions = {
28
28
  host: inputs.host,
29
29
  username: inputs.username,
30
30
  password: inputs.password,
31
- logger: logger, // Use the logger passed to createBaichuanApi, not inputs.logger
31
+ logger,
32
32
  debugOptions: inputs.debugOptions ?? {}
33
33
  };
34
34