@apocaliss92/scrypted-reolink-native 0.1.42 → 0.2.0

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/src/nvr.ts CHANGED
@@ -1,13 +1,16 @@
1
- import type { EventsResponse, ReolinkBaichuanApi, ReolinkBaichuanDeviceSummary, ReolinkSimpleEvent } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
1
+ import type { EventsResponse, NativeVideoStreamVariant, ReolinkBaichuanApi, ReolinkBaichuanDeviceSummary, ReolinkSimpleEvent, StreamProfile } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
2
2
  import sdk, { AdoptDevice, Device, DeviceDiscovery, DeviceProvider, DiscoveredDevice, Reboot, ScryptedDeviceType, ScryptedInterface, Setting, Settings, SettingValue } from "@scrypted/sdk";
3
3
  import { StorageSettings } from "@scrypted/sdk/storage-settings";
4
4
  import { BaseBaichuanClass, type BaichuanConnectionCallbacks, type BaichuanConnectionConfig } from "./baichuan-base";
5
5
  import { ReolinkNativeCamera } from "./camera";
6
6
  import { ReolinkNativeBatteryCamera } from "./camera-battery";
7
- import { normalizeUid } from "./connect";
7
+ import { CommonCameraMixin } from "./common";
8
8
  import { convertDebugLogsToApiOptions, getApiRelevantDebugLogs, getDebugLogChoices } from "./debug-options";
9
9
  import ReolinkNativePlugin from "./main";
10
- import { getDeviceInterfaces, updateDeviceInfo } from "./utils";
10
+ import { ReolinkNativeMultiFocalDevice } from "./multiFocal";
11
+ import { batteryCameraSuffix, batteryMultifocalSuffix, cameraSuffix, getDeviceInterfaces, multifocalSuffix, updateDeviceInfo } from "./utils";
12
+ import { createBaichuanApi } from "./connect";
13
+ import { parseStreamProfileFromId } from "./stream-utils";
11
14
 
12
15
  export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Settings, DeviceDiscovery, DeviceProvider, Reboot {
13
16
  storageSettings = new StorageSettings(this, {
@@ -16,17 +19,17 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
16
19
  type: 'boolean',
17
20
  immediate: true,
18
21
  },
19
- eventSource: {
20
- title: 'Event Source',
21
- description: 'Select the source for camera events: Native (Baichuan) or CGI (HTTP polling)',
22
- type: 'string',
23
- choices: ['Native', 'CGI'],
24
- defaultValue: 'Native',
25
- immediate: true,
26
- onPut: async () => {
27
- await this.reinitEventSubscriptions();
28
- }
29
- },
22
+ // eventSource: {
23
+ // title: 'Event Source',
24
+ // description: 'Select the source for camera events: Native (Baichuan) or CGI (HTTP polling)',
25
+ // type: 'string',
26
+ // choices: ['Native', 'CGI'],
27
+ // defaultValue: 'Native',
28
+ // immediate: true,
29
+ // onPut: async () => {
30
+ // await this.reinitEventSubscriptions();
31
+ // }
32
+ // },
30
33
  ipAddress: {
31
34
  title: 'IP address',
32
35
  type: 'string',
@@ -89,7 +92,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
89
92
  // Trigger reconnection
90
93
  await this.ensureBaichuanClient();
91
94
  } catch (e) {
92
- logger.warn('Failed to reset client after debug logs change', e);
95
+ logger.warn('Failed to reset client after debug logs change', e?.message || String(e));
93
96
  }
94
97
  }, 2000);
95
98
  }
@@ -103,10 +106,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
103
106
  rtspChannel: number;
104
107
  deviceData: ReolinkBaichuanDeviceSummary;
105
108
  }>();
106
- lastNvrInfoCheck: number | undefined;
107
- lastErrorsCheck: number | undefined;
108
- lastDevicesStatusCheck: number | undefined;
109
- cameraNativeMap = new Map<string, ReolinkNativeCamera | ReolinkNativeBatteryCamera>();
109
+ cameraNativeMap = new Map<string, CommonCameraMixin>();
110
110
  private channelToNativeIdMap = new Map<number, string>();
111
111
  private discoverDevicesPromise: Promise<DiscoveredDevice[]> | undefined;
112
112
  processing = false;
@@ -114,7 +114,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
114
114
  private debugLogsResetTimeout: NodeJS.Timeout | undefined;
115
115
 
116
116
  constructor(nativeId: string, plugin: ReolinkNativePlugin) {
117
- super(nativeId);
117
+ super(nativeId, "tcp");
118
118
  this.plugin = plugin;
119
119
 
120
120
  this.scheduleInit();
@@ -142,6 +142,23 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
142
142
  };
143
143
  }
144
144
 
145
+ protected getStreamClientInputs(): BaichuanConnectionConfig {
146
+ const { ipAddress, username, password } = this.storageSettings.values;
147
+ if (!ipAddress || !username || !password) {
148
+ throw new Error('Missing NVR credentials');
149
+ }
150
+
151
+ const debugOptions = this.getBaichuanDebugOptions();
152
+
153
+ return {
154
+ host: ipAddress,
155
+ username,
156
+ password,
157
+ transport: 'tcp',
158
+ debugOptions,
159
+ };
160
+ }
161
+
145
162
  getBaichuanDebugOptions(): any | undefined {
146
163
  const socketDebugLogs = this.storageSettings.values.socketApiDebugLogs || [];
147
164
  return convertDebugLogsToApiOptions(socketDebugLogs);
@@ -153,14 +170,14 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
153
170
  await this.reinit();
154
171
  },
155
172
  onSimpleEvent: (ev) => this.forwardNativeEvent(ev),
156
- getEventSubscriptionEnabled: () => {
157
- const eventSource = this.storageSettings.values.eventSource || 'Native';
158
- return eventSource === 'Native';
159
- },
173
+ getEventSubscriptionEnabled: () => true,
174
+ // getEventSubscriptionEnabled: () => {
175
+ // const eventSource = this.storageSettings.values.eventSource || 'Native';
176
+ // return eventSource === 'Native';
177
+ // },
160
178
  };
161
179
  }
162
180
 
163
-
164
181
  protected isDebugEnabled(): boolean {
165
182
  return this.storageSettings.values.debugLogs || false;
166
183
  }
@@ -198,18 +215,24 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
198
215
  }, isReinit ? 500 : 2000);
199
216
  }
200
217
 
218
+ /**
219
+ * Forward events received from Baichuan to the appropriate child device (Camera or MultiFocal).
220
+ * This ensures that only NVR (root device) subscribes to events, and events are forwarded down the hierarchy:
221
+ * - NVR → MultiFocal → Camera
222
+ * - NVR → Camera (directly)
223
+ */
201
224
  private forwardNativeEvent(ev: ReolinkSimpleEvent): void {
202
225
  const logger = this.getBaichuanLogger();
203
226
 
204
- const eventSource = this.storageSettings.values.eventSource || 'Native';
205
- if (eventSource !== 'Native') {
206
- return;
207
- }
227
+ // const eventSource = this.storageSettings.values.eventSource || 'Native';
228
+ // if (eventSource !== 'Native') {
229
+ // return;
230
+ // }
208
231
 
209
232
  try {
210
233
  logger.debug(`Baichuan event: ${JSON.stringify(ev)}`);
211
234
 
212
- // Find camera for this channel
235
+ // Find device (camera or multifocal) for this channel
213
236
  const channel = ev?.channel;
214
237
  if (channel === undefined) {
215
238
  logger.error('Event has no channel, ignoring');
@@ -217,10 +240,16 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
217
240
  }
218
241
 
219
242
  const nativeId = this.channelToNativeIdMap.get(channel);
220
- const targetCamera = nativeId ? this.cameraNativeMap.get(nativeId) : undefined;
243
+ const targetDevice = nativeId ? this.cameraNativeMap.get(nativeId) : undefined;
221
244
 
222
- if (!targetCamera) {
223
- logger.debug(`No camera found for channel ${channel} (nativeId: ${nativeId}), ignoring event`);
245
+ if (!targetDevice) {
246
+ logger.debug(`No device found for channel ${channel} (nativeId: ${nativeId}), ignoring event`);
247
+ return;
248
+ }
249
+
250
+ // If target is a MultiFocal device, forward the event to it (it will forward to its camera children)
251
+ if (targetDevice instanceof ReolinkNativeMultiFocalDevice) {
252
+ targetDevice.forwardNativeEvent(ev);
224
253
  return;
225
254
  }
226
255
 
@@ -237,10 +266,10 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
237
266
  case 'doorbell':
238
267
  // Handle doorbell if camera supports it
239
268
  try {
240
- targetCamera.handleDoorbellEvent();
269
+ targetDevice.handleDoorbellEvent();
241
270
  }
242
271
  catch (e) {
243
- logger.warn(`Error handling doorbell event for camera channel ${channel}`, e);
272
+ logger.warn(`Error handling doorbell event for camera channel ${channel}`, e?.message || String(e));
244
273
  }
245
274
  motion = true;
246
275
  break;
@@ -267,41 +296,44 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
267
296
  }
268
297
 
269
298
  if (isSleepingEvent) {
270
- (targetCamera as ReolinkNativeBatteryCamera).updateSleepingState({
299
+ targetDevice.updateSleepingState({
271
300
  reason: 'NVR',
272
301
  state: ev.type === 'sleeping' ? 'sleeping' : 'awake',
273
302
  }).catch(() => { });
274
- } if (isSleepingEvent) {
275
- (targetCamera as ReolinkNativeBatteryCamera).updateOnlineState(
303
+ } else if (isOnlineEvent) {
304
+ (targetDevice as ReolinkNativeBatteryCamera).updateOnlineState(
276
305
  ev.type === 'online' ? true : false
277
306
  ).catch(() => { });
278
307
  } else {
279
308
  // Process events on the target camera
280
- targetCamera.processEvents({ motion, objects }).catch((e) => {
281
- logger.warn(`Error processing events for camera channel ${channel}`, e);
309
+ targetDevice.processEvents({ motion, objects }).catch((e) => {
310
+ logger.warn(`Error processing events for camera channel ${channel}`, e?.message || String(e));
282
311
  });
283
312
  }
284
313
  }
285
314
  catch (e) {
286
- logger.warn('Error in NVR Native event forwarder', e);
315
+ logger.warn('Error in NVR Native event forwarder', e?.message || String(e));
287
316
  }
288
317
  }
289
318
 
290
319
  async ensureBaichuanClient(): Promise<ReolinkBaichuanApi> {
291
- // Use base class implementation
292
320
  return await super.ensureBaichuanClient();
293
321
  }
294
322
 
295
- async subscribeToAllEvents(): Promise<void> {
296
- const eventSource = this.storageSettings.values.eventSource || 'Native';
297
-
298
- if (eventSource !== 'Native') {
299
- await this.unsubscribeFromAllEvents();
300
- } else {
301
- await super.subscribeToEvents();
302
- }
323
+ async ensureClient(): Promise<ReolinkBaichuanApi> {
324
+ return await this.ensureBaichuanClient();
303
325
  }
304
326
 
327
+ // async subscribeToAllEvents(): Promise<void> {
328
+ // const eventSource = this.storageSettings.values.eventSource || 'Native';
329
+
330
+ // if (eventSource !== 'Native') {
331
+ // await this.unsubscribeFromAllEvents();
332
+ // } else {
333
+ // await super.subscribeToEvents();
334
+ // }
335
+ // }
336
+
305
337
  private async runNvrDiagnostics(): Promise<void> {
306
338
  const logger = this.getBaichuanLogger();
307
339
  logger.log(`Starting NVR diagnostics...`);
@@ -313,7 +345,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
313
345
  logger: this.console,
314
346
  });
315
347
  } catch (e) {
316
- logger.error('Failed to run NVR diagnostics', e);
348
+ logger.error('Failed to run NVR diagnostics', e?.message || String(e));
317
349
  throw e;
318
350
  }
319
351
  }
@@ -326,104 +358,88 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
326
358
  /**
327
359
  * Reinitialize event subscriptions based on selected event source
328
360
  */
329
- private async reinitEventSubscriptions(): Promise<void> {
330
- const logger = this.getBaichuanLogger();
331
- const { eventSource } = this.storageSettings.values;
332
-
333
- // Unsubscribe from Native events if switching away
334
- if (eventSource !== 'Native') {
335
- await this.unsubscribeFromAllEvents();
336
- } else {
337
- this.subscribeToAllEvents().catch((e) => {
338
- logger.warn('Failed to subscribe to Native events', e);
339
- });
340
- }
341
-
342
- logger.log(`Event source set to: ${eventSource}`);
343
- }
361
+ // private async reinitEventSubscriptions(): Promise<void> {
362
+ // const logger = this.getBaichuanLogger();
363
+ // const { eventSource } = this.storageSettings.values;
364
+
365
+ // // Unsubscribe from Native events if switching away
366
+ // if (eventSource !== 'Native') {
367
+ // await this.unsubscribeFromAllEvents();
368
+ // } else {
369
+ // this.subscribeToAllEvents().catch((e) => {
370
+ // logger.warn('Failed to subscribe to Native events', e?.message || String(e));
371
+ // });
372
+ // }
373
+
374
+ // logger.log(`Event source set to: ${eventSource}`);
375
+ // }
344
376
 
345
377
  /**
346
378
  * Forward events from CGI source to cameras
347
379
  */
348
- private forwardCgiEvents(eventsRes: Record<number, EventsResponse>): void {
349
- const logger = this.getBaichuanLogger();
380
+ // private forwardCgiEvents(eventsRes: Record<number, EventsResponse>): void {
381
+ // const logger = this.getBaichuanLogger();
350
382
 
351
- logger.debug(`CGI Events call result: ${JSON.stringify(eventsRes)}`);
383
+ // logger.debug(`CGI Events call result: ${JSON.stringify(eventsRes)}`);
352
384
 
353
- // Use channel map for efficient lookup
354
- for (const [channel, nativeId] of this.channelToNativeIdMap.entries()) {
355
- const targetCamera = nativeId ? this.cameraNativeMap.get(nativeId) : undefined;
356
- const cameraEventsData = eventsRes[channel];
357
- if (cameraEventsData && targetCamera) {
358
- targetCamera.processEvents(cameraEventsData);
359
- }
360
- }
361
- }
385
+ // // Use channel map for efficient lookup
386
+ // for (const [channel, nativeId] of this.channelToNativeIdMap.entries()) {
387
+ // const targetCamera = nativeId ? this.cameraNativeMap.get(nativeId) : undefined;
388
+ // const cameraEventsData = eventsRes[channel];
389
+ // if (cameraEventsData && targetCamera) {
390
+ // targetCamera.processEvents(cameraEventsData);
391
+ // }
392
+ // }
393
+ // }
362
394
 
363
395
  async init() {
364
- const logger = this.getBaichuanLogger();
396
+ // const logger = this.getBaichuanLogger();
365
397
  await this.ensureBaichuanClient();
366
398
 
367
399
  await this.updateDeviceInfo();
368
-
369
- await this.reinitEventSubscriptions();
370
-
371
- setInterval(async () => {
372
- if (this.processing) {
373
- return;
374
- }
375
- this.processing = true;
376
- try {
377
- const now = Date.now();
378
-
379
- if (!this.lastErrorsCheck || (now - this.lastErrorsCheck > 60 * 1000)) {
380
- this.lastErrorsCheck = now;
381
- // Note: ReolinkCgiApi doesn't have checkErrors, skip for now
382
- }
383
-
384
- if (!this.lastNvrInfoCheck || now - this.lastNvrInfoCheck > 1000 * 60 * 5) {
385
- this.lastNvrInfoCheck = now;
386
- // const { nvrData } = await api.getNvrInfo();
387
- // const { devicesData, channelsResponse, response } = await api.getDevicesInfo();
388
- // logger.log(`NVR info data fetched`);
389
- // logger.debug(`${JSON.stringify({ nvrData, devicesData, channelsResponse, response })}`);
390
-
391
- await this.discoverDevices(true);
392
- }
393
-
394
- const api = await this.ensureBaichuanClient();
395
-
396
- const { eventSource } = this.storageSettings.values;
397
-
398
- if (eventSource === 'CGI') {
399
- const eventsRes = await api.getAllChannelsEvents();
400
- this.forwardCgiEvents(eventsRes.parsed);
401
-
402
- const { batteryInfoData, response } = await api.getAllChannelsBatteryInfo();
403
-
404
- logger.debug(`Battery info call result: ${JSON.stringify({ batteryInfoData, response })}`);
405
-
406
- this.cameraNativeMap.forEach((camera) => {
407
- if (camera) {
408
- const channel = camera.storageSettings.values.rtspChannel;
409
- const cameraBatteryData = batteryInfoData[channel];
410
- if (cameraBatteryData) {
411
- (camera as ReolinkNativeBatteryCamera).updateSleepingState({
412
- reason: 'NVR',
413
- state: cameraBatteryData.sleeping ? 'sleeping' : 'awake',
414
- idleMs: 0,
415
- lastRxAtMs: 0,
416
- }).catch(() => { });
417
- }
418
- }
419
- });
420
- }
421
- } catch (e) {
422
- logger.error('Error on events flow', e);
423
- } finally {
424
- this.processing = false;
425
- }
426
- }, 1000);
400
+ await this.subscribeToEvents();
401
+
402
+ // await this.reinitEventSubscriptions();
403
+
404
+ // setInterval(async () => {
405
+ // if (this.processing) {
406
+ // return;
407
+ // }
408
+ // this.processing = true;
409
+ // try {
410
+ // const api = await this.ensureBaichuanClient();
411
+
412
+ // const { eventSource } = this.storageSettings.values;
413
+
414
+ // if (eventSource === 'CGI') {
415
+ // const eventsRes = await api.getAllChannelsEvents();
416
+ // this.forwardCgiEvents(eventsRes.parsed);
417
+
418
+ // const { batteryInfoData, response } = await api.getAllChannelsBatteryInfo();
419
+
420
+ // logger.debug(`Battery info call result: ${JSON.stringify({ batteryInfoData, response })}`);
421
+
422
+ // this.cameraNativeMap.forEach((camera) => {
423
+ // if (camera) {
424
+ // const channel = camera.storageSettings.values.rtspChannel;
425
+ // const cameraBatteryData = batteryInfoData[channel];
426
+ // if (cameraBatteryData) {
427
+ // camera.updateSleepingState({
428
+ // reason: 'NVR',
429
+ // state: cameraBatteryData.sleeping ? 'sleeping' : 'awake',
430
+ // idleMs: 0,
431
+ // lastRxAtMs: 0,
432
+ // }).catch(() => { });
433
+ // }
434
+ // }
435
+ // });
436
+ // }
437
+ // } catch (e) {
438
+ // logger.error('Error on events flow', e?.message || String(e));
439
+ // } finally {
440
+ // this.processing = false;
441
+ // }
442
+ // }, 1000);
427
443
  }
428
444
 
429
445
  async updateDeviceInfo(): Promise<void> {
@@ -441,7 +457,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
441
457
  logger
442
458
  });
443
459
  } catch (e) {
444
- logger.warn('Failed to fetch device info', e);
460
+ logger.warn('Failed to fetch device info', e?.message || String(e));
445
461
  }
446
462
  }
447
463
 
@@ -458,27 +474,38 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
458
474
  this.cameraNativeMap.delete(nativeId);
459
475
  }
460
476
 
461
- async getDevice(nativeId: string): Promise<ReolinkNativeCamera | ReolinkNativeBatteryCamera> {
477
+ async getDevice(nativeId: string): Promise<CommonCameraMixin> {
462
478
  let device = this.cameraNativeMap.get(nativeId);
463
479
 
464
480
  if (!device) {
465
- if (nativeId.endsWith('-battery-cam')) {
481
+ if (nativeId.endsWith(batteryCameraSuffix)) {
466
482
  device = new ReolinkNativeBatteryCamera(nativeId, this.plugin, this);
483
+ } else if (nativeId.endsWith(batteryMultifocalSuffix)) {
484
+ device = new ReolinkNativeMultiFocalDevice(nativeId, this.plugin, "multi-focal-battery", this);
485
+ } else if (nativeId.endsWith(multifocalSuffix)) {
486
+ device = new ReolinkNativeMultiFocalDevice(nativeId, this.plugin, "multi-focal", this);
467
487
  } else {
468
488
  device = new ReolinkNativeCamera(nativeId, this.plugin, this);
469
489
  }
470
- this.cameraNativeMap.set(nativeId, device);
490
+
491
+ if (device) {
492
+ this.cameraNativeMap.set(nativeId, device);
493
+ }
471
494
  }
472
495
 
473
496
  return device;
474
497
  }
475
498
 
476
- buildNativeId(channel: number, serialNumber?: string, isBattery?: boolean): string {
477
- const suffix = isBattery ? '-battery-cam' : '-cam';
478
- if (serialNumber) {
479
- return `${this.nativeId}-ch${channel}-${serialNumber}${suffix}`;
480
- }
481
- return `${this.nativeId}-ch${channel}${suffix}`;
499
+ buildNativeId(props: {
500
+ identifier?: string, isBattery?: boolean, isMultifocal?: boolean
501
+ }): string {
502
+ const { identifier, isBattery, isMultifocal } = props;
503
+
504
+ const suffix = isBattery ?
505
+ (isMultifocal ? batteryMultifocalSuffix : batteryCameraSuffix) :
506
+ (isMultifocal ? multifocalSuffix : cameraSuffix)
507
+
508
+ return `${this.nativeId}-${identifier}${suffix}`;
482
509
  }
483
510
 
484
511
  getCameraInterfaces() {
@@ -495,10 +522,10 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
495
522
 
496
523
  async syncEntitiesFromRemote() {
497
524
  const logger = this.getBaichuanLogger();
525
+ // const { ipAddress } = this.storageSettings.values;
498
526
 
499
527
  const api = await this.ensureBaichuanClient();
500
- const { devices, channels } = await api.getDevicesInfo();
501
- logger.log(devices, channels);
528
+ const { devices, channels } = await api.getNvrChannelsSummary({ source: "cgi" });
502
529
 
503
530
  if (!channels.length) {
504
531
  logger.debug(`No channels found, ${JSON.stringify({ channels, devices })}`);
@@ -508,10 +535,16 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
508
535
  logger.log(`Sync entities from remote for ${channels.length} channels`);
509
536
 
510
537
  for (const deviceData of devices) {
511
- const { isBattery, serialNumber, name, model, isDoorbell, uid, channel } = deviceData
538
+ const { isBattery, serialNumber, name, model, isDoorbell, uid, channel, isMultifocal } = deviceData;
539
+ const identifier = uid || name || `channel-${channel}`;
540
+ // const identifier = uid || mac || (ip !== ipAddress ? ip : undefined) || name || randomBytes(4).toString('hex');
512
541
 
513
542
  try {
514
- const nativeId = this.buildNativeId(channel, uid, isBattery);
543
+ const nativeId = this.buildNativeId({
544
+ isBattery,
545
+ isMultifocal,
546
+ identifier,
547
+ });
515
548
  const interfaces = [ScryptedInterface.VideoCamera];
516
549
  if (isBattery) {
517
550
  interfaces.push(ScryptedInterface.Battery);
@@ -527,7 +560,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
527
560
  info: {
528
561
  manufacturer: 'Reolink',
529
562
  model,
530
- serialNumber: uid,
563
+ serialNumber,
531
564
  }
532
565
  };
533
566
 
@@ -538,7 +571,10 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
538
571
  if (
539
572
  allNativeIds.some(
540
573
  nid => nid.includes(uid) ||
541
- nid.includes(serialNumber) ||
574
+ nid.includes(`channel-${channel}`) ||
575
+ // nid.includes(mac) ||
576
+ // nid.includes(ip) ||
577
+ nid.includes(name) ||
542
578
  nid === nativeId)
543
579
  ) {
544
580
  continue;
@@ -601,29 +637,28 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
601
637
 
602
638
  await this.onDeviceEvent(ScryptedInterface.DeviceDiscovery, await this.discoverDevices());
603
639
 
604
- const isBattery = entry.device.interfaces.includes(ScryptedInterface.Battery);
605
640
  const { uid } = entry.deviceData;
606
641
 
607
642
  const { ReolinkBaichuanApi } = await import("@apocaliss92/reolink-baichuan-js");
608
643
  const transport = 'tcp';
609
- const normalizedUid = isBattery && uid ? normalizeUid(uid) : undefined;
610
644
  const baichuanApi = new ReolinkBaichuanApi({
611
645
  host: this.storageSettings.values.ipAddress,
612
646
  username: this.storageSettings.values.username,
613
647
  password: this.storageSettings.values.password,
614
648
  transport,
615
649
  channel: entry.rtspChannel,
616
- ...(normalizedUid ? { uid: normalizedUid } : {}),
650
+ uid,
617
651
  });
618
652
  await baichuanApi.login();
619
653
  const { capabilities, objects, presets } = await baichuanApi.getDeviceCapabilities(entry.rtspChannel);
620
654
  const { interfaces, type } = getDeviceInterfaces({
621
655
  capabilities,
622
- logger: this.console,
656
+ logger: this.getBaichuanLogger(),
623
657
  });
624
658
 
625
659
  const actualDevice: Device = {
626
660
  ...entry.device,
661
+ providerNativeId: this.nativeId,
627
662
  interfaces,
628
663
  type
629
664
  };
@@ -632,8 +667,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
632
667
 
633
668
  const device = await this.getDevice(adopt.nativeId);
634
669
  const logger = this.getBaichuanLogger();
635
- logger.log('Adopted device', device?.name);
636
- logger.log(JSON.stringify(entry));
670
+ logger.log('Adopted device', device?.name, JSON.stringify(actualDevice));
637
671
  const { username, password, ipAddress } = this.storageSettings.values;
638
672
 
639
673
  device.storageSettings.values.rtspChannel = entry.rtspChannel;