@apocaliss92/scrypted-reolink-native 0.3.5 → 0.3.7
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/.vscode/settings.json +1 -1
- package/dist/main.nodejs.js +1 -1
- package/dist/main.nodejs.js.LICENSE.txt +255 -0
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/baichuan-base.ts +226 -2
- package/src/camera.ts +14 -4
- package/src/stream-utils.ts +12 -0
|
@@ -1,3 +1,258 @@
|
|
|
1
1
|
/*! formdata-polyfill. MIT License. Jimmy Wärting <https://jimmy.warting.se/opensource> */
|
|
2
2
|
|
|
3
|
+
/*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
|
|
4
|
+
|
|
5
|
+
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
6
|
+
|
|
3
7
|
/*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @preserve
|
|
11
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
12
|
+
* All rights reserved. (MIT Licensed)
|
|
13
|
+
*
|
|
14
|
+
* cie94.ts - part of Image Quantization Library
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @preserve
|
|
19
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
20
|
+
* All rights reserved. (MIT Licensed)
|
|
21
|
+
*
|
|
22
|
+
* ciede2000.ts - part of Image Quantization Library
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @preserve
|
|
27
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
28
|
+
* All rights reserved. (MIT Licensed)
|
|
29
|
+
*
|
|
30
|
+
* cmetric.ts - part of Image Quantization Library
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @preserve
|
|
35
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
36
|
+
* All rights reserved. (MIT Licensed)
|
|
37
|
+
*
|
|
38
|
+
* common.ts - part of Image Quantization Library
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @preserve
|
|
43
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
44
|
+
* All rights reserved. (MIT Licensed)
|
|
45
|
+
*
|
|
46
|
+
* constants.ts - part of Image Quantization Library
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @preserve
|
|
51
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
52
|
+
* All rights reserved. (MIT Licensed)
|
|
53
|
+
*
|
|
54
|
+
* ditherErrorDiffusionArray.ts - part of Image Quantization Library
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @preserve
|
|
59
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
60
|
+
* All rights reserved. (MIT Licensed)
|
|
61
|
+
*
|
|
62
|
+
* euclidean.ts - part of Image Quantization Library
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @preserve
|
|
67
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
68
|
+
* All rights reserved. (MIT Licensed)
|
|
69
|
+
*
|
|
70
|
+
* helper.ts - part of Image Quantization Library
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @preserve
|
|
75
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
76
|
+
* All rights reserved. (MIT Licensed)
|
|
77
|
+
*
|
|
78
|
+
* hueStatistics.ts - part of Image Quantization Library
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @preserve
|
|
83
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
84
|
+
* All rights reserved. (MIT Licensed)
|
|
85
|
+
*
|
|
86
|
+
* iq.ts - Image Quantization Library
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @preserve
|
|
91
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
92
|
+
* All rights reserved. (MIT Licensed)
|
|
93
|
+
*
|
|
94
|
+
* lab2rgb.ts - part of Image Quantization Library
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* @preserve
|
|
99
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
100
|
+
* All rights reserved. (MIT Licensed)
|
|
101
|
+
*
|
|
102
|
+
* lab2xyz.ts - part of Image Quantization Library
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @preserve
|
|
107
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
108
|
+
* All rights reserved. (MIT Licensed)
|
|
109
|
+
*
|
|
110
|
+
* manhattanNeuQuant.ts - part of Image Quantization Library
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @preserve
|
|
115
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
116
|
+
* All rights reserved. (MIT Licensed)
|
|
117
|
+
*
|
|
118
|
+
* nearestColor.ts - part of Image Quantization Library
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @preserve
|
|
123
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
124
|
+
* All rights reserved. (MIT Licensed)
|
|
125
|
+
*
|
|
126
|
+
* palette.ts - part of Image Quantization Library
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @preserve
|
|
131
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
132
|
+
* All rights reserved. (MIT Licensed)
|
|
133
|
+
*
|
|
134
|
+
* pngQuant.ts - part of Image Quantization Library
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* @preserve
|
|
139
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
140
|
+
* All rights reserved. (MIT Licensed)
|
|
141
|
+
*
|
|
142
|
+
* point.ts - part of Image Quantization Library
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @preserve
|
|
147
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
148
|
+
* All rights reserved. (MIT Licensed)
|
|
149
|
+
*
|
|
150
|
+
* pointContainer.ts - part of Image Quantization Library
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @preserve
|
|
155
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
156
|
+
* All rights reserved. (MIT Licensed)
|
|
157
|
+
*
|
|
158
|
+
* rgb2hsl.ts - part of Image Quantization Library
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @preserve
|
|
163
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
164
|
+
* All rights reserved. (MIT Licensed)
|
|
165
|
+
*
|
|
166
|
+
* rgb2lab.ts - part of Image Quantization Library
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @preserve
|
|
171
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
172
|
+
* All rights reserved. (MIT Licensed)
|
|
173
|
+
*
|
|
174
|
+
* rgb2xyz.ts - part of Image Quantization Library
|
|
175
|
+
*/
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @preserve
|
|
179
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
180
|
+
* All rights reserved. (MIT Licensed)
|
|
181
|
+
*
|
|
182
|
+
* ssim.ts - part of Image Quantization Library
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @preserve
|
|
187
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
188
|
+
* All rights reserved. (MIT Licensed)
|
|
189
|
+
*
|
|
190
|
+
* wuQuant.ts - part of Image Quantization Library
|
|
191
|
+
*/
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* @preserve
|
|
195
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
196
|
+
* All rights reserved. (MIT Licensed)
|
|
197
|
+
*
|
|
198
|
+
* xyz2lab.ts - part of Image Quantization Library
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @preserve
|
|
203
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
204
|
+
* All rights reserved. (MIT Licensed)
|
|
205
|
+
*
|
|
206
|
+
* xyz2rgb.ts - part of Image Quantization Library
|
|
207
|
+
*/
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @preserve
|
|
211
|
+
* MIT License
|
|
212
|
+
*
|
|
213
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
214
|
+
*
|
|
215
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
216
|
+
* of this software and associated documentation files (the "Software"), to
|
|
217
|
+
* deal in the Software without restriction, including without limitation the
|
|
218
|
+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
219
|
+
* sell copies of the Software, and to permit persons to whom the Software is
|
|
220
|
+
* furnished to do so, subject to the following conditions:
|
|
221
|
+
*
|
|
222
|
+
* The above copyright notice and this permission notice shall be included in
|
|
223
|
+
* all copies or substantial portions of the Software.
|
|
224
|
+
*
|
|
225
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
226
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
227
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
228
|
+
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
229
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
230
|
+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
231
|
+
* IN THE SOFTWARE.
|
|
232
|
+
*
|
|
233
|
+
* riemersma.ts - part of Image Quantization Library
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* @preserve TypeScript port:
|
|
238
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
239
|
+
* All rights reserved. (MIT Licensed)
|
|
240
|
+
*
|
|
241
|
+
* colorHistogram.ts - part of Image Quantization Library
|
|
242
|
+
*/
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @preserve TypeScript port:
|
|
246
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
247
|
+
* All rights reserved. (MIT Licensed)
|
|
248
|
+
*
|
|
249
|
+
* neuquant.ts - part of Image Quantization Library
|
|
250
|
+
*/
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* @preserve TypeScript port:
|
|
254
|
+
* Copyright 2015-2018 Igor Bezkrovnyi
|
|
255
|
+
* All rights reserved. (MIT Licensed)
|
|
256
|
+
*
|
|
257
|
+
* rgbquant.ts - part of Image Quantization Library
|
|
258
|
+
*/
|
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/baichuan-base.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BaichuanClientOptions, ReolinkBaichuanApi, ReolinkSimpleEvent } from "@apocaliss92/reolink-baichuan-js" with { "resolution-mode": "import" };
|
|
2
2
|
import { ScryptedDeviceBase } from "@scrypted/sdk";
|
|
3
3
|
import { createBaichuanApi, type BaichuanTransport } from "./connect";
|
|
4
|
+
import { StreamManager } from "./stream-utils";
|
|
4
5
|
|
|
5
6
|
export interface BaichuanConnectionConfig {
|
|
6
7
|
host: string;
|
|
@@ -176,6 +177,11 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
176
177
|
private lastDisconnectTime: number = 0;
|
|
177
178
|
private readonly reconnectBackoffMs: number = 2000; // 2 seconds minimum between reconnects
|
|
178
179
|
private eventSubscriptionActive: boolean = false;
|
|
180
|
+
private lastEventTime: number = 0;
|
|
181
|
+
private pingInterval?: NodeJS.Timeout;
|
|
182
|
+
private autoRenewInterval?: NodeJS.Timeout;
|
|
183
|
+
private eventCheckInterval?: NodeJS.Timeout;
|
|
184
|
+
private consecutivePingFailures: number = 0;
|
|
179
185
|
|
|
180
186
|
/**
|
|
181
187
|
* Get the connection configuration for this instance
|
|
@@ -204,6 +210,12 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
204
210
|
*/
|
|
205
211
|
protected abstract getStreamClientInputs(): BaichuanConnectionConfig;
|
|
206
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Get StreamManager if available (optional, only for devices that support streaming)
|
|
215
|
+
* Override in subclasses that have a StreamManager
|
|
216
|
+
*/
|
|
217
|
+
protected getStreamManager?(): StreamManager | undefined;
|
|
218
|
+
|
|
207
219
|
/**
|
|
208
220
|
* Get a Baichuan logger instance with formatting and debug control
|
|
209
221
|
* This logger implements Console interface and can be used everywhere
|
|
@@ -293,6 +305,14 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
293
305
|
this.baichuanApi = api;
|
|
294
306
|
this.connectionTime = Date.now();
|
|
295
307
|
|
|
308
|
+
// Start ping and auto-renewal for TCP connections
|
|
309
|
+
if (this.transport === 'tcp') {
|
|
310
|
+
this.startConnectionMaintenance(api);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Start event check for all connections
|
|
314
|
+
this.startEventCheck(api);
|
|
315
|
+
|
|
296
316
|
return api;
|
|
297
317
|
}
|
|
298
318
|
catch (e) {
|
|
@@ -449,11 +469,206 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
449
469
|
// ignore
|
|
450
470
|
}
|
|
451
471
|
|
|
472
|
+
// Stop ping and auto-renewal intervals
|
|
473
|
+
this.stopConnectionMaintenance();
|
|
474
|
+
|
|
475
|
+
// Stop event check interval
|
|
476
|
+
this.stopEventCheck();
|
|
477
|
+
|
|
452
478
|
// Reset state
|
|
453
479
|
this.baichuanApi = undefined;
|
|
454
480
|
this.ensureClientPromise = undefined;
|
|
455
481
|
}
|
|
456
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Get all active Baichuan connections (main + stream clients)
|
|
485
|
+
*/
|
|
486
|
+
private getAllActiveConnections(): ReolinkBaichuanApi[] {
|
|
487
|
+
const connections: ReolinkBaichuanApi[] = [];
|
|
488
|
+
|
|
489
|
+
// Add main connection if exists and is valid
|
|
490
|
+
if (this.baichuanApi) {
|
|
491
|
+
const isConnected = this.baichuanApi.client.isSocketConnected();
|
|
492
|
+
const isLoggedIn = this.baichuanApi.client.loggedIn;
|
|
493
|
+
if (isConnected && isLoggedIn) {
|
|
494
|
+
connections.push(this.baichuanApi);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Add all stream clients that are valid
|
|
499
|
+
for (const streamClient of this.streamClients.values()) {
|
|
500
|
+
const isConnected = streamClient.client.isSocketConnected();
|
|
501
|
+
const isLoggedIn = streamClient.client.loggedIn;
|
|
502
|
+
if (isConnected && isLoggedIn) {
|
|
503
|
+
connections.push(streamClient);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return connections;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Start ping and auto-renewal maintenance for TCP connections
|
|
512
|
+
*/
|
|
513
|
+
private startConnectionMaintenance(api: ReolinkBaichuanApi): void {
|
|
514
|
+
const logger = this.getBaichuanLogger();
|
|
515
|
+
|
|
516
|
+
// Stop any existing intervals
|
|
517
|
+
this.stopConnectionMaintenance();
|
|
518
|
+
|
|
519
|
+
// Ping every 30 seconds to keep all connections alive
|
|
520
|
+
this.pingInterval = setInterval(async () => {
|
|
521
|
+
if (!this.baichuanApi || this.baichuanApi !== api) {
|
|
522
|
+
return; // Connection changed, stop this interval
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
// Get all active connections (main + stream clients)
|
|
527
|
+
const allConnections = this.getAllActiveConnections();
|
|
528
|
+
logger.debug(`Pinging ${allConnections.length} connections`);
|
|
529
|
+
|
|
530
|
+
if (allConnections.length === 0) {
|
|
531
|
+
this.consecutivePingFailures++;
|
|
532
|
+
logger.debug(`No active connections found, failures=${this.consecutivePingFailures}`);
|
|
533
|
+
|
|
534
|
+
if (this.consecutivePingFailures >= 3) {
|
|
535
|
+
logger.log('No active connections detected, renewing connection');
|
|
536
|
+
await this.cleanupBaichuanApi();
|
|
537
|
+
this.consecutivePingFailures = 0;
|
|
538
|
+
}
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Ping all connections using the specific ping method
|
|
543
|
+
const pingResults = await Promise.allSettled(
|
|
544
|
+
allConnections.map(async (conn) => {
|
|
545
|
+
try {
|
|
546
|
+
await conn.ping();
|
|
547
|
+
return { success: true, conn };
|
|
548
|
+
} catch (e) {
|
|
549
|
+
return { success: false, conn, error: e };
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
// Check results
|
|
555
|
+
const failedPings = pingResults.filter(r => r.status === 'rejected' || (r.status === 'fulfilled' && !r.value.success));
|
|
556
|
+
|
|
557
|
+
if (failedPings.length > 0) {
|
|
558
|
+
this.consecutivePingFailures++;
|
|
559
|
+
logger.debug(`Ping failed for ${failedPings.length}/${allConnections.length} connections, failures=${this.consecutivePingFailures}`);
|
|
560
|
+
|
|
561
|
+
if (this.consecutivePingFailures >= 3) {
|
|
562
|
+
logger.log(`Multiple ping failures detected (${failedPings.length} connections), renewing connection`);
|
|
563
|
+
await this.cleanupBaichuanApi();
|
|
564
|
+
this.consecutivePingFailures = 0;
|
|
565
|
+
}
|
|
566
|
+
} else {
|
|
567
|
+
// All pings successful, reset failure counter
|
|
568
|
+
this.consecutivePingFailures = 0;
|
|
569
|
+
if (allConnections.length > 1) {
|
|
570
|
+
logger.debug(`Ping successful for all ${allConnections.length} connections`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
} catch (e) {
|
|
574
|
+
logger.debug(`Error in ping check: ${e?.message || String(e)}`);
|
|
575
|
+
}
|
|
576
|
+
}, 30_000); // Every 30 seconds
|
|
577
|
+
|
|
578
|
+
// Auto-renewal every 5 minutes if no active streams
|
|
579
|
+
this.autoRenewInterval = setInterval(async () => {
|
|
580
|
+
if (!this.baichuanApi || this.baichuanApi !== api) {
|
|
581
|
+
return; // Connection changed, stop this interval
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
try {
|
|
585
|
+
// Check if there are active streams
|
|
586
|
+
const hasActiveStreams = this.getStreamManager?.()?.hasActiveStreams() ?? false;
|
|
587
|
+
|
|
588
|
+
if (!hasActiveStreams) {
|
|
589
|
+
logger.log('No active streams detected, renewing connection (auto-renewal)');
|
|
590
|
+
await this.cleanupBaichuanApi();
|
|
591
|
+
} else {
|
|
592
|
+
logger.debug('Active streams detected, skipping auto-renewal');
|
|
593
|
+
}
|
|
594
|
+
} catch (e) {
|
|
595
|
+
logger.debug(`Error in auto-renewal check: ${e?.message || String(e)}`);
|
|
596
|
+
}
|
|
597
|
+
}, 5 * 60_000); // Every 5 minutes
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Stop ping and auto-renewal maintenance
|
|
602
|
+
*/
|
|
603
|
+
private stopConnectionMaintenance(): void {
|
|
604
|
+
if (this.pingInterval) {
|
|
605
|
+
clearInterval(this.pingInterval);
|
|
606
|
+
this.pingInterval = undefined;
|
|
607
|
+
}
|
|
608
|
+
if (this.autoRenewInterval) {
|
|
609
|
+
clearInterval(this.autoRenewInterval);
|
|
610
|
+
this.autoRenewInterval = undefined;
|
|
611
|
+
}
|
|
612
|
+
this.consecutivePingFailures = 0;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Start event check to monitor if events are being received
|
|
617
|
+
*/
|
|
618
|
+
private startEventCheck(api: ReolinkBaichuanApi): void {
|
|
619
|
+
const logger = this.getBaichuanLogger();
|
|
620
|
+
|
|
621
|
+
// Stop any existing interval
|
|
622
|
+
this.stopEventCheck();
|
|
623
|
+
|
|
624
|
+
// Check every minute if events are being received
|
|
625
|
+
this.eventCheckInterval = setInterval(async () => {
|
|
626
|
+
if (!this.baichuanApi || this.baichuanApi !== api) {
|
|
627
|
+
return; // Connection changed, stop this interval
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Only check if event subscription is active
|
|
631
|
+
if (!this.eventSubscriptionActive) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
try {
|
|
636
|
+
const now = Date.now();
|
|
637
|
+
const timeSinceLastEvent = now - this.lastEventTime;
|
|
638
|
+
const fiveMinutesMs = 5 * 60 * 1000;
|
|
639
|
+
|
|
640
|
+
if (this.lastEventTime > 0 && timeSinceLastEvent > fiveMinutesMs) {
|
|
641
|
+
logger.log(`No events received in the last ${Math.round(timeSinceLastEvent / 60_000)} minutes, restarting event listener`);
|
|
642
|
+
// Restart event subscription
|
|
643
|
+
await this.unsubscribeFromEvents();
|
|
644
|
+
await this.subscribeToEvents();
|
|
645
|
+
} else if (this.lastEventTime === 0) {
|
|
646
|
+
// If lastEventTime is 0, it means we just subscribed but haven't received any events yet
|
|
647
|
+
// Wait a bit longer before considering it a problem
|
|
648
|
+
const timeSinceSubscription = now - (this.connectionTime || now);
|
|
649
|
+
if (timeSinceSubscription > fiveMinutesMs) {
|
|
650
|
+
logger.log(`No events received since subscription (${Math.round(timeSinceSubscription / 60_000)} minutes ago), restarting event listener`);
|
|
651
|
+
await this.unsubscribeFromEvents();
|
|
652
|
+
await this.subscribeToEvents();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
} catch (e) {
|
|
656
|
+
logger.debug(`Error in event check: ${e?.message || String(e)}`);
|
|
657
|
+
}
|
|
658
|
+
}, 5_000); // Check every minute
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Stop event check interval
|
|
663
|
+
*/
|
|
664
|
+
private stopEventCheck(): void {
|
|
665
|
+
if (this.eventCheckInterval) {
|
|
666
|
+
clearInterval(this.eventCheckInterval);
|
|
667
|
+
this.eventCheckInterval = undefined;
|
|
668
|
+
}
|
|
669
|
+
this.lastEventTime = 0;
|
|
670
|
+
}
|
|
671
|
+
|
|
457
672
|
/**
|
|
458
673
|
* Subscribe to Baichuan simple events
|
|
459
674
|
*/
|
|
@@ -493,10 +708,19 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
493
708
|
return;
|
|
494
709
|
}
|
|
495
710
|
|
|
496
|
-
// Subscribe to events
|
|
711
|
+
// Subscribe to events with wrapper to track last event time
|
|
497
712
|
try {
|
|
498
|
-
|
|
713
|
+
const originalHandler = callbacks.onSimpleEvent;
|
|
714
|
+
const wrappedHandler = (ev: ReolinkSimpleEvent) => {
|
|
715
|
+
// Update last event time
|
|
716
|
+
this.lastEventTime = Date.now();
|
|
717
|
+
// Call original handler
|
|
718
|
+
originalHandler(ev);
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
await api.onSimpleEvent(wrappedHandler);
|
|
499
722
|
this.eventSubscriptionActive = true;
|
|
723
|
+
this.lastEventTime = Date.now(); // Initialize on subscription
|
|
500
724
|
logger.log('Subscribed to Baichuan events');
|
|
501
725
|
}
|
|
502
726
|
catch (e) {
|
package/src/camera.ts
CHANGED
|
@@ -1512,6 +1512,13 @@ export class ReolinkCamera extends BaseBaichuanClass implements VideoCamera, Cam
|
|
|
1512
1512
|
return convertDebugLogsToApiOptions(socketDebugLogs);
|
|
1513
1513
|
}
|
|
1514
1514
|
|
|
1515
|
+
/**
|
|
1516
|
+
* Get StreamManager if available
|
|
1517
|
+
*/
|
|
1518
|
+
protected getStreamManager(): StreamManager | undefined {
|
|
1519
|
+
return this.streamManager;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1515
1522
|
/**
|
|
1516
1523
|
* Initialize or recreate the StreamManager, taking into account multifocal composite options.
|
|
1517
1524
|
*/
|
|
@@ -2070,7 +2077,7 @@ export class ReolinkCamera extends BaseBaichuanClass implements VideoCamera, Cam
|
|
|
2070
2077
|
async takePictureInternal(client: ReolinkBaichuanApi) {
|
|
2071
2078
|
const { rtspChannel, variantType } = this.storageSettings.values;
|
|
2072
2079
|
const logger = this.getBaichuanLogger();
|
|
2073
|
-
logger.
|
|
2080
|
+
logger.debug(`Taking new snapshot from camera: forceNewSnapshot=${this.forceNewSnapshot} channel=${rtspChannel} variant=${variantType}`);
|
|
2074
2081
|
|
|
2075
2082
|
const compositeOptions = this.isMultiFocal ? {
|
|
2076
2083
|
widerChannel: this.isOnNvr ? rtspChannel : undefined,
|
|
@@ -2124,7 +2131,7 @@ export class ReolinkCamera extends BaseBaichuanClass implements VideoCamera, Cam
|
|
|
2124
2131
|
}
|
|
2125
2132
|
|
|
2126
2133
|
if (!shouldTakeNewSnapshot && this.lastPicture) {
|
|
2127
|
-
logger.
|
|
2134
|
+
logger.debug(`Returning cached snapshot, taken at ${new Date(this.lastPicture.atMs).toLocaleString()}`);
|
|
2128
2135
|
return this.lastPicture.mo;
|
|
2129
2136
|
}
|
|
2130
2137
|
|
|
@@ -2781,15 +2788,18 @@ export class ReolinkCamera extends BaseBaichuanClass implements VideoCamera, Cam
|
|
|
2781
2788
|
|
|
2782
2789
|
if (sleepStatus.state === 'sleeping') {
|
|
2783
2790
|
if (!this.sleeping) {
|
|
2784
|
-
this.getBaichuanLogger().log(`Camera is sleeping
|
|
2791
|
+
this.getBaichuanLogger().log(`Camera is sleeping`);
|
|
2785
2792
|
this.sleeping = true;
|
|
2786
2793
|
}
|
|
2787
2794
|
} else if (sleepStatus.state === 'awake') {
|
|
2788
2795
|
// Camera is awake
|
|
2789
2796
|
const wasSleeping = this.sleeping;
|
|
2790
2797
|
if (wasSleeping) {
|
|
2791
|
-
this.getBaichuanLogger().log(`Camera
|
|
2798
|
+
this.getBaichuanLogger().log(`Camera is awake`);
|
|
2792
2799
|
this.sleeping = false;
|
|
2800
|
+
const client = await this.ensureClient();
|
|
2801
|
+
await client.wakeUp();
|
|
2802
|
+
await this.takePictureInternal(client);
|
|
2793
2803
|
}
|
|
2794
2804
|
|
|
2795
2805
|
if (wasSleeping) {
|
package/src/stream-utils.ts
CHANGED
|
@@ -456,6 +456,18 @@ export class StreamManager {
|
|
|
456
456
|
});
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Check if there are any active streams (servers that are listening).
|
|
461
|
+
*/
|
|
462
|
+
hasActiveStreams(): boolean {
|
|
463
|
+
for (const server of this.nativeRfcServers.values()) {
|
|
464
|
+
if (server?.server?.listening) {
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
|
|
459
471
|
/**
|
|
460
472
|
* Close all active stream servers.
|
|
461
473
|
* Useful when the main connection is reset and streams need to be recreated.
|