@apocaliss92/scrypted-reolink-native 0.1.17 → 0.1.18
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/connect.ts +2 -2
- package/src/multifocal.ts +114 -63
- package/src/nvr.ts +34 -13
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/connect.ts
CHANGED
|
@@ -175,7 +175,7 @@ export async function autoDetectDeviceType(
|
|
|
175
175
|
|
|
176
176
|
// Get device info to check device type
|
|
177
177
|
const deviceInfo = await tcpApi.getInfo();
|
|
178
|
-
const { support } = await tcpApi.getDeviceCapabilities(
|
|
178
|
+
const { support } = await tcpApi.getDeviceCapabilities();
|
|
179
179
|
const channelNum = support?.channelNum ?? 1;
|
|
180
180
|
|
|
181
181
|
logger.log(`[AutoDetect] TCP connection successful. channelNum=${channelNum}`);
|
|
@@ -248,7 +248,7 @@ export async function autoDetectDeviceType(
|
|
|
248
248
|
await udpApi.login();
|
|
249
249
|
|
|
250
250
|
const deviceInfo = await udpApi.getInfo();
|
|
251
|
-
const { support } = await udpApi.getDeviceCapabilities(
|
|
251
|
+
const { support } = await udpApi.getDeviceCapabilities();
|
|
252
252
|
const channelNum = support?.channelNum ?? 1;
|
|
253
253
|
|
|
254
254
|
// Multi-focal devices can also be UDP (battery multi-focal cameras)
|
package/src/multifocal.ts
CHANGED
|
@@ -61,15 +61,14 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
61
61
|
cameraNativeMap = new Map<string, ReolinkNativeCamera | ReolinkNativeBatteryCamera>();
|
|
62
62
|
private channelToNativeIdMap = new Map<number, string>();
|
|
63
63
|
processing = false;
|
|
64
|
+
private initReinitTimeout: NodeJS.Timeout | undefined;
|
|
64
65
|
|
|
65
66
|
constructor(nativeId: string, plugin: ReolinkNativePlugin, transport: BaichuanTransport = 'tcp') {
|
|
66
67
|
super(nativeId);
|
|
67
68
|
this.plugin = plugin;
|
|
68
69
|
this.protocol = transport;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
await this.init();
|
|
72
|
-
}, 2000);
|
|
71
|
+
this.scheduleInit();
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
async reboot(): Promise<void> {
|
|
@@ -121,10 +120,31 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
121
120
|
}
|
|
122
121
|
|
|
123
122
|
async reinit(): Promise<void> {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
123
|
+
// Cancel any pending init/reinit
|
|
124
|
+
if (this.initReinitTimeout) {
|
|
125
|
+
clearTimeout(this.initReinitTimeout);
|
|
126
|
+
this.initReinitTimeout = undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Schedule reinit with debounce
|
|
130
|
+
this.scheduleInit(true);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private scheduleInit(isReinit: boolean = false): void {
|
|
134
|
+
// Cancel any pending init/reinit
|
|
135
|
+
if (this.initReinitTimeout) {
|
|
136
|
+
clearTimeout(this.initReinitTimeout);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this.initReinitTimeout = setTimeout(async () => {
|
|
140
|
+
const logger = this.getBaichuanLogger();
|
|
141
|
+
if (isReinit) {
|
|
142
|
+
logger.log('Reinitializing multi-focal device...');
|
|
143
|
+
await this.cleanupBaichuanApi();
|
|
144
|
+
}
|
|
145
|
+
await this.init();
|
|
146
|
+
this.initReinitTimeout = undefined;
|
|
147
|
+
}, isReinit ? 500 : 2000);
|
|
128
148
|
}
|
|
129
149
|
|
|
130
150
|
async init(): Promise<void> {
|
|
@@ -132,13 +152,29 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
132
152
|
try {
|
|
133
153
|
// Update UID setting visibility based on transport
|
|
134
154
|
this.storageSettings.settings.uid.hide = this.protocol === 'tcp';
|
|
135
|
-
|
|
155
|
+
|
|
156
|
+
logger.debug('Initializing: ensuring Baichuan client...');
|
|
136
157
|
await this.ensureBaichuanClient();
|
|
158
|
+
|
|
159
|
+
logger.debug('Initializing: updating device info...');
|
|
137
160
|
await this.updateDeviceInfo();
|
|
161
|
+
|
|
162
|
+
logger.debug('Initializing: subscribing to events...');
|
|
138
163
|
await this.subscribeToEvents();
|
|
164
|
+
|
|
165
|
+
logger.debug('Initializing: discovering devices...');
|
|
139
166
|
await this.discoverDevices(true);
|
|
167
|
+
|
|
168
|
+
logger.log('Initialization completed successfully');
|
|
140
169
|
} catch (e) {
|
|
141
170
|
logger.error('Failed to initialize multi-focal device', e);
|
|
171
|
+
// Log more details about the error
|
|
172
|
+
if (e instanceof Error) {
|
|
173
|
+
logger.error(`Error message: ${e.message}`);
|
|
174
|
+
logger.error(`Error stack: ${e.stack}`);
|
|
175
|
+
} else {
|
|
176
|
+
logger.error(`Error details: ${JSON.stringify(e)}`);
|
|
177
|
+
}
|
|
142
178
|
}
|
|
143
179
|
}
|
|
144
180
|
|
|
@@ -164,61 +200,76 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
164
200
|
const api = await this.ensureBaichuanClient();
|
|
165
201
|
const logger = this.getBaichuanLogger();
|
|
166
202
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
203
|
+
try {
|
|
204
|
+
const channelsInfo = await api.getNvrChannelsInfo();
|
|
205
|
+
const deviceInfo = await api.getInfo();
|
|
206
|
+
const { support } = await api.getDeviceCapabilities();
|
|
207
|
+
const channelNum = support?.channelNum ?? 1;
|
|
208
|
+
logger.log(`Sync entities from remote for ${channelNum} channels`);
|
|
209
|
+
const channels = Array.from({ length: channelNum }, (_, i) => i + 1);
|
|
210
|
+
|
|
211
|
+
logger.log(JSON.stringify({ channelsInfo, deviceInfo, channels }));
|
|
212
|
+
|
|
213
|
+
// for (const channel of channels) {
|
|
214
|
+
// try {
|
|
215
|
+
// const name = deviceInfo?.name || `Channel ${channel}`;
|
|
216
|
+
// const uid = deviceInfo?.uid;
|
|
217
|
+
// const isBattery = !!(abilities?.battery?.ver ?? 0);
|
|
218
|
+
|
|
219
|
+
// const nativeId = this.buildNativeId(channel, uid, isBattery);
|
|
220
|
+
// const interfaces = [ScryptedInterface.VideoCamera];
|
|
221
|
+
// if (isBattery) {
|
|
222
|
+
// interfaces.push(ScryptedInterface.Battery);
|
|
223
|
+
// }
|
|
224
|
+
// const type = abilities.supportDoorbellLight ? ScryptedDeviceType.Doorbell : ScryptedDeviceType.Camera;
|
|
225
|
+
|
|
226
|
+
// const device: Device = {
|
|
227
|
+
// nativeId,
|
|
228
|
+
// name,
|
|
229
|
+
// providerNativeId: this.nativeId,
|
|
230
|
+
// interfaces,
|
|
231
|
+
// type,
|
|
232
|
+
// info: {
|
|
233
|
+
// manufacturer: 'Reolink',
|
|
234
|
+
// model: channelInfo?.typeInfo,
|
|
235
|
+
// serialNumber: uid,
|
|
236
|
+
// }
|
|
237
|
+
// };
|
|
238
|
+
|
|
239
|
+
// this.channelToNativeIdMap.set(channel, nativeId);
|
|
240
|
+
|
|
241
|
+
// if (sdk.deviceManager.getNativeIds().includes(nativeId)) {
|
|
242
|
+
// continue;
|
|
243
|
+
// }
|
|
244
|
+
|
|
245
|
+
// if (this.discoveredDevices.has(nativeId)) {
|
|
246
|
+
// continue;
|
|
247
|
+
// }
|
|
248
|
+
|
|
249
|
+
// this.discoveredDevices.set(nativeId, {
|
|
250
|
+
// device,
|
|
251
|
+
// description: `${name} (Channel ${channel})`,
|
|
252
|
+
// rtspChannel: channel,
|
|
253
|
+
// deviceData: devicesData[channel],
|
|
254
|
+
// });
|
|
255
|
+
|
|
256
|
+
// logger.debug(`Discovered channel ${channel}: ${name}`);
|
|
257
|
+
// } catch (e: any) {
|
|
258
|
+
// logger.debug(`Error processing channel ${channel}: ${e?.message || String(e)}`);
|
|
259
|
+
// }
|
|
260
|
+
// }
|
|
261
|
+
|
|
262
|
+
// logger.log(`Channel discovery completed. ${JSON.stringify({ devicesData, channels })}`);
|
|
263
|
+
} catch (e) {
|
|
264
|
+
logger.error('Failed to sync entities from remote', e);
|
|
265
|
+
if (e instanceof Error) {
|
|
266
|
+
logger.error(`Error in syncEntitiesFromRemote: ${e.message}`);
|
|
267
|
+
logger.error(`Stack: ${e.stack}`);
|
|
268
|
+
} else {
|
|
269
|
+
logger.error(`Error details: ${JSON.stringify(e)}`);
|
|
218
270
|
}
|
|
271
|
+
throw e;
|
|
219
272
|
}
|
|
220
|
-
|
|
221
|
-
logger.log(`Channel discovery completed. ${JSON.stringify({ devicesData, channels })}`);
|
|
222
273
|
}
|
|
223
274
|
|
|
224
275
|
async discoverDevices(scan?: boolean): Promise<DiscoveredDevice[]> {
|
|
@@ -278,7 +329,7 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
278
329
|
device.storageSettings.values.password = this.storageSettings.values.password;
|
|
279
330
|
device.storageSettings.values.rtspChannel = entry.rtspChannel;
|
|
280
331
|
// Set multiFocalDevice reference through options (similar to how NVR does it)
|
|
281
|
-
(device
|
|
332
|
+
(device).options = { ...(device).options, multiFocalDevice: this, nvrDevice: undefined };
|
|
282
333
|
device.classes = objects;
|
|
283
334
|
device.presets = presets;
|
|
284
335
|
}
|
|
@@ -386,7 +437,7 @@ export class ReolinkNativeMultiFocalDevice extends BaseBaichuanClass implements
|
|
|
386
437
|
});
|
|
387
438
|
|
|
388
439
|
logger.log(`NVR diagnostics completed successfully.`);
|
|
389
|
-
|
|
440
|
+
|
|
390
441
|
// Print diagnostics to console
|
|
391
442
|
cgiApi.printNvrDiagnostics(diagnostics, this.console);
|
|
392
443
|
} catch (e) {
|
package/src/nvr.ts
CHANGED
|
@@ -69,14 +69,13 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
69
69
|
cameraNativeMap = new Map<string, ReolinkNativeCamera | ReolinkNativeBatteryCamera>();
|
|
70
70
|
private channelToNativeIdMap = new Map<number, string>();
|
|
71
71
|
processing = false;
|
|
72
|
+
private initReinitTimeout: NodeJS.Timeout | undefined;
|
|
72
73
|
|
|
73
74
|
constructor(nativeId: string, plugin: ReolinkNativePlugin) {
|
|
74
75
|
super(nativeId);
|
|
75
76
|
this.plugin = plugin;
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
await this.init();
|
|
79
|
-
}, 2000);
|
|
78
|
+
this.scheduleInit();
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
async reboot(): Promise<void> {
|
|
@@ -130,18 +129,40 @@ export class ReolinkNativeNvrDevice extends BaseBaichuanClass implements Setting
|
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
async reinit() {
|
|
133
|
-
//
|
|
134
|
-
if (this.
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
// Cancel any pending init/reinit
|
|
133
|
+
if (this.initReinitTimeout) {
|
|
134
|
+
clearTimeout(this.initReinitTimeout);
|
|
135
|
+
this.initReinitTimeout = undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Schedule reinit with debounce
|
|
139
|
+
this.scheduleInit(true);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private scheduleInit(isReinit: boolean = false): void {
|
|
143
|
+
// Cancel any pending init/reinit
|
|
144
|
+
if (this.initReinitTimeout) {
|
|
145
|
+
clearTimeout(this.initReinitTimeout);
|
|
140
146
|
}
|
|
141
|
-
this.nvrApi = undefined;
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
this.initReinitTimeout = setTimeout(async () => {
|
|
149
|
+
if (isReinit) {
|
|
150
|
+
// Cleanup CGI API
|
|
151
|
+
if (this.nvrApi) {
|
|
152
|
+
try {
|
|
153
|
+
await this.nvrApi.logout();
|
|
154
|
+
} catch {
|
|
155
|
+
// ignore
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
this.nvrApi = undefined;
|
|
159
|
+
|
|
160
|
+
// Cleanup Baichuan API (this handles all listeners and connection)
|
|
161
|
+
await super.cleanupBaichuanApi();
|
|
162
|
+
}
|
|
163
|
+
await this.init();
|
|
164
|
+
this.initReinitTimeout = undefined;
|
|
165
|
+
}, isReinit ? 500 : 2000);
|
|
145
166
|
}
|
|
146
167
|
|
|
147
168
|
async ensureClient(): Promise<ReolinkCgiApi> {
|