@apocaliss92/scrypted-reolink-native 0.1.14 → 0.1.16
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/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/common.ts +48 -28
- package/src/nvr.ts +18 -18
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/common.ts
CHANGED
|
@@ -263,6 +263,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
263
263
|
defaultValue: [],
|
|
264
264
|
choices: getDebugLogChoices(),
|
|
265
265
|
onPut: async (ov, value) => {
|
|
266
|
+
const logger = this.getBaichuanLogger();
|
|
266
267
|
const oldApiOptions = getApiRelevantDebugLogs(ov || []);
|
|
267
268
|
const newApiOptions = getApiRelevantDebugLogs(value || []);
|
|
268
269
|
|
|
@@ -288,7 +289,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
288
289
|
// Trigger reconnection
|
|
289
290
|
await this.ensureClient();
|
|
290
291
|
} catch (e) {
|
|
291
|
-
|
|
292
|
+
logger.warn('Failed to reset client after debug logs change', e);
|
|
292
293
|
}
|
|
293
294
|
}, 2000);
|
|
294
295
|
}
|
|
@@ -566,16 +567,21 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
566
567
|
onClose: async () => {
|
|
567
568
|
// Reset client state on close
|
|
568
569
|
// The base class already handles cleanup
|
|
569
|
-
//
|
|
570
|
-
//
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
570
|
+
// For battery cameras, don't auto-resubscribe after idle disconnects
|
|
571
|
+
// (idle disconnects are normal for battery cameras to save power)
|
|
572
|
+
// Events will be resubscribed when ensureClient() is called for actual operations
|
|
573
|
+
const isBattery = this.options.type === 'battery';
|
|
574
|
+
if (!isBattery) {
|
|
575
|
+
// For non-battery cameras, resubscribe to events after reconnection
|
|
576
|
+
setTimeout(async () => {
|
|
577
|
+
try {
|
|
578
|
+
await this.subscribeToEvents();
|
|
579
|
+
} catch (e) {
|
|
580
|
+
const logger = this.getBaichuanLogger();
|
|
581
|
+
logger.warn('Failed to resubscribe to events after reconnection', e);
|
|
582
|
+
}
|
|
583
|
+
}, 1000);
|
|
584
|
+
}
|
|
579
585
|
},
|
|
580
586
|
onSimpleEvent: this.onSimpleEvent,
|
|
581
587
|
getEventSubscriptionEnabled: () => this.isEventDispatchEnabled?.() ?? false,
|
|
@@ -686,6 +692,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
686
692
|
}
|
|
687
693
|
|
|
688
694
|
onSimpleEvent = (ev: ReolinkSimpleEvent) => {
|
|
695
|
+
const logger = this.getBaichuanLogger();
|
|
696
|
+
|
|
689
697
|
try {
|
|
690
698
|
const logger = this.getBaichuanLogger();
|
|
691
699
|
|
|
@@ -732,7 +740,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
732
740
|
});
|
|
733
741
|
}
|
|
734
742
|
catch (e) {
|
|
735
|
-
|
|
743
|
+
logger.warn('Error in onSimpleEvent handler', e);
|
|
736
744
|
}
|
|
737
745
|
}
|
|
738
746
|
|
|
@@ -827,6 +835,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
827
835
|
|
|
828
836
|
// PanTiltZoom interface implementation
|
|
829
837
|
async ptzCommand(command: PanTiltZoomCommand): Promise<void> {
|
|
838
|
+
const logger = this.getBaichuanLogger();
|
|
839
|
+
|
|
830
840
|
const client = await this.ensureClient();
|
|
831
841
|
if (!client) {
|
|
832
842
|
return;
|
|
@@ -839,13 +849,13 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
839
849
|
if (preset !== undefined && preset !== null) {
|
|
840
850
|
const presetId = Number(preset);
|
|
841
851
|
if (!Number.isFinite(presetId)) {
|
|
842
|
-
|
|
852
|
+
logger.warn(`Invalid PTZ preset id: ${preset}`);
|
|
843
853
|
return;
|
|
844
854
|
}
|
|
845
855
|
if (this.ptzPresets) {
|
|
846
856
|
await this.ptzPresets.moveToPreset(presetId);
|
|
847
857
|
} else {
|
|
848
|
-
|
|
858
|
+
logger.warn('PTZ presets not available');
|
|
849
859
|
}
|
|
850
860
|
return;
|
|
851
861
|
}
|
|
@@ -880,14 +890,14 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
880
890
|
|
|
881
891
|
const step = Number(this.storageSettings.values.ptzZoomStep);
|
|
882
892
|
if (!Number.isFinite(step) || step <= 0) {
|
|
883
|
-
|
|
893
|
+
logger.warn('Invalid PTZ zoom step, using default 0.1');
|
|
884
894
|
return;
|
|
885
895
|
}
|
|
886
896
|
|
|
887
897
|
// Get current zoom factor and apply step
|
|
888
898
|
const info = await client.getZoomFocus(channel);
|
|
889
899
|
if (!info?.zoom) {
|
|
890
|
-
|
|
900
|
+
logger.warn('Zoom command requested but camera did not report zoom support.');
|
|
891
901
|
return;
|
|
892
902
|
}
|
|
893
903
|
|
|
@@ -1095,6 +1105,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1095
1105
|
}
|
|
1096
1106
|
|
|
1097
1107
|
async updateDeviceInfo(): Promise<void> {
|
|
1108
|
+
const logger = this.getBaichuanLogger();
|
|
1109
|
+
|
|
1098
1110
|
const { ipAddress, rtspChannel } = this.storageSettings.values;
|
|
1099
1111
|
try {
|
|
1100
1112
|
const api = await this.ensureClient();
|
|
@@ -1105,8 +1117,10 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1105
1117
|
ipAddress,
|
|
1106
1118
|
deviceData,
|
|
1107
1119
|
});
|
|
1120
|
+
|
|
1121
|
+
logger.log(`Device info updated: ${JSON.stringify(deviceData)}`);
|
|
1108
1122
|
} catch (e) {
|
|
1109
|
-
|
|
1123
|
+
logger.warn('Failed to fetch device info', e);
|
|
1110
1124
|
}
|
|
1111
1125
|
}
|
|
1112
1126
|
|
|
@@ -1182,6 +1196,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1182
1196
|
* This should be called periodically for regular cameras and once when battery cameras wake up.
|
|
1183
1197
|
*/
|
|
1184
1198
|
async alignAuxDevicesState(): Promise<void> {
|
|
1199
|
+
const logger = this.getBaichuanLogger();
|
|
1200
|
+
|
|
1185
1201
|
const api = this.baichuanApi;
|
|
1186
1202
|
if (!api) return;
|
|
1187
1203
|
|
|
@@ -1195,7 +1211,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1195
1211
|
const sirenState = await api.getSiren(channel);
|
|
1196
1212
|
this.siren.on = sirenState.enabled;
|
|
1197
1213
|
} catch (e) {
|
|
1198
|
-
|
|
1214
|
+
logger.debug('Failed to align siren state', e);
|
|
1199
1215
|
}
|
|
1200
1216
|
}
|
|
1201
1217
|
|
|
@@ -1208,7 +1224,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1208
1224
|
this.floodlight.brightness = wl.brightness;
|
|
1209
1225
|
}
|
|
1210
1226
|
} catch (e) {
|
|
1211
|
-
|
|
1227
|
+
logger.debug('Failed to align floodlight state', e);
|
|
1212
1228
|
}
|
|
1213
1229
|
}
|
|
1214
1230
|
|
|
@@ -1232,16 +1248,18 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1232
1248
|
}
|
|
1233
1249
|
}
|
|
1234
1250
|
} catch (e) {
|
|
1235
|
-
|
|
1251
|
+
logger.debug('Failed to align PIR state', e);
|
|
1236
1252
|
}
|
|
1237
1253
|
}
|
|
1238
1254
|
} catch (e) {
|
|
1239
|
-
|
|
1255
|
+
logger.debug('Failed to align auxiliary devices state', e);
|
|
1240
1256
|
}
|
|
1241
1257
|
}
|
|
1242
1258
|
|
|
1243
1259
|
// Video stream helper methods
|
|
1244
1260
|
protected addRtspCredentials(rtspUrl: string): string {
|
|
1261
|
+
const logger = this.getBaichuanLogger();
|
|
1262
|
+
|
|
1245
1263
|
const { username, password } = this.storageSettings.values;
|
|
1246
1264
|
if (!username) {
|
|
1247
1265
|
return rtspUrl;
|
|
@@ -1266,7 +1284,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1266
1284
|
return url.toString();
|
|
1267
1285
|
} catch (e) {
|
|
1268
1286
|
// If URL parsing fails, return original URL
|
|
1269
|
-
|
|
1287
|
+
logger.warn('Failed to parse URL for credentials', e);
|
|
1270
1288
|
return rtspUrl;
|
|
1271
1289
|
}
|
|
1272
1290
|
}
|
|
@@ -1284,6 +1302,8 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1284
1302
|
}
|
|
1285
1303
|
|
|
1286
1304
|
protected async ensureNetPortCache(): Promise<void> {
|
|
1305
|
+
const logger = this.getBaichuanLogger();
|
|
1306
|
+
|
|
1287
1307
|
if (this.cachedNetPort) {
|
|
1288
1308
|
return;
|
|
1289
1309
|
}
|
|
@@ -1307,7 +1327,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1307
1327
|
} catch (e) {
|
|
1308
1328
|
// Only log if it's not a recoverable error to avoid spam
|
|
1309
1329
|
if (!this.isRecoverableBaichuanError?.(e)) {
|
|
1310
|
-
|
|
1330
|
+
logger.warn('Failed to get net port, using defaults', e);
|
|
1311
1331
|
}
|
|
1312
1332
|
// Use defaults if we can't get the ports
|
|
1313
1333
|
this.cachedNetPort = {
|
|
@@ -1363,7 +1383,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1363
1383
|
}
|
|
1364
1384
|
|
|
1365
1385
|
if (streams.length) {
|
|
1366
|
-
|
|
1386
|
+
logger.log('Fetched video stream options', { streams, netPort: this.cachedNetPort });
|
|
1367
1387
|
this.cachedVideoStreamOptions = streams;
|
|
1368
1388
|
return streams;
|
|
1369
1389
|
}
|
|
@@ -1492,14 +1512,14 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1492
1512
|
info: this.info,
|
|
1493
1513
|
};
|
|
1494
1514
|
|
|
1495
|
-
|
|
1515
|
+
logger.log(`Updating device interfaces: ${JSON.stringify(device)}`);
|
|
1496
1516
|
|
|
1497
1517
|
await sdk.deviceManager.onDeviceDiscovered(device);
|
|
1498
1518
|
} catch (e) {
|
|
1499
|
-
|
|
1519
|
+
logger.error('Failed to update device interfaces', e);
|
|
1500
1520
|
}
|
|
1501
1521
|
|
|
1502
|
-
|
|
1522
|
+
logger.log(`Refreshed device capabilities: ${JSON.stringify({ capabilities, abilities, support, presets })}`);
|
|
1503
1523
|
}
|
|
1504
1524
|
catch (e) {
|
|
1505
1525
|
logger.error('Failed to refresh abilities', e);
|
|
@@ -1561,7 +1581,7 @@ export abstract class CommonCameraMixin extends BaseBaichuanClass implements Vid
|
|
|
1561
1581
|
|
|
1562
1582
|
this.streamManager = new StreamManager({
|
|
1563
1583
|
createStreamClient: () => this.createStreamClient(),
|
|
1564
|
-
getLogger: () =>
|
|
1584
|
+
getLogger: () => logger as Console,
|
|
1565
1585
|
credentials: {
|
|
1566
1586
|
username,
|
|
1567
1587
|
password
|
package/src/nvr.ts
CHANGED
|
@@ -53,7 +53,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
53
53
|
rtspChannel: number;
|
|
54
54
|
deviceData: DeviceInfoResponse;
|
|
55
55
|
}>();
|
|
56
|
-
|
|
56
|
+
lastNvrInfoCheck: number | undefined;
|
|
57
57
|
lastErrorsCheck: number | undefined;
|
|
58
58
|
lastDevicesStatusCheck: number | undefined;
|
|
59
59
|
cameraNativeMap = new Map<string, ReolinkNativeCamera | ReolinkNativeBatteryCamera>();
|
|
@@ -169,7 +169,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
169
169
|
// Find camera for this channel
|
|
170
170
|
const channel = ev?.channel;
|
|
171
171
|
if (channel === undefined) {
|
|
172
|
-
logger.
|
|
172
|
+
logger.error('Event has no channel, ignoring');
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
175
175
|
|
|
@@ -177,7 +177,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
177
177
|
const targetCamera = nativeId ? this.cameraNativeMap.get(nativeId) : undefined;
|
|
178
178
|
|
|
179
179
|
if (!targetCamera) {
|
|
180
|
-
logger.
|
|
180
|
+
logger.error(`No camera found for channel ${channel}, ignoring event`);
|
|
181
181
|
return;
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -211,7 +211,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
211
211
|
motion = true;
|
|
212
212
|
break;
|
|
213
213
|
default:
|
|
214
|
-
logger.
|
|
214
|
+
logger.error(`Unknown event type: ${ev?.type}`);
|
|
215
215
|
return;
|
|
216
216
|
}
|
|
217
217
|
|
|
@@ -309,13 +309,12 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
309
309
|
// Note: ReolinkCgiApi doesn't have checkErrors, skip for now
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
-
if (!this.
|
|
313
|
-
logger.log('Starting
|
|
314
|
-
this.
|
|
315
|
-
const {
|
|
312
|
+
if (!this.lastNvrInfoCheck || now - this.lastNvrInfoCheck > 1000 * 60 * 5) {
|
|
313
|
+
logger.log('Starting NVR info data fetch');
|
|
314
|
+
this.lastNvrInfoCheck = now;
|
|
315
|
+
const { nvrData } = await api.getNvrInfo();
|
|
316
316
|
const { devicesData, channelsResponse, response } = await api.getDevicesInfo();
|
|
317
|
-
logger.log(
|
|
318
|
-
logger.debug(`${JSON.stringify({ hubData, devicesData, channelsResponse, response })}`);
|
|
317
|
+
logger.log(`NVR info data fetched: ${JSON.stringify({ nvrData, devicesData, channelsResponse, response })}`);
|
|
319
318
|
|
|
320
319
|
await this.discoverDevices(true);
|
|
321
320
|
}
|
|
@@ -355,6 +354,8 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
355
354
|
}
|
|
356
355
|
|
|
357
356
|
async updateDeviceInfo(): Promise<void> {
|
|
357
|
+
const logger = this.getBaichuanLogger();
|
|
358
|
+
|
|
358
359
|
const { ipAddress } = this.storageSettings.values;
|
|
359
360
|
try {
|
|
360
361
|
const api = await this.ensureClient();
|
|
@@ -365,8 +366,10 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
365
366
|
ipAddress,
|
|
366
367
|
deviceData,
|
|
367
368
|
});
|
|
369
|
+
|
|
370
|
+
logger.log(`Device info updated: ${JSON.stringify(deviceData)}`);
|
|
368
371
|
} catch (e) {
|
|
369
|
-
|
|
372
|
+
logger.warn('Failed to fetch device info', e);
|
|
370
373
|
}
|
|
371
374
|
}
|
|
372
375
|
|
|
@@ -422,12 +425,8 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
422
425
|
const api = await this.ensureClient();
|
|
423
426
|
const logger = this.getBaichuanLogger();
|
|
424
427
|
|
|
425
|
-
logger.log('Starting channels discovery using getDevicesInfo...');
|
|
426
|
-
|
|
427
428
|
const { devicesData, channels } = await api.getDevicesInfo();
|
|
428
|
-
|
|
429
|
-
logger.log(`getDevicesInfo completed. Found ${channels.length} channels.`);
|
|
430
|
-
|
|
429
|
+
logger.log(`Sync entities from remote for ${channels.length} channels`);
|
|
431
430
|
// Process each channel that was successfully discovered
|
|
432
431
|
for (const channel of channels) {
|
|
433
432
|
try {
|
|
@@ -479,7 +478,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
479
478
|
}
|
|
480
479
|
}
|
|
481
480
|
|
|
482
|
-
logger.log(`Channel discovery completed.
|
|
481
|
+
logger.log(`Channel discovery completed. ${JSON.stringify({ devicesData, channels })}`);
|
|
483
482
|
}
|
|
484
483
|
|
|
485
484
|
async discoverDevices(scan?: boolean): Promise<DiscoveredDevice[]> {
|
|
@@ -532,7 +531,8 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
532
531
|
await sdk.deviceManager.onDeviceDiscovered(actualDevice);
|
|
533
532
|
|
|
534
533
|
const device = await this.getDevice(adopt.nativeId);
|
|
535
|
-
this.getBaichuanLogger()
|
|
534
|
+
const logger = this.getBaichuanLogger();
|
|
535
|
+
logger.log('Adopted device', entry, device?.name);
|
|
536
536
|
const { username, password, ipAddress } = this.storageSettings.values;
|
|
537
537
|
|
|
538
538
|
device.storageSettings.values.rtspChannel = entry.rtspChannel;
|