@apocaliss92/scrypted-reolink-native 0.5.19 → 0.5.21
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/main.nodejs.js.LICENSE.txt +4 -0
- package/dist/plugin.zip +0 -0
- package/package.json +3 -3
- package/src/baichuan-base.ts +47 -1
- package/src/camera.ts +205 -5
- package/src/email-push-server-device.ts +583 -0
- package/src/main.ts +228 -188
package/src/main.ts
CHANGED
|
@@ -11,16 +11,16 @@ if (typeof globalThis.File === "undefined") {
|
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
import type {
|
|
15
|
-
|
|
14
|
+
import type {
|
|
15
|
+
AutoDetectMode,
|
|
16
|
+
ReolinkBaichuanApi,
|
|
17
|
+
} from "@apocaliss92/nodelink-js" with {
|
|
18
|
+
"resolution-mode": "import",
|
|
16
19
|
};
|
|
17
20
|
import sdk, {
|
|
18
|
-
AdoptDevice,
|
|
19
21
|
DeviceCreator,
|
|
20
22
|
DeviceCreatorSettings,
|
|
21
|
-
DeviceDiscovery,
|
|
22
23
|
DeviceProvider,
|
|
23
|
-
DiscoveredDevice,
|
|
24
24
|
HttpRequest,
|
|
25
25
|
HttpResponse,
|
|
26
26
|
ScryptedDeviceBase,
|
|
@@ -28,15 +28,23 @@ import sdk, {
|
|
|
28
28
|
ScryptedInterface,
|
|
29
29
|
ScryptedNativeId,
|
|
30
30
|
Setting,
|
|
31
|
-
Settings
|
|
31
|
+
Settings,
|
|
32
32
|
} from "@scrypted/sdk";
|
|
33
33
|
import { randomBytes } from "crypto";
|
|
34
34
|
import { BaseBaichuanClass } from "./baichuan-base";
|
|
35
35
|
import { ReolinkCamera } from "./camera";
|
|
36
|
-
import { createBaichuanApi, normalizeUid, type BaichuanTransport } from "./connect";
|
|
37
36
|
import {
|
|
38
|
-
|
|
37
|
+
createBaichuanApi,
|
|
38
|
+
normalizeUid,
|
|
39
|
+
type BaichuanTransport,
|
|
40
|
+
} from "./connect";
|
|
41
|
+
import {
|
|
42
|
+
EMAIL_PUSH_SERVER_NATIVE_ID,
|
|
43
|
+
EmailPushServerDevice,
|
|
44
|
+
} from "./email-push-server-device";
|
|
45
|
+
import {
|
|
39
46
|
INTERCOM_PROVIDER_NATIVE_ID,
|
|
47
|
+
ReolinkNativeIntercom,
|
|
40
48
|
} from "./intercom-provider";
|
|
41
49
|
import { ReolinkNativeMultiFocalDevice } from "./multiFocal";
|
|
42
50
|
import { ReolinkNativeNvrDevice } from "./nvr";
|
|
@@ -54,14 +62,22 @@ import {
|
|
|
54
62
|
|
|
55
63
|
class ReolinkNativePlugin
|
|
56
64
|
extends ScryptedDeviceBase
|
|
57
|
-
implements DeviceProvider, DeviceCreator,
|
|
65
|
+
implements DeviceProvider, DeviceCreator, Settings
|
|
66
|
+
{
|
|
58
67
|
devices = new Map<string, BaseBaichuanClass>();
|
|
59
68
|
camerasMap = new Map<string, ReolinkCamera>();
|
|
60
69
|
nvrDeviceId: string;
|
|
61
70
|
private intercomProvider?: ReolinkNativeIntercom;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
// Public so `ReolinkCamera` can call the device's
|
|
72
|
+
// `getManagerSetupParamsForCamera()` when the user clicks
|
|
73
|
+
// "Auto-configure from Email Push Server" on the camera page.
|
|
74
|
+
// May be undefined until `getDevice(EMAIL_PUSH_SERVER_NATIVE_ID)`
|
|
75
|
+
// is invoked at least once — callers materialise it via
|
|
76
|
+
// `await plugin.getDevice(EMAIL_PUSH_SERVER_NATIVE_ID)`.
|
|
77
|
+
emailPushServer?: EmailPushServerDevice;
|
|
78
|
+
// private networkDiscoveredDevices = new Map<string, { discovered: DiscoveredDevice; libDevice: LibDiscoveredDevice }>();
|
|
79
|
+
// private discoverDevicesPromise: Promise<DiscoveredDevice[]> | undefined;
|
|
80
|
+
// private autodiscoveryClient: AutodiscoveryClient | undefined;
|
|
65
81
|
|
|
66
82
|
// Shared Baichuan API connections for external devices (non-Reolink Native cameras).
|
|
67
83
|
// Keyed by Scrypted device ID. Multiple mixins on the same device share one connection.
|
|
@@ -85,58 +101,66 @@ class ReolinkNativePlugin
|
|
|
85
101
|
providerNativeId: this.nativeId,
|
|
86
102
|
});
|
|
87
103
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Stop previous client if settings changed
|
|
96
|
-
this.autodiscoveryClient?.stop();
|
|
97
|
-
|
|
98
|
-
const methods: string[] = JSON.parse(this.storage.getItem("discoveryMethods") || '["arp","onvif"]');
|
|
99
|
-
const subnetsRaw = this.storage.getItem("discoverySubnets") || "";
|
|
100
|
-
const subnets = subnetsRaw.split(",").map((s: string) => s.trim()).filter(Boolean);
|
|
101
|
-
|
|
102
|
-
this.autodiscoveryClient = new AutodiscoveryClientClass({
|
|
103
|
-
enableHttpScanning: methods.includes("http"),
|
|
104
|
-
enableUdpDiscovery: methods.includes("udp"),
|
|
105
|
-
enableTcpPortScan: methods.includes("tcp"),
|
|
106
|
-
enableArpLookup: methods.includes("arp"),
|
|
107
|
-
enableDhcpListener: methods.includes("dhcp"),
|
|
108
|
-
enableOnvifDiscovery: methods.includes("onvif"),
|
|
109
|
-
logger: this.console,
|
|
110
|
-
httpProbeTimeoutMs: 3000,
|
|
111
|
-
udpBroadcastTimeoutMs: 5000,
|
|
112
|
-
tcpProbeTimeoutMs: 1500,
|
|
113
|
-
dhcpListenerTimeoutMs: 10000,
|
|
114
|
-
scanIntervalMs: 120_000, // 2 minutes
|
|
115
|
-
autoStart: true,
|
|
116
|
-
...(subnets.length > 0 ? { networkCidr: subnets[0] } : {}),
|
|
117
|
-
onDeviceDiscovered: (libDevice) => {
|
|
118
|
-
this.handleBackgroundDiscovery(libDevice);
|
|
119
|
-
},
|
|
104
|
+
sdk.deviceManager.onDeviceDiscovered({
|
|
105
|
+
nativeId: EMAIL_PUSH_SERVER_NATIVE_ID,
|
|
106
|
+
name: "Reolink E-mail Push Server",
|
|
107
|
+
interfaces: [ScryptedInterface.Settings, ScryptedInterface.Online],
|
|
108
|
+
type: ScryptedDeviceType.API,
|
|
109
|
+
providerNativeId: this.nativeId,
|
|
120
110
|
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
private handleBackgroundDiscovery(libDevice: LibDiscoveredDevice) {
|
|
124
|
-
const existingIps = this.getExistingDeviceIps();
|
|
125
|
-
if (existingIps.has(libDevice.host)) return;
|
|
126
|
-
|
|
127
|
-
const nativeId = `discovered-${libDevice.host}`;
|
|
128
|
-
if (this.networkDiscoveredDevices.has(nativeId)) return;
|
|
129
|
-
|
|
130
|
-
this.addToDiscoveryList(libDevice);
|
|
131
|
-
this.console.log(`[Discovery] Background: new device ${libDevice.host} (${libDevice.name || libDevice.model || "unknown"})`);
|
|
132
111
|
|
|
133
|
-
//
|
|
134
|
-
this.
|
|
135
|
-
ScryptedInterface.DeviceDiscovery,
|
|
136
|
-
[...this.networkDiscoveredDevices.values()].map((d) => d.discovered),
|
|
137
|
-
);
|
|
112
|
+
// Start background autodiscovery
|
|
113
|
+
// this.startBackgroundDiscovery();
|
|
138
114
|
}
|
|
139
115
|
|
|
116
|
+
// private async startBackgroundDiscovery() {
|
|
117
|
+
// const { AutodiscoveryClient: AutodiscoveryClientClass } = await import("@apocaliss92/nodelink-js");
|
|
118
|
+
|
|
119
|
+
// // Stop previous client if settings changed
|
|
120
|
+
// this.autodiscoveryClient?.stop();
|
|
121
|
+
|
|
122
|
+
// const methods: string[] = JSON.parse(this.storage.getItem("discoveryMethods") || '["arp","onvif"]');
|
|
123
|
+
// const subnetsRaw = this.storage.getItem("discoverySubnets") || "";
|
|
124
|
+
// const subnets = subnetsRaw.split(",").map((s: string) => s.trim()).filter(Boolean);
|
|
125
|
+
|
|
126
|
+
// this.autodiscoveryClient = new AutodiscoveryClientClass({
|
|
127
|
+
// enableHttpScanning: methods.includes("http"),
|
|
128
|
+
// enableUdpDiscovery: methods.includes("udp"),
|
|
129
|
+
// enableTcpPortScan: methods.includes("tcp"),
|
|
130
|
+
// enableArpLookup: methods.includes("arp"),
|
|
131
|
+
// enableDhcpListener: methods.includes("dhcp"),
|
|
132
|
+
// enableOnvifDiscovery: methods.includes("onvif"),
|
|
133
|
+
// logger: this.console,
|
|
134
|
+
// httpProbeTimeoutMs: 3000,
|
|
135
|
+
// udpBroadcastTimeoutMs: 5000,
|
|
136
|
+
// tcpProbeTimeoutMs: 1500,
|
|
137
|
+
// dhcpListenerTimeoutMs: 10000,
|
|
138
|
+
// scanIntervalMs: 120_000, // 2 minutes
|
|
139
|
+
// autoStart: true,
|
|
140
|
+
// ...(subnets.length > 0 ? { networkCidr: subnets[0] } : {}),
|
|
141
|
+
// onDeviceDiscovered: (libDevice) => {
|
|
142
|
+
// this.handleBackgroundDiscovery(libDevice);
|
|
143
|
+
// },
|
|
144
|
+
// });
|
|
145
|
+
// }
|
|
146
|
+
|
|
147
|
+
// private handleBackgroundDiscovery(libDevice: LibDiscoveredDevice) {
|
|
148
|
+
// const existingIps = this.getExistingDeviceIps();
|
|
149
|
+
// if (existingIps.has(libDevice.host)) return;
|
|
150
|
+
|
|
151
|
+
// const nativeId = `discovered-${libDevice.host}`;
|
|
152
|
+
// if (this.networkDiscoveredDevices.has(nativeId)) return;
|
|
153
|
+
|
|
154
|
+
// this.addToDiscoveryList(libDevice);
|
|
155
|
+
// this.console.log(`[Discovery] Background: new device ${libDevice.host} (${libDevice.name || libDevice.model || "unknown"})`);
|
|
156
|
+
|
|
157
|
+
// // Notify Scrypted of updated discovery list
|
|
158
|
+
// this.onDeviceEvent(
|
|
159
|
+
// ScryptedInterface.DeviceDiscovery,
|
|
160
|
+
// [...this.networkDiscoveredDevices.values()].map((d) => d.discovered),
|
|
161
|
+
// );
|
|
162
|
+
// }
|
|
163
|
+
|
|
140
164
|
async acquireExternalClient(
|
|
141
165
|
deviceId: string,
|
|
142
166
|
config: {
|
|
@@ -198,6 +222,20 @@ class ReolinkNativePlugin
|
|
|
198
222
|
return this.intercomProvider;
|
|
199
223
|
}
|
|
200
224
|
|
|
225
|
+
if (nativeId === EMAIL_PUSH_SERVER_NATIVE_ID) {
|
|
226
|
+
if (!this.emailPushServer) {
|
|
227
|
+
this.emailPushServer = new EmailPushServerDevice(nativeId);
|
|
228
|
+
this.emailPushServer.plugin = this;
|
|
229
|
+
// Fire-and-forget: starts the SMTP server if enabled in storage.
|
|
230
|
+
void this.emailPushServer.start().catch((e: unknown) => {
|
|
231
|
+
this.console.error(
|
|
232
|
+
`Email push server start failed: ${e instanceof Error ? e.message : e}`,
|
|
233
|
+
);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return this.emailPushServer;
|
|
237
|
+
}
|
|
238
|
+
|
|
201
239
|
if (this.devices.has(nativeId)) {
|
|
202
240
|
return this.devices.get(nativeId)!;
|
|
203
241
|
}
|
|
@@ -229,8 +267,7 @@ class ReolinkNativePlugin
|
|
|
229
267
|
this.console.log(
|
|
230
268
|
`[AutoDetect] Starting device type detection for ${ipAddress}...${forceType ? ` (forcing type: ${forceType})` : ""}`,
|
|
231
269
|
);
|
|
232
|
-
const { autoDetectDeviceType } =
|
|
233
|
-
await import("@apocaliss92/nodelink-js");
|
|
270
|
+
const { autoDetectDeviceType } = await import("@apocaliss92/nodelink-js");
|
|
234
271
|
// 'Auto', 'NVR', 'Battery Camera', 'Regular Camera
|
|
235
272
|
const mode: AutoDetectMode =
|
|
236
273
|
forceType === "Auto"
|
|
@@ -426,8 +463,8 @@ class ReolinkNativePlugin
|
|
|
426
463
|
private getExistingDeviceIps(): Set<string> {
|
|
427
464
|
const ips = new Set<string>();
|
|
428
465
|
for (const [, device] of this.devices) {
|
|
429
|
-
|
|
430
|
-
|
|
466
|
+
const ip = (device as any).storageSettings?.values?.ipAddress;
|
|
467
|
+
if (ip) ips.add(ip);
|
|
431
468
|
}
|
|
432
469
|
// Also check storage for devices not yet instantiated
|
|
433
470
|
const nativeIds = sdk.deviceManager.getNativeIds().filter((nid) => !!nid);
|
|
@@ -443,133 +480,136 @@ class ReolinkNativePlugin
|
|
|
443
480
|
return ips;
|
|
444
481
|
}
|
|
445
482
|
|
|
446
|
-
async discoverDevices(scan?: boolean): Promise<DiscoveredDevice[]> {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
private rebuildDiscoveryList() {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
private addToDiscoveryList(libDevice: LibDiscoveredDevice) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
async adoptDevice(adopt: AdoptDevice): Promise<string> {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
483
|
+
// async discoverDevices(scan?: boolean): Promise<DiscoveredDevice[]> {
|
|
484
|
+
// if (scan) {
|
|
485
|
+
// if (this.discoverDevicesPromise) {
|
|
486
|
+
// return await this.discoverDevicesPromise;
|
|
487
|
+
// }
|
|
488
|
+
// this.discoverDevicesPromise = (async () => {
|
|
489
|
+
// try {
|
|
490
|
+
// // Trigger immediate scan on the background client
|
|
491
|
+
// if (this.autodiscoveryClient) {
|
|
492
|
+
// await this.autodiscoveryClient.scanNow();
|
|
493
|
+
// }
|
|
494
|
+
|
|
495
|
+
// // Rebuild the discovery list from the client's cumulative results
|
|
496
|
+
// this.rebuildDiscoveryList();
|
|
497
|
+
|
|
498
|
+
// return [...this.networkDiscoveredDevices.values()].map((d) => d.discovered);
|
|
499
|
+
// } catch (e: any) {
|
|
500
|
+
// this.console.error(`[Discovery] Scan failed: ${e?.message || String(e)}`);
|
|
501
|
+
// return [];
|
|
502
|
+
// } finally {
|
|
503
|
+
// this.discoverDevicesPromise = undefined;
|
|
504
|
+
// }
|
|
505
|
+
// })();
|
|
506
|
+
// return await this.discoverDevicesPromise;
|
|
507
|
+
// }
|
|
508
|
+
|
|
509
|
+
// // No scan: return cached results
|
|
510
|
+
// return [...this.networkDiscoveredDevices.values()].map((d) => d.discovered);
|
|
511
|
+
// }
|
|
512
|
+
|
|
513
|
+
// private rebuildDiscoveryList() {
|
|
514
|
+
// const existingIps = this.getExistingDeviceIps();
|
|
515
|
+
// const allDevices = this.autodiscoveryClient?.getDiscoveredDevices() ?? [];
|
|
516
|
+
// this.networkDiscoveredDevices.clear();
|
|
517
|
+
|
|
518
|
+
// for (const libDevice of allDevices) {
|
|
519
|
+
// const alreadyAdded = existingIps.has(libDevice.host);
|
|
520
|
+
// this.console.log(`[Discovery] Found ${libDevice.host} (${libDevice.name || libDevice.model || "unknown"})${alreadyAdded ? " — already added, skipping" : ""}`);
|
|
521
|
+
// if (alreadyAdded) continue;
|
|
522
|
+
// this.addToDiscoveryList(libDevice);
|
|
523
|
+
// }
|
|
524
|
+
|
|
525
|
+
// this.console.log(`[Discovery] ${this.networkDiscoveredDevices.size} new device(s), ${existingIps.size} already added`);
|
|
526
|
+
// }
|
|
527
|
+
|
|
528
|
+
// private addToDiscoveryList(libDevice: LibDiscoveredDevice) {
|
|
529
|
+
// const nativeId = `discovered-${libDevice.host}`;
|
|
530
|
+
// const name = libDevice.name || libDevice.model || `Reolink ${libDevice.host}`;
|
|
531
|
+
// const descParts: string[] = [libDevice.host];
|
|
532
|
+
// if (libDevice.model) descParts.push(libDevice.model);
|
|
533
|
+
// if (libDevice.uid) descParts.push(`UID: ${libDevice.uid}`);
|
|
534
|
+
|
|
535
|
+
// const discovered: DiscoveredDevice = {
|
|
536
|
+
// nativeId,
|
|
537
|
+
// name,
|
|
538
|
+
// description: descParts.join(" — "),
|
|
539
|
+
// type: ScryptedDeviceType.Camera,
|
|
540
|
+
// settings: [
|
|
541
|
+
// { key: "username", title: "Username", value: "admin" },
|
|
542
|
+
// { key: "password", title: "Password", type: "password" },
|
|
543
|
+
// ],
|
|
544
|
+
// };
|
|
545
|
+
|
|
546
|
+
// if (libDevice.firmwareVersion || libDevice.model) {
|
|
547
|
+
// discovered.info = {
|
|
548
|
+
// manufacturer: "Reolink",
|
|
549
|
+
// ...(libDevice.model ? { model: libDevice.model } : {}),
|
|
550
|
+
// ...(libDevice.firmwareVersion ? { firmware: libDevice.firmwareVersion } : {}),
|
|
551
|
+
// };
|
|
552
|
+
// }
|
|
553
|
+
|
|
554
|
+
// this.networkDiscoveredDevices.set(nativeId, { discovered, libDevice });
|
|
555
|
+
// }
|
|
556
|
+
|
|
557
|
+
// async adoptDevice(adopt: AdoptDevice): Promise<string> {
|
|
558
|
+
// const entry = this.networkDiscoveredDevices.get(adopt.nativeId);
|
|
559
|
+
// if (!entry) throw new Error("Device not found in discovery cache");
|
|
560
|
+
|
|
561
|
+
// const { libDevice } = entry;
|
|
562
|
+
// const username = adopt.settings?.username?.toString() || "admin";
|
|
563
|
+
// const password = adopt.settings?.password?.toString();
|
|
564
|
+
// if (!password) throw new Error("Password is required");
|
|
565
|
+
|
|
566
|
+
// // Delegate to createDevice which handles auto-detection, capabilities, etc.
|
|
567
|
+
// const nativeId = await this.createDevice({
|
|
568
|
+
// ip: libDevice.host,
|
|
569
|
+
// username,
|
|
570
|
+
// password,
|
|
571
|
+
// uid: libDevice.uid || "",
|
|
572
|
+
// deviceType: "Auto",
|
|
573
|
+
// });
|
|
574
|
+
|
|
575
|
+
// // Remove from discovered cache
|
|
576
|
+
// this.networkDiscoveredDevices.delete(adopt.nativeId);
|
|
577
|
+
|
|
578
|
+
// // Notify Scrypted of updated discovery list
|
|
579
|
+
// await this.onDeviceEvent(
|
|
580
|
+
// ScryptedInterface.DeviceDiscovery,
|
|
581
|
+
// [...this.networkDiscoveredDevices.values()].map((d) => d.discovered),
|
|
582
|
+
// );
|
|
583
|
+
|
|
584
|
+
// return nativeId;
|
|
585
|
+
// }
|
|
549
586
|
|
|
550
587
|
async getSettings(): Promise<Setting[]> {
|
|
551
588
|
return [
|
|
552
|
-
{
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
},
|
|
561
|
-
{
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
},
|
|
589
|
+
// {
|
|
590
|
+
// key: "discoveryMethods",
|
|
591
|
+
// title: "Discovery Methods",
|
|
592
|
+
// description: "ARP: reads ARP table for Reolink MAC prefixes (fast, like Home Assistant). ONVIF: WS-Discovery multicast (most Reolink cameras). DHCP: passive listener on port 67 (requires root).",
|
|
593
|
+
// type: "string",
|
|
594
|
+
// choices: ["arp", "onvif", "dhcp"],
|
|
595
|
+
// multiple: true,
|
|
596
|
+
// value: JSON.parse(this.storage.getItem("discoveryMethods") || '["arp","onvif"]'),
|
|
597
|
+
// },
|
|
598
|
+
// {
|
|
599
|
+
// key: "discoverySubnets",
|
|
600
|
+
// title: "Discovery Subnets",
|
|
601
|
+
// description: "Comma-separated list of subnets to scan in CIDR notation (e.g. 192.168.1.0/24, 10.0.0.0/24). Leave empty to auto-detect the local network. Used for HTTP and TCP scanning; ARP/UDP/DHCP methods don't need this.",
|
|
602
|
+
// type: "string",
|
|
603
|
+
// placeholder: "192.168.1.0/24, 10.0.0.0/24",
|
|
604
|
+
// value: this.storage.getItem("discoverySubnets") || "",
|
|
605
|
+
// },
|
|
569
606
|
];
|
|
570
607
|
}
|
|
571
608
|
|
|
572
|
-
async putSetting(
|
|
609
|
+
async putSetting(
|
|
610
|
+
key: string,
|
|
611
|
+
value: string | number | boolean | string[],
|
|
612
|
+
): Promise<void> {
|
|
573
613
|
if (Array.isArray(value)) {
|
|
574
614
|
this.storage.setItem(key, JSON.stringify(value));
|
|
575
615
|
} else {
|
|
@@ -577,9 +617,9 @@ class ReolinkNativePlugin
|
|
|
577
617
|
}
|
|
578
618
|
|
|
579
619
|
// Restart background discovery when discovery settings change
|
|
580
|
-
if (key === "discoveryMethods" || key === "discoverySubnets") {
|
|
581
|
-
|
|
582
|
-
}
|
|
620
|
+
// if (key === "discoveryMethods" || key === "discoverySubnets") {
|
|
621
|
+
// this.startBackgroundDiscovery();
|
|
622
|
+
// }
|
|
583
623
|
}
|
|
584
624
|
|
|
585
625
|
async getCreateDeviceSettings(): Promise<Setting[]> {
|