@apocaliss92/scrypted-reolink-native 0.1.24 → 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/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/baichuan-base.ts +1 -2
- package/src/camera-battery.ts +1 -8
- package/src/camera.ts +4 -25
- package/src/common.ts +122 -100
- package/src/connect.ts +3 -3
- package/src/main.ts +24 -16
- package/src/multiFocal.ts +43 -103
- package/src/nvr.ts +6 -6
- package/src/utils.ts +12 -3
package/src/multiFocal.ts
CHANGED
|
@@ -1,110 +1,50 @@
|
|
|
1
1
|
import type { DeviceCapabilities, DualLensChannelAnalysis, ReolinkSimpleEvent } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
|
|
2
|
-
import sdk, { Device, DeviceProvider, Reboot, ScryptedDeviceType, Setting, Settings, SettingValue } from "@scrypted/sdk";
|
|
3
|
-
import {
|
|
4
|
-
import { BaseBaichuanClass, type BaichuanConnectionCallbacks, type BaichuanConnectionConfig } from "./baichuan-base";
|
|
2
|
+
import sdk, { Device, DeviceProvider, MediaObject, Reboot, ScryptedDeviceType, Setting, Settings, SettingValue } from "@scrypted/sdk";
|
|
3
|
+
import { type BaichuanConnectionCallbacks } from "./baichuan-base";
|
|
5
4
|
import { ReolinkNativeCamera } from "./camera";
|
|
6
5
|
import { ReolinkNativeBatteryCamera } from "./camera-battery";
|
|
7
|
-
import {
|
|
6
|
+
import { CameraType, CommonCameraMixin } from "./common";
|
|
8
7
|
import ReolinkNativePlugin from "./main";
|
|
9
8
|
import { batteryCameraSuffix, cameraSuffix, getDeviceInterfaces, updateDeviceInfo } from "./utils";
|
|
10
9
|
|
|
11
|
-
export class ReolinkNativeMultiFocalDevice extends
|
|
12
|
-
storageSettings = new StorageSettings(this, {
|
|
13
|
-
debugEvents: {
|
|
14
|
-
title: 'Debug Events',
|
|
15
|
-
type: 'boolean',
|
|
16
|
-
immediate: true,
|
|
17
|
-
},
|
|
18
|
-
ipAddress: {
|
|
19
|
-
title: 'IP address',
|
|
20
|
-
type: 'string',
|
|
21
|
-
onPut: async () => await this.reinit()
|
|
22
|
-
},
|
|
23
|
-
username: {
|
|
24
|
-
title: 'Username',
|
|
25
|
-
placeholder: 'admin',
|
|
26
|
-
defaultValue: 'admin',
|
|
27
|
-
type: 'string',
|
|
28
|
-
onPut: async () => await this.reinit()
|
|
29
|
-
},
|
|
30
|
-
password: {
|
|
31
|
-
title: 'Password',
|
|
32
|
-
type: 'password',
|
|
33
|
-
onPut: async () => await this.reinit()
|
|
34
|
-
},
|
|
35
|
-
uid: {
|
|
36
|
-
title: 'UID',
|
|
37
|
-
description: 'Reolink UID (required for UDP/battery multi-focal devices)',
|
|
38
|
-
type: 'string',
|
|
39
|
-
hide: true,
|
|
40
|
-
onPut: async () => await this.reinit()
|
|
41
|
-
},
|
|
42
|
-
protocol: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
hide: true,
|
|
45
|
-
},
|
|
46
|
-
diagnosticsRun: {
|
|
47
|
-
subgroup: 'Diagnostics',
|
|
48
|
-
title: 'Run Diagnostics',
|
|
49
|
-
description: 'Collect diagnostics and display results in logs.',
|
|
50
|
-
type: 'button',
|
|
51
|
-
immediate: true,
|
|
52
|
-
onPut: async () => {
|
|
53
|
-
await this.runDiagnostics();
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
multifocalInfo: {
|
|
57
|
-
json: true,
|
|
58
|
-
hide: true,
|
|
59
|
-
},
|
|
60
|
-
capabilities: {
|
|
61
|
-
json: true,
|
|
62
|
-
hide: true,
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
10
|
+
export class ReolinkNativeMultiFocalDevice extends CommonCameraMixin implements Settings, DeviceProvider, Reboot {
|
|
66
11
|
plugin: ReolinkNativePlugin;
|
|
67
12
|
cameraNativeMap = new Map<string, ReolinkNativeCamera | ReolinkNativeBatteryCamera>();
|
|
68
13
|
private channelToNativeIdMap = new Map<number, string>();
|
|
69
14
|
private initReinitTimeout: NodeJS.Timeout | undefined;
|
|
70
15
|
isBattery: boolean;
|
|
71
16
|
|
|
72
|
-
constructor(nativeId: string, plugin: ReolinkNativePlugin) {
|
|
73
|
-
super(nativeId);
|
|
17
|
+
constructor(nativeId: string, plugin: ReolinkNativePlugin, type: CameraType) {
|
|
18
|
+
super(nativeId, plugin, { type });
|
|
74
19
|
this.plugin = plugin;
|
|
75
20
|
|
|
76
|
-
this.isBattery = this.storageSettings.values.protocol === 'udp';
|
|
77
|
-
|
|
78
21
|
this.scheduleInit();
|
|
79
22
|
}
|
|
80
23
|
|
|
24
|
+
getAbilities(): DeviceCapabilities {
|
|
25
|
+
const { capabilities } = this.storageSettings.values;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
...capabilities,
|
|
29
|
+
hasPan: false,
|
|
30
|
+
hasTilt: false,
|
|
31
|
+
hasZoom: false,
|
|
32
|
+
hasPresets: false,
|
|
33
|
+
hasIntercom: false,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
81
37
|
async reboot(): Promise<void> {
|
|
82
38
|
const api = await this.ensureBaichuanClient();
|
|
83
39
|
await api.reboot();
|
|
84
40
|
}
|
|
85
41
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
throw new Error('Missing device credentials');
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const { protocol } = this.storageSettings.values;
|
|
93
|
-
|
|
94
|
-
const normalizedUid = this.isBattery ? normalizeUid(uid) : undefined;
|
|
95
|
-
|
|
96
|
-
if (protocol === 'udp' && !normalizedUid) {
|
|
97
|
-
throw new Error('UID is required for UDP multi-focal devices (BCUDP)');
|
|
98
|
-
}
|
|
42
|
+
takePicture(options?: any): Promise<MediaObject> {
|
|
43
|
+
throw new Error("Method not implemented.");
|
|
44
|
+
}
|
|
99
45
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
username,
|
|
103
|
-
password,
|
|
104
|
-
uid: normalizedUid,
|
|
105
|
-
transport: protocol,
|
|
106
|
-
logger: this.console,
|
|
107
|
-
};
|
|
46
|
+
getPictureOptions(): Promise<any[]> {
|
|
47
|
+
throw new Error("Method not implemented.");
|
|
108
48
|
}
|
|
109
49
|
|
|
110
50
|
protected getConnectionCallbacks(): BaichuanConnectionCallbacks {
|
|
@@ -134,7 +74,7 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
134
74
|
}
|
|
135
75
|
|
|
136
76
|
protected isDebugEnabled(): boolean {
|
|
137
|
-
return this.storageSettings.values.debugEvents;
|
|
77
|
+
return this.storageSettings.values.debugEvents || false;
|
|
138
78
|
}
|
|
139
79
|
|
|
140
80
|
protected getDeviceName(): string {
|
|
@@ -199,9 +139,8 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
199
139
|
device: this,
|
|
200
140
|
deviceData,
|
|
201
141
|
ipAddress: this.storageSettings.values.ipAddress,
|
|
142
|
+
logger,
|
|
202
143
|
});
|
|
203
|
-
|
|
204
|
-
logger.log(`Device info updated: ${JSON.stringify(deviceData)}`);
|
|
205
144
|
} catch (e) {
|
|
206
145
|
logger.warn('Failed to fetch device info', e);
|
|
207
146
|
}
|
|
@@ -232,7 +171,7 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
232
171
|
async reportDevices(): Promise<void> {
|
|
233
172
|
const api = await this.ensureBaichuanClient();
|
|
234
173
|
const logger = this.getBaichuanLogger();
|
|
235
|
-
const {
|
|
174
|
+
const { username, password, ipAddress, uid } = this.storageSettings.values;
|
|
236
175
|
|
|
237
176
|
const { capabilities, support, abilities, features, objects, presets } = await api.getDeviceCapabilities();
|
|
238
177
|
|
|
@@ -286,16 +225,19 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
286
225
|
}
|
|
287
226
|
|
|
288
227
|
async getDevice(nativeId: string) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
228
|
+
if (nativeId.endsWith(cameraSuffix) || nativeId.endsWith(batteryCameraSuffix)) {
|
|
229
|
+
let device = this.cameraNativeMap.get(nativeId);
|
|
230
|
+
if (!device) {
|
|
231
|
+
if (nativeId.endsWith(batteryCameraSuffix)) {
|
|
232
|
+
device = new ReolinkNativeBatteryCamera(nativeId, this.plugin, undefined, this);
|
|
233
|
+
} else {
|
|
234
|
+
device = new ReolinkNativeCamera(nativeId, this.plugin, undefined, this);
|
|
235
|
+
}
|
|
295
236
|
}
|
|
237
|
+
return device;
|
|
238
|
+
} else {
|
|
239
|
+
return super.getDevice(nativeId);
|
|
296
240
|
}
|
|
297
|
-
|
|
298
|
-
return device;
|
|
299
241
|
}
|
|
300
242
|
|
|
301
243
|
async getSettings(): Promise<Setting[]> {
|
|
@@ -309,11 +251,11 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
309
251
|
|
|
310
252
|
async releaseDevice(id: string, nativeId: string) {
|
|
311
253
|
this.cameraNativeMap.delete(nativeId);
|
|
254
|
+
super.releaseDevice(id, nativeId);
|
|
312
255
|
}
|
|
313
256
|
|
|
314
257
|
buildNativeId(channel: number): string {
|
|
315
|
-
|
|
316
|
-
return `${this.nativeId}-channel${channel}${protocol === "udp" ? batteryCameraSuffix : cameraSuffix}`;
|
|
258
|
+
return `${this.nativeId}-channel${channel}`;
|
|
317
259
|
}
|
|
318
260
|
|
|
319
261
|
forwardNativeEvent(ev: ReolinkSimpleEvent): void {
|
|
@@ -337,16 +279,14 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
337
279
|
return;
|
|
338
280
|
}
|
|
339
281
|
|
|
340
|
-
|
|
341
|
-
if (camera.onSimpleEvent) {
|
|
342
|
-
camera.onSimpleEvent(ev);
|
|
343
|
-
}
|
|
282
|
+
camera.onSimpleEvent(ev);
|
|
344
283
|
}
|
|
284
|
+
|
|
345
285
|
async unsubscribeFromAllEvents(): Promise<void> {
|
|
346
286
|
await super.unsubscribeFromEvents();
|
|
347
287
|
}
|
|
348
288
|
|
|
349
|
-
|
|
289
|
+
public async runDiagnostics(): Promise<void> {
|
|
350
290
|
const logger = this.getBaichuanLogger();
|
|
351
291
|
logger.log(`Starting Multifocal diagnostics...`);
|
|
352
292
|
|
package/src/nvr.ts
CHANGED
|
@@ -360,7 +360,8 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
360
360
|
this.lastNvrInfoCheck = now;
|
|
361
361
|
const { nvrData } = await api.getNvrInfo();
|
|
362
362
|
const { devicesData, channelsResponse, response } = await api.getDevicesInfo();
|
|
363
|
-
logger.log(`NVR info data fetched
|
|
363
|
+
logger.log(`NVR info data fetched`);
|
|
364
|
+
logger.debug(`${JSON.stringify({ nvrData, devicesData, channelsResponse, response })}`);
|
|
364
365
|
|
|
365
366
|
await this.discoverDevices(true);
|
|
366
367
|
}
|
|
@@ -372,7 +373,6 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
372
373
|
this.forwardCgiEvents(eventsRes.parsed);
|
|
373
374
|
}
|
|
374
375
|
|
|
375
|
-
// Always fetch battery info (not event-related)
|
|
376
376
|
const { batteryInfoData, response } = await api.getAllChannelsBatteryInfo();
|
|
377
377
|
|
|
378
378
|
logger.debug(`Battery info call result: ${JSON.stringify({ batteryInfoData, response })}`);
|
|
@@ -411,9 +411,8 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
411
411
|
device: this,
|
|
412
412
|
ipAddress,
|
|
413
413
|
deviceData,
|
|
414
|
+
logger
|
|
414
415
|
});
|
|
415
|
-
|
|
416
|
-
logger.log(`Device info updated: ${JSON.stringify(deviceData)}`);
|
|
417
416
|
} catch (e) {
|
|
418
417
|
logger.warn('Failed to fetch device info', e);
|
|
419
418
|
}
|
|
@@ -552,7 +551,7 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
552
551
|
}
|
|
553
552
|
}
|
|
554
553
|
|
|
555
|
-
logger.
|
|
554
|
+
logger.debug(`Channel discovery completed. ${JSON.stringify({ devicesData, channels })}`);
|
|
556
555
|
}
|
|
557
556
|
|
|
558
557
|
async discoverDevices(scan?: boolean): Promise<DiscoveredDevice[]> {
|
|
@@ -606,7 +605,8 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
606
605
|
|
|
607
606
|
const device = await this.getDevice(adopt.nativeId);
|
|
608
607
|
const logger = this.getBaichuanLogger();
|
|
609
|
-
logger.log('Adopted device',
|
|
608
|
+
logger.log('Adopted device', device?.name);
|
|
609
|
+
logger.log(JSON.stringify(entry));
|
|
610
610
|
const { username, password, ipAddress } = this.storageSettings.values;
|
|
611
611
|
|
|
612
612
|
device.storageSettings.values.rtspChannel = entry.rtspChannel;
|
package/src/utils.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DeviceCapabilities, ReolinkDeviceInfo } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
|
|
2
|
-
import { DeviceBase, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk";
|
|
2
|
+
import sdk, { Device, DeviceBase, ScryptedDeviceBase, ScryptedDeviceType, ScryptedInterface } from "@scrypted/sdk";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Enumeration of operation types that may require specific channel assignments
|
|
@@ -23,7 +23,11 @@ export type OperationChannelMap = Partial<Record<OperationChannelType, number>>;
|
|
|
23
23
|
export const nvrSuffix = `-nvr`;
|
|
24
24
|
export const batteryCameraSuffix = `-battery-cam`;
|
|
25
25
|
export const multifocalSuffix = `-multifocal`;
|
|
26
|
+
export const batteryMultifocalSuffix = `-battery-multifocal`;
|
|
26
27
|
export const cameraSuffix = `-cam`;
|
|
28
|
+
export const sirenSuffix = `-siren`;
|
|
29
|
+
export const floodlightSuffix = `-floodlight`;
|
|
30
|
+
export const pirSuffix = `-pir`;
|
|
27
31
|
|
|
28
32
|
export const getDeviceInterfaces = (props: {
|
|
29
33
|
capabilities: DeviceCapabilities,
|
|
@@ -78,9 +82,10 @@ export const getDeviceInterfaces = (props: {
|
|
|
78
82
|
export const updateDeviceInfo = async (props: {
|
|
79
83
|
device: DeviceBase,
|
|
80
84
|
ipAddress: string,
|
|
81
|
-
deviceData: ReolinkDeviceInfo
|
|
85
|
+
deviceData: ReolinkDeviceInfo,
|
|
86
|
+
logger: Console
|
|
82
87
|
}) => {
|
|
83
|
-
const { device, ipAddress, deviceData } = props;
|
|
88
|
+
const { device, ipAddress, deviceData, logger } = props;
|
|
84
89
|
try {
|
|
85
90
|
const info = device.info || {};
|
|
86
91
|
|
|
@@ -101,5 +106,9 @@ export const updateDeviceInfo = async (props: {
|
|
|
101
106
|
device.info = info;
|
|
102
107
|
|
|
103
108
|
throw e;
|
|
109
|
+
} finally {
|
|
110
|
+
|
|
111
|
+
logger.log(`Device info updated`);
|
|
112
|
+
logger.debug(`${JSON.stringify({ newInfo: device.info, deviceData })}`);
|
|
104
113
|
}
|
|
105
114
|
}
|