@apocaliss92/scrypted-reolink-native 0.4.8 → 0.4.9
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 +55 -3
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/baichuan-base.ts
CHANGED
|
@@ -199,6 +199,15 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
199
199
|
*/
|
|
200
200
|
protected abstract getConnectionCallbacks(): BaichuanConnectionCallbacks;
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* Check if this is an NVR/Hub device (multiple channels).
|
|
204
|
+
* Override in subclasses to return true for NVR devices.
|
|
205
|
+
* This is used for socket pooling to allocate separate sockets per channel.
|
|
206
|
+
*/
|
|
207
|
+
protected isNvrDevice(): boolean {
|
|
208
|
+
return false; // Default: standalone camera
|
|
209
|
+
}
|
|
210
|
+
|
|
202
211
|
/**
|
|
203
212
|
* Check if debug logging is enabled
|
|
204
213
|
*/
|
|
@@ -236,8 +245,16 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
236
245
|
* Ensure Baichuan client is connected and ready
|
|
237
246
|
*/
|
|
238
247
|
async ensureBaichuanClient(): Promise<ReolinkBaichuanApi> {
|
|
248
|
+
const logger = this.getBaichuanLogger();
|
|
249
|
+
const caller = new Error().stack?.split("\n")[2]?.trim() ?? "unknown";
|
|
250
|
+
|
|
239
251
|
// Prevent concurrent login storms - check promise first
|
|
240
|
-
if (this.ensureClientPromise)
|
|
252
|
+
if (this.ensureClientPromise) {
|
|
253
|
+
logger.debug(
|
|
254
|
+
`ensureBaichuanClient: waiting on existing promise (caller: ${caller})`,
|
|
255
|
+
);
|
|
256
|
+
return await this.ensureClientPromise;
|
|
257
|
+
}
|
|
241
258
|
|
|
242
259
|
// Reuse existing client if socket is still connected and logged in
|
|
243
260
|
// Check this AFTER checking the promise to avoid race conditions
|
|
@@ -247,18 +264,22 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
247
264
|
|
|
248
265
|
// Only reuse if both conditions are true
|
|
249
266
|
if (isConnected && isLoggedIn) {
|
|
267
|
+
logger.debug(
|
|
268
|
+
`ensureBaichuanClient: reusing existing client (caller: ${caller})`,
|
|
269
|
+
);
|
|
250
270
|
return this.baichuanApi;
|
|
251
271
|
}
|
|
252
272
|
|
|
253
273
|
// If socket is not connected or not logged in, cleanup the stale client
|
|
254
274
|
// This prevents leaking connections when the socket appears connected but isn't
|
|
255
|
-
const logger = this.getBaichuanLogger();
|
|
256
275
|
logger.log(
|
|
257
|
-
`Stale client detected: connected=${isConnected}, loggedIn=${isLoggedIn}, cleaning up`,
|
|
276
|
+
`Stale client detected: connected=${isConnected}, loggedIn=${isLoggedIn}, cleaning up (caller: ${caller})`,
|
|
258
277
|
);
|
|
259
278
|
await this.cleanupBaichuanApi();
|
|
260
279
|
}
|
|
261
280
|
|
|
281
|
+
logger.log(`ensureBaichuanClient: creating NEW client (caller: ${caller})`);
|
|
282
|
+
|
|
262
283
|
// IMPORTANT: Assign the promise BEFORE the backoff to prevent parallel reconnections
|
|
263
284
|
this.ensureClientPromise = (async () => {
|
|
264
285
|
// Apply backoff to avoid aggressive reconnection after disconnection
|
|
@@ -299,6 +320,10 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
299
320
|
|
|
300
321
|
await api.login();
|
|
301
322
|
|
|
323
|
+
// Set NVR flag BEFORE any streaming to ensure correct socket pooling
|
|
324
|
+
// NVR devices need separate sockets per channel
|
|
325
|
+
api.setIsNvr(this.isNvrDevice());
|
|
326
|
+
|
|
302
327
|
// Verify socket is connected before returning
|
|
303
328
|
if (!api.client.isSocketConnected()) {
|
|
304
329
|
throw new Error("Socket not connected after login");
|
|
@@ -657,8 +682,15 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
657
682
|
`No events received in the last ${Math.round(timeSinceLastEvent / 60_000)} minutes, restarting event listener`,
|
|
658
683
|
);
|
|
659
684
|
// Restart event subscription
|
|
685
|
+
logger.debug(
|
|
686
|
+
"Restarting event listener: calling unsubscribeFromEvents...",
|
|
687
|
+
);
|
|
660
688
|
await this.unsubscribeFromEvents(true);
|
|
689
|
+
logger.debug(
|
|
690
|
+
"Restarting event listener: calling subscribeToEvents...",
|
|
691
|
+
);
|
|
661
692
|
await this.subscribeToEvents(true);
|
|
693
|
+
logger.debug("Restarting event listener: done");
|
|
662
694
|
} else if (this.lastEventTime === 0) {
|
|
663
695
|
// If lastEventTime is 0, it means we just subscribed but haven't received any events yet
|
|
664
696
|
// Wait a bit longer before considering it a problem
|
|
@@ -667,8 +699,17 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
667
699
|
logger.log(
|
|
668
700
|
`No events received since subscription (${Math.round(timeSinceSubscription / 60_000)} minutes ago), restarting event listener`,
|
|
669
701
|
);
|
|
702
|
+
logger.debug(
|
|
703
|
+
"Restarting event listener (no events since sub): calling unsubscribeFromEvents...",
|
|
704
|
+
);
|
|
670
705
|
await this.unsubscribeFromEvents(true);
|
|
706
|
+
logger.debug(
|
|
707
|
+
"Restarting event listener (no events since sub): calling subscribeToEvents...",
|
|
708
|
+
);
|
|
671
709
|
await this.subscribeToEvents(true);
|
|
710
|
+
logger.debug(
|
|
711
|
+
"Restarting event listener (no events since sub): done",
|
|
712
|
+
);
|
|
672
713
|
}
|
|
673
714
|
}
|
|
674
715
|
} catch (e) {
|
|
@@ -694,6 +735,13 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
694
735
|
async subscribeToEvents(silent: boolean = false): Promise<void> {
|
|
695
736
|
const logger = this.getBaichuanLogger();
|
|
696
737
|
const callbacks = this.getConnectionCallbacks();
|
|
738
|
+
const existingClientInfo = this.baichuanApi
|
|
739
|
+
? `connected=${this.baichuanApi.client.isSocketConnected()}, loggedIn=${this.baichuanApi.client.loggedIn}`
|
|
740
|
+
: "no client";
|
|
741
|
+
|
|
742
|
+
logger.debug(
|
|
743
|
+
`subscribeToEvents() called: silent=${silent}, existingClient=[${existingClientInfo}], eventSubscriptionActive=${this.eventSubscriptionActive}`,
|
|
744
|
+
);
|
|
697
745
|
|
|
698
746
|
if (!callbacks.onSimpleEvent) {
|
|
699
747
|
return;
|
|
@@ -716,7 +764,11 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
716
764
|
await this.unsubscribeFromEvents(silent);
|
|
717
765
|
|
|
718
766
|
// Get Baichuan client connection
|
|
767
|
+
logger.debug("subscribeToEvents: calling ensureBaichuanClient...");
|
|
719
768
|
const api = await this.ensureBaichuanClient();
|
|
769
|
+
logger.debug(
|
|
770
|
+
`subscribeToEvents: ensureBaichuanClient returned, reused=${api === this.baichuanApi}`,
|
|
771
|
+
);
|
|
720
772
|
|
|
721
773
|
// Verify connection is ready
|
|
722
774
|
if (!api.client.isSocketConnected() || !api.client.loggedIn) {
|