@apocaliss92/nodelink-js 0.2.1 → 0.2.2
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/README.md +1 -1
- package/dist/{DiagnosticsTools-NUMCYEKQ.js → DiagnosticsTools-FNLGCOVA.js} +2 -2
- package/dist/{chunk-PCPEXOWB.js → chunk-MN7GUZT7.js} +750 -213
- package/dist/chunk-MN7GUZT7.js.map +1 -0
- package/dist/{chunk-YPU7RAEY.js → chunk-NLTB7GTA.js} +17 -1
- package/dist/{chunk-YPU7RAEY.js.map → chunk-NLTB7GTA.js.map} +1 -1
- package/dist/cli/rtsp-server.cjs +747 -210
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +771 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +235 -12
- package/dist/index.d.ts +249 -11
- package/dist/index.js +26 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-PCPEXOWB.js.map +0 -1
- /package/dist/{DiagnosticsTools-NUMCYEKQ.js.map → DiagnosticsTools-FNLGCOVA.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
34
34
|
function bcHeaderHasPayloadOffset(messageClass) {
|
|
35
35
|
return messageClass === BC_CLASS_MODERN_24 || messageClass === BC_CLASS_MODERN_24_ALT || messageClass === BC_CLASS_FILE_DOWNLOAD;
|
|
36
36
|
}
|
|
37
|
-
var BC_TCP_DEFAULT_PORT, BC_MAGIC, BC_MAGIC_REV, BC_XML_KEY, BC_AES_IV, BC_CLASS_LEGACY, BC_CLASS_MODERN_20, BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT, BC_CLASS_FILE_DOWNLOAD, BC_CMD_ID_LOGIN, BC_CMD_ID_LOGOUT, BC_CMD_ID_VIDEO, BC_CMD_ID_VIDEO_STOP, BC_CMD_ID_FILE_INFO_LIST_REPLAY, BC_CMD_ID_FILE_INFO_LIST_STOP, BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO, BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD, BC_CMD_ID_FILE_INFO_LIST_OPEN, BC_CMD_ID_FILE_INFO_LIST_GET, BC_CMD_ID_FILE_INFO_LIST_CLOSE, BC_CMD_ID_FIND_REC_VIDEO_OPEN, BC_CMD_ID_FIND_REC_VIDEO_GET, BC_CMD_ID_FIND_REC_VIDEO_CLOSE, BC_CMD_ID_COVER_PREVIEW, BC_CMD_ID_COVER_STANDALONE_458, BC_CMD_ID_COVER_STANDALONE_459, BC_CMD_ID_COVER_STANDALONE_460, BC_CMD_ID_COVER_STANDALONE_461, BC_CMD_ID_COVER_STANDALONE_462, BC_CMD_ID_COVER_RESPONSE, BC_CMD_ID_TALK_ABILITY, BC_CMD_ID_TALK_RESET, BC_CMD_ID_TALK_CONFIG, BC_CMD_ID_TALK, BC_CMD_ID_PTZ_CONTROL, BC_CMD_ID_PTZ_CONTROL_PRESET, BC_CMD_ID_GET_PTZ_PRESET, BC_CMD_ID_GET_PTZ_POSITION, BC_CMD_ID_GET_ZOOM_FOCUS, BC_CMD_ID_SET_ZOOM_FOCUS, BC_CMD_ID_GET_BATTERY_INFO_LIST, BC_CMD_ID_GET_BATTERY_INFO, BC_CMD_ID_UDP_KEEP_ALIVE, BC_CMD_ID_GET_PIR_INFO, BC_CMD_ID_SET_PIR_INFO, BC_CMD_ID_GET_MOTION_ALARM, BC_CMD_ID_SET_MOTION_ALARM, BC_CMD_ID_ALARM_EVENT_LIST, BC_CMD_ID_GET_AI_ALARM, BC_CMD_ID_SET_AI_ALARM, BC_CMD_ID_GET_AUDIO_ALARM, BC_CMD_ID_AUDIO_ALARM_PLAY, BC_CMD_ID_GET_WHITE_LED, BC_CMD_ID_SET_WHITE_LED_STATE, BC_CMD_ID_SET_WHITE_LED_TASK, BC_CMD_ID_FLOODLIGHT_STATUS_LIST, BC_CMD_ID_ABILITY_INFO, BC_CMD_ID_SUPPORT, BC_CMD_ID_PING, BC_CMD_ID_CHANNEL_INFO_ALL, BC_CMD_ID_GET_OSD_DATETIME, BC_CMD_ID_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, BC_CMD_ID_GET_RECORD, BC_CMD_ID_GET_HDD_INFO_LIST, BC_CMD_ID_GET_WIFI_SIGNAL, BC_CMD_ID_GET_WIFI, BC_CMD_ID_GET_ONLINE_USER_LIST, BC_CMD_ID_GET_DAY_RECORDS, BC_CMD_ID_GET_STREAM_INFO_LIST, BC_CMD_ID_GET_LED_STATE, BC_CMD_ID_GET_EMAIL_TASK, BC_CMD_ID_GET_AUDIO_TASK, BC_CMD_ID_GET_AUDIO_CFG, BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_TIMELAPSE_CFG, BC_CMD_ID_GET_AI_DENOISE, BC_CMD_ID_GET_KIT_AP_CFG, BC_CMD_ID_GET_REC_ENC_CFG, BC_CMD_ID_GET_ACCESS_USER_LIST, BC_CMD_ID_GET_SLEEP_STATE, BC_CMD_ID_GET_VIDEO_INPUT, BC_CMD_ID_GET_SYSTEM_GENERAL, BC_CMD_ID_GET_SUPPORT, BC_CMD_ID_GET_AI_CFG, BC_CMD_ID_SET_AI_CFG, BC_CMD_ID_GET_SIREN_STATUS, BC_CMD_ID_SET_AUDIO_TASK, BC_CMD_ID_CMD_123, BC_CMD_ID_CMD_209, BC_CMD_ID_CMD_265, BC_CMD_ID_CMD_440, BC_CMD_ID_PUSH_VIDEO_INPUT, BC_CMD_ID_PUSH_SERIAL, BC_CMD_ID_PUSH_NET_INFO, BC_CMD_ID_PUSH_DINGDONG_LIST, BC_CMD_ID_PUSH_SLEEP_STATUS, BC_CMD_ID_PUSH_COORDINATE_POINT_LIST;
|
|
37
|
+
var BC_TCP_DEFAULT_PORT, BC_MAGIC, BC_MAGIC_REV, BC_XML_KEY, BC_AES_IV, BC_CLASS_LEGACY, BC_CLASS_MODERN_20, BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT, BC_CLASS_FILE_DOWNLOAD, BC_CMD_ID_LOGIN, BC_CMD_ID_LOGOUT, BC_CMD_ID_VIDEO, BC_CMD_ID_VIDEO_STOP, BC_CMD_ID_FILE_INFO_LIST_REPLAY, BC_CMD_ID_FILE_INFO_LIST_STOP, BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO, BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD, BC_CMD_ID_FILE_INFO_LIST_OPEN, BC_CMD_ID_FILE_INFO_LIST_GET, BC_CMD_ID_FILE_INFO_LIST_CLOSE, BC_CMD_ID_FIND_REC_VIDEO_OPEN, BC_CMD_ID_FIND_REC_VIDEO_GET, BC_CMD_ID_FIND_REC_VIDEO_CLOSE, BC_CMD_ID_COVER_PREVIEW, BC_CMD_ID_COVER_STANDALONE_458, BC_CMD_ID_COVER_STANDALONE_459, BC_CMD_ID_COVER_STANDALONE_460, BC_CMD_ID_COVER_STANDALONE_461, BC_CMD_ID_COVER_STANDALONE_462, BC_CMD_ID_COVER_RESPONSE, BC_CMD_ID_TALK_ABILITY, BC_CMD_ID_TALK_RESET, BC_CMD_ID_TALK_CONFIG, BC_CMD_ID_TALK, BC_CMD_ID_PTZ_CONTROL, BC_CMD_ID_PTZ_CONTROL_PRESET, BC_CMD_ID_GET_PTZ_PRESET, BC_CMD_ID_GET_PTZ_POSITION, BC_CMD_ID_GET_ZOOM_FOCUS, BC_CMD_ID_SET_ZOOM_FOCUS, BC_CMD_ID_GET_BATTERY_INFO_LIST, BC_CMD_ID_GET_BATTERY_INFO, BC_CMD_ID_UDP_KEEP_ALIVE, BC_CMD_ID_GET_PIR_INFO, BC_CMD_ID_SET_PIR_INFO, BC_CMD_ID_GET_MOTION_ALARM, BC_CMD_ID_SET_MOTION_ALARM, BC_CMD_ID_ALARM_EVENT_LIST, BC_CMD_ID_GET_AI_ALARM, BC_CMD_ID_SET_AI_ALARM, BC_CMD_ID_GET_AUDIO_ALARM, BC_CMD_ID_AUDIO_ALARM_PLAY, BC_CMD_ID_GET_WHITE_LED, BC_CMD_ID_SET_WHITE_LED_STATE, BC_CMD_ID_SET_WHITE_LED_TASK, BC_CMD_ID_FLOODLIGHT_STATUS_LIST, BC_CMD_ID_ABILITY_INFO, BC_CMD_ID_SUPPORT, BC_CMD_ID_PING, BC_CMD_ID_CHANNEL_INFO_ALL, BC_CMD_ID_GET_OSD_DATETIME, BC_CMD_ID_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, BC_CMD_ID_GET_RECORD, BC_CMD_ID_GET_HDD_INFO_LIST, BC_CMD_ID_GET_WIFI_SIGNAL, BC_CMD_ID_GET_WIFI, BC_CMD_ID_GET_ONLINE_USER_LIST, BC_CMD_ID_GET_DAY_RECORDS, BC_CMD_ID_GET_STREAM_INFO_LIST, BC_CMD_ID_GET_LED_STATE, BC_CMD_ID_GET_EMAIL_TASK, BC_CMD_ID_GET_AUDIO_TASK, BC_CMD_ID_GET_AUDIO_CFG, BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_TIMELAPSE_CFG, BC_CMD_ID_GET_AI_DENOISE, BC_CMD_ID_GET_KIT_AP_CFG, BC_CMD_ID_GET_REC_ENC_CFG, BC_CMD_ID_GET_ACCESS_USER_LIST, BC_CMD_ID_GET_SLEEP_STATE, BC_CMD_ID_GET_VIDEO_INPUT, BC_CMD_ID_GET_SYSTEM_GENERAL, BC_CMD_ID_GET_SUPPORT, BC_CMD_ID_GET_AI_CFG, BC_CMD_ID_SET_AI_CFG, BC_CMD_ID_GET_SIREN_STATUS, BC_CMD_ID_SET_AUDIO_TASK, BC_CMD_ID_CMD_123, BC_CMD_ID_CMD_209, BC_CMD_ID_CMD_265, BC_CMD_ID_CMD_440, BC_CMD_ID_PUSH_VIDEO_INPUT, BC_CMD_ID_PUSH_SERIAL, BC_CMD_ID_PUSH_NET_INFO, BC_CMD_ID_PUSH_DINGDONG_LIST, BC_CMD_ID_PUSH_SLEEP_STATUS, BC_CMD_ID_PUSH_COORDINATE_POINT_LIST, BC_CMD_ID_DING_DONG_CTRL, BC_CMD_ID_GET_DING_DONG_LIST, BC_CMD_ID_DING_DONG_OPT, BC_CMD_ID_GET_DING_DONG_CFG, BC_CMD_ID_SET_DING_DONG_CFG, BC_CMD_ID_QUICK_REPLY_PLAY, BC_CMD_ID_GET_DING_DONG_SILENT, BC_CMD_ID_SET_DING_DONG_SILENT;
|
|
38
38
|
var init_constants = __esm({
|
|
39
39
|
"src/protocol/constants.ts"() {
|
|
40
40
|
"use strict";
|
|
@@ -147,6 +147,14 @@ var init_constants = __esm({
|
|
|
147
147
|
BC_CMD_ID_PUSH_DINGDONG_LIST = 484;
|
|
148
148
|
BC_CMD_ID_PUSH_SLEEP_STATUS = 623;
|
|
149
149
|
BC_CMD_ID_PUSH_COORDINATE_POINT_LIST = 723;
|
|
150
|
+
BC_CMD_ID_DING_DONG_CTRL = 483;
|
|
151
|
+
BC_CMD_ID_GET_DING_DONG_LIST = 484;
|
|
152
|
+
BC_CMD_ID_DING_DONG_OPT = 485;
|
|
153
|
+
BC_CMD_ID_GET_DING_DONG_CFG = 486;
|
|
154
|
+
BC_CMD_ID_SET_DING_DONG_CFG = 487;
|
|
155
|
+
BC_CMD_ID_QUICK_REPLY_PLAY = 349;
|
|
156
|
+
BC_CMD_ID_GET_DING_DONG_SILENT = 609;
|
|
157
|
+
BC_CMD_ID_SET_DING_DONG_SILENT = 610;
|
|
150
158
|
}
|
|
151
159
|
});
|
|
152
160
|
|
|
@@ -7331,6 +7339,8 @@ __export(index_exports, {
|
|
|
7331
7339
|
BC_CMD_ID_COVER_STANDALONE_460: () => BC_CMD_ID_COVER_STANDALONE_460,
|
|
7332
7340
|
BC_CMD_ID_COVER_STANDALONE_461: () => BC_CMD_ID_COVER_STANDALONE_461,
|
|
7333
7341
|
BC_CMD_ID_COVER_STANDALONE_462: () => BC_CMD_ID_COVER_STANDALONE_462,
|
|
7342
|
+
BC_CMD_ID_DING_DONG_CTRL: () => BC_CMD_ID_DING_DONG_CTRL,
|
|
7343
|
+
BC_CMD_ID_DING_DONG_OPT: () => BC_CMD_ID_DING_DONG_OPT,
|
|
7334
7344
|
BC_CMD_ID_FILE_INFO_LIST_CLOSE: () => BC_CMD_ID_FILE_INFO_LIST_CLOSE,
|
|
7335
7345
|
BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO: () => BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO,
|
|
7336
7346
|
BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD: () => BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,
|
|
@@ -7354,6 +7364,9 @@ __export(index_exports, {
|
|
|
7354
7364
|
BC_CMD_ID_GET_BATTERY_INFO_LIST: () => BC_CMD_ID_GET_BATTERY_INFO_LIST,
|
|
7355
7365
|
BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD: () => BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
|
|
7356
7366
|
BC_CMD_ID_GET_DAY_RECORDS: () => BC_CMD_ID_GET_DAY_RECORDS,
|
|
7367
|
+
BC_CMD_ID_GET_DING_DONG_CFG: () => BC_CMD_ID_GET_DING_DONG_CFG,
|
|
7368
|
+
BC_CMD_ID_GET_DING_DONG_LIST: () => BC_CMD_ID_GET_DING_DONG_LIST,
|
|
7369
|
+
BC_CMD_ID_GET_DING_DONG_SILENT: () => BC_CMD_ID_GET_DING_DONG_SILENT,
|
|
7357
7370
|
BC_CMD_ID_GET_EMAIL_TASK: () => BC_CMD_ID_GET_EMAIL_TASK,
|
|
7358
7371
|
BC_CMD_ID_GET_FTP_TASK: () => BC_CMD_ID_GET_FTP_TASK,
|
|
7359
7372
|
BC_CMD_ID_GET_HDD_INFO_LIST: () => BC_CMD_ID_GET_HDD_INFO_LIST,
|
|
@@ -7390,9 +7403,12 @@ __export(index_exports, {
|
|
|
7390
7403
|
BC_CMD_ID_PUSH_SERIAL: () => BC_CMD_ID_PUSH_SERIAL,
|
|
7391
7404
|
BC_CMD_ID_PUSH_SLEEP_STATUS: () => BC_CMD_ID_PUSH_SLEEP_STATUS,
|
|
7392
7405
|
BC_CMD_ID_PUSH_VIDEO_INPUT: () => BC_CMD_ID_PUSH_VIDEO_INPUT,
|
|
7406
|
+
BC_CMD_ID_QUICK_REPLY_PLAY: () => BC_CMD_ID_QUICK_REPLY_PLAY,
|
|
7393
7407
|
BC_CMD_ID_SET_AI_ALARM: () => BC_CMD_ID_SET_AI_ALARM,
|
|
7394
7408
|
BC_CMD_ID_SET_AI_CFG: () => BC_CMD_ID_SET_AI_CFG,
|
|
7395
7409
|
BC_CMD_ID_SET_AUDIO_TASK: () => BC_CMD_ID_SET_AUDIO_TASK,
|
|
7410
|
+
BC_CMD_ID_SET_DING_DONG_CFG: () => BC_CMD_ID_SET_DING_DONG_CFG,
|
|
7411
|
+
BC_CMD_ID_SET_DING_DONG_SILENT: () => BC_CMD_ID_SET_DING_DONG_SILENT,
|
|
7396
7412
|
BC_CMD_ID_SET_MOTION_ALARM: () => BC_CMD_ID_SET_MOTION_ALARM,
|
|
7397
7413
|
BC_CMD_ID_SET_PIR_INFO: () => BC_CMD_ID_SET_PIR_INFO,
|
|
7398
7414
|
BC_CMD_ID_SET_WHITE_LED_STATE: () => BC_CMD_ID_SET_WHITE_LED_STATE,
|
|
@@ -7509,6 +7525,7 @@ __export(index_exports, {
|
|
|
7509
7525
|
getGlobalLogger: () => getGlobalLogger,
|
|
7510
7526
|
getH265NalType: () => getH265NalType,
|
|
7511
7527
|
getMjpegContentType: () => getMjpegContentType,
|
|
7528
|
+
getSupportItemForChannel: () => getSupportItemForChannel,
|
|
7512
7529
|
getVideoStream: () => getVideoStream,
|
|
7513
7530
|
getVideoclipClientInfo: () => getVideoclipClientInfo,
|
|
7514
7531
|
getXmlText: () => getXmlText,
|
|
@@ -12833,6 +12850,8 @@ var NativeStreamFanout = class {
|
|
|
12833
12850
|
} finally {
|
|
12834
12851
|
for (const q of this.queues.values()) q.close();
|
|
12835
12852
|
this.queues.clear();
|
|
12853
|
+
this.running = false;
|
|
12854
|
+
this.opts.onEnd?.();
|
|
12836
12855
|
}
|
|
12837
12856
|
})();
|
|
12838
12857
|
}
|
|
@@ -13158,7 +13177,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13158
13177
|
this.logger.warn(
|
|
13159
13178
|
`[BaichuanRtspServer] Could not get stream metadata: ${error}`
|
|
13160
13179
|
);
|
|
13161
|
-
this.streamMetadata = { frameRate: 25
|
|
13180
|
+
this.streamMetadata = { frameRate: 25 };
|
|
13162
13181
|
this.setFlowVideoType("H264", "metadata unavailable");
|
|
13163
13182
|
}
|
|
13164
13183
|
this.clientConnectionServer = net2.createServer((socket) => {
|
|
@@ -13190,7 +13209,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13190
13209
|
*/
|
|
13191
13210
|
handleRtspConnection(socket) {
|
|
13192
13211
|
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
|
|
13193
|
-
|
|
13212
|
+
const connectTime = Date.now();
|
|
13213
|
+
this.logger.info(
|
|
13214
|
+
`[rebroadcast] client connected client=${clientId} path=${this.path} profile=${this.profile} channel=${this.channel}`
|
|
13215
|
+
);
|
|
13194
13216
|
let sessionId = "";
|
|
13195
13217
|
let buffer = Buffer.alloc(0);
|
|
13196
13218
|
let clientFfmpeg;
|
|
@@ -13198,6 +13220,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13198
13220
|
let clientUdpSocket = null;
|
|
13199
13221
|
let clientUdpSocketAudio = null;
|
|
13200
13222
|
const cleanup = () => {
|
|
13223
|
+
const sessionDurationMs = Date.now() - connectTime;
|
|
13224
|
+
const res = this.clientResources.get(clientId);
|
|
13225
|
+
const framesSent = res?.framesSent ?? 0;
|
|
13226
|
+
this.logger.info(
|
|
13227
|
+
`[rebroadcast] client disconnected client=${clientId} path=${this.path} profile=${this.profile} duration=${sessionDurationMs}ms frames=${framesSent}`
|
|
13228
|
+
);
|
|
13201
13229
|
this.removeClient(clientId);
|
|
13202
13230
|
this.authNonces.delete(clientId);
|
|
13203
13231
|
const resources = this.clientResources.get(clientId);
|
|
@@ -13339,7 +13367,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13339
13367
|
Public: "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS"
|
|
13340
13368
|
});
|
|
13341
13369
|
} else if (method === "DESCRIBE") {
|
|
13342
|
-
if (!this.
|
|
13370
|
+
if (!this.flow.getFmtp().hasParamSets && this.connectedClients.size === 0) {
|
|
13343
13371
|
try {
|
|
13344
13372
|
if (!this.nativeStreamActive) {
|
|
13345
13373
|
await this.startNativeStream();
|
|
@@ -13421,7 +13449,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13421
13449
|
seenFirstVideoKeyframe: false,
|
|
13422
13450
|
setupTrack0: false,
|
|
13423
13451
|
setupTrack1: false,
|
|
13424
|
-
isPlaying: false
|
|
13452
|
+
isPlaying: false,
|
|
13453
|
+
connectTime
|
|
13425
13454
|
});
|
|
13426
13455
|
} else {
|
|
13427
13456
|
existing.rtspSocket = socket;
|
|
@@ -13468,8 +13497,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13468
13497
|
if (resources) {
|
|
13469
13498
|
if (isTrack1) resources.setupTrack1 = true;
|
|
13470
13499
|
else resources.setupTrack0 = true;
|
|
13471
|
-
|
|
13472
|
-
|
|
13500
|
+
const transport2 = useTcpInterleaved ? "TCP/interleaved" : "UDP";
|
|
13501
|
+
const track = isTrack1 ? "track1(audio)" : "track0(video)";
|
|
13502
|
+
this.logger.info(
|
|
13503
|
+
`[rebroadcast] SETUP client=${clientId} ${track} transport=${transport2} session=${sessionId}`
|
|
13473
13504
|
);
|
|
13474
13505
|
}
|
|
13475
13506
|
}
|
|
@@ -13494,8 +13525,9 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13494
13525
|
const resources = this.clientResources.get(clientId);
|
|
13495
13526
|
if (resources) {
|
|
13496
13527
|
resources.isPlaying = true;
|
|
13497
|
-
|
|
13498
|
-
|
|
13528
|
+
const hasAudio = !!resources.setupTrack1;
|
|
13529
|
+
this.logger.info(
|
|
13530
|
+
`[rebroadcast] PLAY client=${clientId} path=${this.path} profile=${this.profile} channel=${this.channel} codec=${this.flow.sdpCodec} audio=${hasAudio} session=${sessionId}`
|
|
13499
13531
|
);
|
|
13500
13532
|
}
|
|
13501
13533
|
}
|
|
@@ -13504,6 +13536,9 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13504
13536
|
Range: "npt=0.000-"
|
|
13505
13537
|
});
|
|
13506
13538
|
} else if (method === "TEARDOWN") {
|
|
13539
|
+
this.logger.info(
|
|
13540
|
+
`[rebroadcast] TEARDOWN client=${clientId} session=${sessionId}`
|
|
13541
|
+
);
|
|
13507
13542
|
cleanup();
|
|
13508
13543
|
sendResponse(200, "OK", {
|
|
13509
13544
|
Session: sessionId
|
|
@@ -13598,7 +13633,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13598
13633
|
this.logger.warn(
|
|
13599
13634
|
`[BaichuanRtspServer] Could not fetch stream metadata: ${error}`
|
|
13600
13635
|
);
|
|
13601
|
-
streamMetadata = { frameRate: 25
|
|
13636
|
+
streamMetadata = { frameRate: 25 };
|
|
13602
13637
|
}
|
|
13603
13638
|
}
|
|
13604
13639
|
const ffmpegFormat = this.flow.ffmpegFormat;
|
|
@@ -14186,15 +14221,17 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14186
14221
|
`Sent ${frameCount} frames to client ${clientId} (frame size: ${frame.data.length} bytes)`
|
|
14187
14222
|
);
|
|
14188
14223
|
}
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14224
|
+
if (!useDirectRtp) {
|
|
14225
|
+
const now = Date.now();
|
|
14226
|
+
const timeSinceLastFrame = now - lastFrameTime;
|
|
14227
|
+
const waitTime = targetFrameInterval - timeSinceLastFrame;
|
|
14228
|
+
if (waitTime > 0) {
|
|
14229
|
+
await new Promise(
|
|
14230
|
+
(resolve) => setTimeout(resolve, Math.min(waitTime, targetFrameInterval * 2))
|
|
14231
|
+
);
|
|
14232
|
+
}
|
|
14233
|
+
lastFrameTime = Date.now();
|
|
14196
14234
|
}
|
|
14197
|
-
lastFrameTime = Date.now();
|
|
14198
14235
|
if (useDirectRtp) {
|
|
14199
14236
|
const videoType = frame.videoType ?? this.flow.videoType;
|
|
14200
14237
|
const normalizedVideoData = videoType === "H264" ? convertToAnnexB(frame.data) : convertToAnnexB2(frame.data);
|
|
@@ -14267,6 +14304,11 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14267
14304
|
}
|
|
14268
14305
|
if (!firstVideoWriteLogged) {
|
|
14269
14306
|
firstVideoWriteLogged = true;
|
|
14307
|
+
const clientConnectTime = resources?.connectTime ?? Date.now();
|
|
14308
|
+
const ttffMs = Date.now() - clientConnectTime;
|
|
14309
|
+
this.logger.info(
|
|
14310
|
+
`[rebroadcast] first keyframe \u2192 client client=${clientId} codec=${videoType} ttff=${ttffMs}ms`
|
|
14311
|
+
);
|
|
14270
14312
|
if (rtspDebug) {
|
|
14271
14313
|
const headHex = frame.data.subarray(0, 16).toString("hex");
|
|
14272
14314
|
rtspDebugLog(
|
|
@@ -14274,6 +14316,9 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14274
14316
|
);
|
|
14275
14317
|
}
|
|
14276
14318
|
}
|
|
14319
|
+
if (resources) {
|
|
14320
|
+
resources.framesSent = (resources.framesSent ?? 0) + 1;
|
|
14321
|
+
}
|
|
14277
14322
|
sendVideoAccessUnit(videoType, normalizedVideoData, true);
|
|
14278
14323
|
} else {
|
|
14279
14324
|
try {
|
|
@@ -14358,8 +14403,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14358
14403
|
this.firstAudioPromise = new Promise((resolve) => {
|
|
14359
14404
|
this.firstAudioResolve = resolve;
|
|
14360
14405
|
});
|
|
14361
|
-
this.
|
|
14362
|
-
`
|
|
14406
|
+
this.logger.info(
|
|
14407
|
+
`[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
|
|
14363
14408
|
);
|
|
14364
14409
|
await this.flow.startKeepAlive(this.api);
|
|
14365
14410
|
this.nativeFanout = new NativeStreamFanout({
|
|
@@ -14402,6 +14447,23 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14402
14447
|
this.logger.warn(
|
|
14403
14448
|
`[BaichuanRtspServer] Shared native stream error: ${error}`
|
|
14404
14449
|
);
|
|
14450
|
+
},
|
|
14451
|
+
onEnd: () => {
|
|
14452
|
+
if (!this.nativeStreamActive) return;
|
|
14453
|
+
this.nativeStreamActive = false;
|
|
14454
|
+
this.firstFrameReceived = false;
|
|
14455
|
+
this.firstFramePromise = null;
|
|
14456
|
+
this.firstFrameResolve = null;
|
|
14457
|
+
this.nativeFanout = null;
|
|
14458
|
+
this.logger.info(
|
|
14459
|
+
`[rebroadcast] native stream ended (camera sleeping or connection lost) profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
|
|
14460
|
+
);
|
|
14461
|
+
if (this.connectedClients.size > 0) {
|
|
14462
|
+
this.logger.info(
|
|
14463
|
+
`[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
|
|
14464
|
+
);
|
|
14465
|
+
setImmediate(() => void this.startNativeStream());
|
|
14466
|
+
}
|
|
14405
14467
|
}
|
|
14406
14468
|
});
|
|
14407
14469
|
this.nativeFanout.start();
|
|
@@ -14440,7 +14502,9 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14440
14502
|
if (!this.nativeStreamActive) {
|
|
14441
14503
|
return;
|
|
14442
14504
|
}
|
|
14443
|
-
this.
|
|
14505
|
+
this.logger.info(
|
|
14506
|
+
`[rebroadcast] native stream stopping profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
|
|
14507
|
+
);
|
|
14444
14508
|
this.flow.stopKeepAlive();
|
|
14445
14509
|
this.clearNoClientAutoStopTimer();
|
|
14446
14510
|
this.nativeStreamActive = false;
|
|
@@ -14474,9 +14538,6 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14474
14538
|
if (this.connectedClients.has(clientId)) {
|
|
14475
14539
|
this.connectedClients.delete(clientId);
|
|
14476
14540
|
this.emit("clientDisconnected", clientId);
|
|
14477
|
-
this.logger.info(
|
|
14478
|
-
`[BaichuanRtspServer] RTSP client disconnected: ${clientId}`
|
|
14479
|
-
);
|
|
14480
14541
|
if (this.connectedClients.size === 0) {
|
|
14481
14542
|
void this.stopNativeStream();
|
|
14482
14543
|
}
|
|
@@ -14951,10 +15012,12 @@ function parseSupportXml(xml) {
|
|
|
14951
15012
|
}
|
|
14952
15013
|
function getSupportItemForChannel(support, channel) {
|
|
14953
15014
|
if (!support?.items?.length) return void 0;
|
|
14954
|
-
const
|
|
15015
|
+
const candidates = support.items.filter((i) => i.chnID === channel);
|
|
15016
|
+
if (!candidates.length) return void 0;
|
|
15017
|
+
const score = (item) => {
|
|
14955
15018
|
const anyItem = item;
|
|
14956
|
-
let
|
|
14957
|
-
if (anyItem.name == null)
|
|
15019
|
+
let result = 0;
|
|
15020
|
+
if (anyItem.name == null) result += 100;
|
|
14958
15021
|
const capabilityKeys = [
|
|
14959
15022
|
"ptzType",
|
|
14960
15023
|
"ptzControl",
|
|
@@ -14966,20 +15029,17 @@ function getSupportItemForChannel(support, channel) {
|
|
|
14966
15029
|
"motion",
|
|
14967
15030
|
"encCtrl",
|
|
14968
15031
|
"newIspCfg",
|
|
14969
|
-
"remoteAbility"
|
|
15032
|
+
"remoteAbility",
|
|
15033
|
+
"aitype",
|
|
15034
|
+
"videoClip",
|
|
15035
|
+
"snap"
|
|
14970
15036
|
];
|
|
14971
15037
|
for (const k of capabilityKeys) {
|
|
14972
|
-
if (anyItem[k] !== void 0)
|
|
15038
|
+
if (anyItem[k] !== void 0) result += 3;
|
|
14973
15039
|
}
|
|
14974
|
-
|
|
14975
|
-
return score;
|
|
14976
|
-
};
|
|
14977
|
-
const pickBest = (chnId) => {
|
|
14978
|
-
const candidates = support.items.filter((i) => i.chnID === chnId);
|
|
14979
|
-
if (!candidates.length) return void 0;
|
|
14980
|
-
return candidates.slice().sort((a, b) => scoreSupportItem(b) - scoreSupportItem(a))[0];
|
|
15040
|
+
return result;
|
|
14981
15041
|
};
|
|
14982
|
-
return
|
|
15042
|
+
return candidates.sort((a, b) => score(b) - score(a))[0];
|
|
14983
15043
|
}
|
|
14984
15044
|
function computeDeviceCapabilities(params) {
|
|
14985
15045
|
const { channel } = params;
|
|
@@ -15011,6 +15071,7 @@ function computeDeviceCapabilities(params) {
|
|
|
15011
15071
|
flat,
|
|
15012
15072
|
/white\s*led|whiteLed|flood\s*light|floodlight/i
|
|
15013
15073
|
);
|
|
15074
|
+
const hasSirenFromSupport = supportItem ? isTruthyNumberLike(supportItem.audioVersion) : false;
|
|
15014
15075
|
const hasSirenFromAbilities = abilitiesHasAny(
|
|
15015
15076
|
flat,
|
|
15016
15077
|
/audio\s*alarm|audioAlarm|siren|pushAlarn|audioPlay/i
|
|
@@ -15023,6 +15084,9 @@ function computeDeviceCapabilities(params) {
|
|
|
15023
15084
|
const hasPirFromSupport = supportItem ? isTruthyNumberLike(supportItem.rfCfg) || isTruthyNumberLike(supportItem.newRfCfg) || isTruthyNumberLike(supportItem.rfVersion) || isTruthyNumberLike(supportItem.battery) : false;
|
|
15024
15085
|
const hasAutotrackingFromSupport = supportItem ? isTruthyNumberLike(supportItem.autoPt) || isTruthyNumberLike(supportItem.smartAI) : false;
|
|
15025
15086
|
const hasAutotrackingFromAbilities = abilitiesHasAny(flat, /smartTrack/i);
|
|
15087
|
+
const hasBattery = hasBatteryFromSupport || hasBatteryFromAbilities;
|
|
15088
|
+
const isDoorbell = isDoorbellFromSupport || isDoorbellFromModel;
|
|
15089
|
+
const hasWirelessChimeFromAbilities = abilitiesHasAny(flat, /dingDong|dingdong/i);
|
|
15026
15090
|
const hasPan = hasPanTiltFromSupport || hasPanTiltFromAbilities;
|
|
15027
15091
|
const hasTilt = hasPanTiltFromSupport || hasPanTiltFromAbilities;
|
|
15028
15092
|
const hasZoom = hasZoomFromSupport || hasZoomFromAbilities;
|
|
@@ -15038,14 +15102,15 @@ function computeDeviceCapabilities(params) {
|
|
|
15038
15102
|
hasZoom: finalHasZoom,
|
|
15039
15103
|
hasPresets: finalHasPresets,
|
|
15040
15104
|
hasPtz: ptzDisabledBySupport ? false : hasPtzFromSupport || finalHasPan || finalHasTilt || finalHasZoom || finalHasPresets,
|
|
15041
|
-
hasBattery
|
|
15105
|
+
hasBattery,
|
|
15042
15106
|
hasIntercom: hasIntercomFromSupport,
|
|
15043
|
-
hasSiren: hasSirenFromAbilities,
|
|
15107
|
+
hasSiren: hasSirenFromSupport || hasSirenFromAbilities,
|
|
15044
15108
|
// lightType >= 2 indicates controllable white LED / floodlight (1 = IR only)
|
|
15045
15109
|
hasFloodlight: Number.isFinite(lightType) ? lightType >= 2 : hasFloodlightFromAbilities,
|
|
15046
15110
|
hasPir: hasPirFromAbilities || hasPirFromSupport,
|
|
15047
|
-
isDoorbell
|
|
15048
|
-
hasAutotracking: hasAutotrackingFromSupport || hasAutotrackingFromAbilities
|
|
15111
|
+
isDoorbell,
|
|
15112
|
+
hasAutotracking: ptzDisabledBySupport ? false : hasAutotrackingFromSupport || hasAutotrackingFromAbilities,
|
|
15113
|
+
hasWirelessChime: isDoorbell || hasWirelessChimeFromAbilities
|
|
15049
15114
|
};
|
|
15050
15115
|
if (ptzMode !== void 0) result.ptzMode = ptzMode;
|
|
15051
15116
|
return result;
|
|
@@ -16710,6 +16775,162 @@ var discoverDeviceUidViaBaichuanGetP2p = async (params) => {
|
|
|
16710
16775
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|
|
16711
16776
|
init_recordingFileName();
|
|
16712
16777
|
|
|
16778
|
+
// src/reolink/baichuan/utils/chime.ts
|
|
16779
|
+
init_xml();
|
|
16780
|
+
var buildDingDongGetParamsXml = (chimeId) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16781
|
+
<body>
|
|
16782
|
+
<dingdongDeviceOpt version="1.1">
|
|
16783
|
+
<id>${chimeId}</id>
|
|
16784
|
+
<opt>getParam</opt>
|
|
16785
|
+
</dingdongDeviceOpt>
|
|
16786
|
+
</body>`;
|
|
16787
|
+
var buildDingDongSetParamsXml = (chimeId, params) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16788
|
+
<body>
|
|
16789
|
+
<dingdongDeviceOpt version="1.1">
|
|
16790
|
+
<opt>setParam</opt>
|
|
16791
|
+
<id>${chimeId}</id>
|
|
16792
|
+
${params.volLevel !== void 0 ? `<volLevel>${params.volLevel}</volLevel>` : ""}
|
|
16793
|
+
${params.ledState !== void 0 ? `<ledState>${params.ledState}</ledState>` : ""}
|
|
16794
|
+
${params.name !== void 0 ? `<name>${params.name}</name>` : ""}
|
|
16795
|
+
</dingdongDeviceOpt>
|
|
16796
|
+
</body>`;
|
|
16797
|
+
var buildDingDongRingXml = (chimeId, musicId) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16798
|
+
<body>
|
|
16799
|
+
<dingdongDeviceOpt version="1.1">
|
|
16800
|
+
<id>${chimeId}</id>
|
|
16801
|
+
<opt>ringWithMusic</opt>
|
|
16802
|
+
<musicId>${musicId}</musicId>
|
|
16803
|
+
</dingdongDeviceOpt>
|
|
16804
|
+
</body>`;
|
|
16805
|
+
var buildSetDingDongCfgXml = (chimeId, eventType, state, musicId) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16806
|
+
<body>
|
|
16807
|
+
<dingdongCfg version="1.1">
|
|
16808
|
+
<deviceCfg>
|
|
16809
|
+
<id>${chimeId}</id>
|
|
16810
|
+
<alarminCfg>
|
|
16811
|
+
<valid>${state}</valid>
|
|
16812
|
+
<musicId>${musicId}</musicId>
|
|
16813
|
+
<type>${eventType}</type>
|
|
16814
|
+
</alarminCfg>
|
|
16815
|
+
</deviceCfg>
|
|
16816
|
+
</dingdongCfg>
|
|
16817
|
+
</body>`;
|
|
16818
|
+
var buildGetDingDongCtrlXml = () => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16819
|
+
<body>
|
|
16820
|
+
<dingdongCtrl version="1.1">
|
|
16821
|
+
<opt>machineStateGet</opt>
|
|
16822
|
+
</dingdongCtrl>
|
|
16823
|
+
</body>`;
|
|
16824
|
+
var buildSetDingDongCtrlXml = (chimeType, enabled, time) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16825
|
+
<body>
|
|
16826
|
+
<dingdongCtrl version="1.1">
|
|
16827
|
+
<opt>machineStateSet</opt>
|
|
16828
|
+
<type>${chimeType}</type>
|
|
16829
|
+
<bopen>${enabled}</bopen>
|
|
16830
|
+
<bsave>1</bsave>
|
|
16831
|
+
<time>${time}</time>
|
|
16832
|
+
</dingdongCtrl>
|
|
16833
|
+
</body>`;
|
|
16834
|
+
var buildQuickReplyPlayXml = (channel, fileId) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16835
|
+
<body>
|
|
16836
|
+
<audioFileInfo version="1.1">
|
|
16837
|
+
<channelId>${channel}</channelId>
|
|
16838
|
+
<id>${fileId}</id>
|
|
16839
|
+
<timeout>0</timeout>
|
|
16840
|
+
</audioFileInfo>
|
|
16841
|
+
</body>`;
|
|
16842
|
+
var parseDingDongListFromXml = (xml) => {
|
|
16843
|
+
const devices = [];
|
|
16844
|
+
const blocks = getXmlBlocks(xml, "dingdongDeviceInfo");
|
|
16845
|
+
for (const block of blocks) {
|
|
16846
|
+
const idText = getXmlText(block, "deviceId") ?? getXmlText(block, "id");
|
|
16847
|
+
const name = getXmlText(block, "deviceName") ?? getXmlText(block, "name") ?? "";
|
|
16848
|
+
const netStateText = getXmlText(block, "netState") ?? getXmlText(block, "netstate");
|
|
16849
|
+
if (idText === void 0) continue;
|
|
16850
|
+
const id = Number(idText);
|
|
16851
|
+
if (!Number.isFinite(id)) continue;
|
|
16852
|
+
devices.push({
|
|
16853
|
+
id,
|
|
16854
|
+
name,
|
|
16855
|
+
netState: netStateText !== void 0 ? Number(netStateText) : 0
|
|
16856
|
+
});
|
|
16857
|
+
}
|
|
16858
|
+
return devices;
|
|
16859
|
+
};
|
|
16860
|
+
var parseDingDongParamsFromXml = (xml) => {
|
|
16861
|
+
const name = getXmlText(xml, "name");
|
|
16862
|
+
const volLevelText = getXmlText(xml, "volLevel");
|
|
16863
|
+
const ledStateText = getXmlText(xml, "ledState");
|
|
16864
|
+
const result = {};
|
|
16865
|
+
if (name !== void 0) result.name = name;
|
|
16866
|
+
if (volLevelText !== void 0) {
|
|
16867
|
+
const n = Number(volLevelText);
|
|
16868
|
+
if (Number.isFinite(n)) result.volLevel = n;
|
|
16869
|
+
}
|
|
16870
|
+
if (ledStateText !== void 0) {
|
|
16871
|
+
const n = Number(ledStateText);
|
|
16872
|
+
if (Number.isFinite(n)) result.ledState = n;
|
|
16873
|
+
}
|
|
16874
|
+
return result;
|
|
16875
|
+
};
|
|
16876
|
+
var parseDingDongCfgFromXml = (xml) => {
|
|
16877
|
+
const configs = [];
|
|
16878
|
+
const deviceBlocks = getXmlBlocks(xml, "deviceCfg");
|
|
16879
|
+
for (const deviceBlock of deviceBlocks) {
|
|
16880
|
+
const idText = getXmlText(deviceBlock, "ringId") ?? getXmlText(deviceBlock, "id");
|
|
16881
|
+
if (idText === void 0) continue;
|
|
16882
|
+
const id = Number(idText);
|
|
16883
|
+
if (!Number.isFinite(id)) continue;
|
|
16884
|
+
const typeMap = {};
|
|
16885
|
+
const alarmBlocks = getXmlBlocks(deviceBlock, "alarminCfg");
|
|
16886
|
+
for (const alarmBlock of alarmBlocks) {
|
|
16887
|
+
const type = getXmlText(alarmBlock, "type");
|
|
16888
|
+
if (!type) continue;
|
|
16889
|
+
const validText = getXmlText(alarmBlock, "switch") ?? getXmlText(alarmBlock, "valid");
|
|
16890
|
+
const musicIdText = getXmlText(alarmBlock, "musicId");
|
|
16891
|
+
typeMap[type] = {
|
|
16892
|
+
valid: validText !== void 0 ? Number(validText) : 0,
|
|
16893
|
+
musicId: musicIdText !== void 0 ? Number(musicIdText) : 0
|
|
16894
|
+
};
|
|
16895
|
+
}
|
|
16896
|
+
configs.push({ id, type: typeMap });
|
|
16897
|
+
}
|
|
16898
|
+
return configs;
|
|
16899
|
+
};
|
|
16900
|
+
var parseHardwiredChimeFromXml = (xml) => {
|
|
16901
|
+
const type = getXmlText(xml, "type") ?? "";
|
|
16902
|
+
const bopenText = getXmlText(xml, "bopen") ?? getXmlText(xml, "enable");
|
|
16903
|
+
const timeText = getXmlText(xml, "time");
|
|
16904
|
+
return {
|
|
16905
|
+
type,
|
|
16906
|
+
enabled: bopenText === "1",
|
|
16907
|
+
time: timeText !== void 0 ? Number(timeText) : 0
|
|
16908
|
+
};
|
|
16909
|
+
};
|
|
16910
|
+
var buildGetDingDongSilentXml = (chimeId) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16911
|
+
<body>
|
|
16912
|
+
<dingdongSilentMode version="1.1">
|
|
16913
|
+
<id>${chimeId}</id>
|
|
16914
|
+
</dingdongSilentMode>
|
|
16915
|
+
</body>`;
|
|
16916
|
+
var buildSetDingDongSilentXml = (chimeId, time) => `<?xml version="1.0" encoding="UTF-8" ?>
|
|
16917
|
+
<body>
|
|
16918
|
+
<dingdongSilentMode version="1.1">
|
|
16919
|
+
<id>${chimeId}</id>
|
|
16920
|
+
<time>${time}</time>
|
|
16921
|
+
<type>63</type>
|
|
16922
|
+
</dingdongSilentMode>
|
|
16923
|
+
</body>`;
|
|
16924
|
+
var parseWirelessChimeSilentFromXml = (xml, chimeId) => {
|
|
16925
|
+
const timeText = getXmlText(xml, "time");
|
|
16926
|
+
const time = timeText !== void 0 ? Number(timeText) : 0;
|
|
16927
|
+
return {
|
|
16928
|
+
id: chimeId,
|
|
16929
|
+
time,
|
|
16930
|
+
active: time === 0
|
|
16931
|
+
};
|
|
16932
|
+
};
|
|
16933
|
+
|
|
16713
16934
|
// src/reolink/baichuan/utils/eventsGetEvents.ts
|
|
16714
16935
|
init_xml();
|
|
16715
16936
|
var parseAiTypeToken = (aiTypeRaw) => {
|
|
@@ -17022,6 +17243,11 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17022
17243
|
host;
|
|
17023
17244
|
username;
|
|
17024
17245
|
password;
|
|
17246
|
+
/**
|
|
17247
|
+
* Set to `true` after `close()` is called.
|
|
17248
|
+
* Once closed, the API instance should not be reused.
|
|
17249
|
+
*/
|
|
17250
|
+
_closed = false;
|
|
17025
17251
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
17026
17252
|
// SOCKET POOL - Tag-based socket management
|
|
17027
17253
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -17051,10 +17277,194 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17051
17277
|
get client() {
|
|
17052
17278
|
const entry = this.socketPool.get("general");
|
|
17053
17279
|
if (!entry) {
|
|
17280
|
+
if (this._closed) {
|
|
17281
|
+
throw new Error(
|
|
17282
|
+
"[ReolinkBaichuanApi] API has been closed \u2014 create a new instance to reconnect"
|
|
17283
|
+
);
|
|
17284
|
+
}
|
|
17054
17285
|
throw new Error("[ReolinkBaichuanApi] General socket not initialized");
|
|
17055
17286
|
}
|
|
17056
17287
|
return entry.client;
|
|
17057
17288
|
}
|
|
17289
|
+
/**
|
|
17290
|
+
* `true` after `close()` has been called. A closed API should not be reused;
|
|
17291
|
+
* the consumer should create a new instance.
|
|
17292
|
+
*/
|
|
17293
|
+
get isClosed() {
|
|
17294
|
+
return this._closed;
|
|
17295
|
+
}
|
|
17296
|
+
/**
|
|
17297
|
+
* `true` when the API is usable: not closed, general socket exists, socket
|
|
17298
|
+
* is connected and the client is logged in.
|
|
17299
|
+
*
|
|
17300
|
+
* This is the recommended way for consumers to check whether the API is
|
|
17301
|
+
* still valid before issuing commands, instead of directly accessing
|
|
17302
|
+
* `api.client.isSocketConnected()` / `api.client.loggedIn` (which throws
|
|
17303
|
+
* if the socket pool was already destroyed).
|
|
17304
|
+
*/
|
|
17305
|
+
get isReady() {
|
|
17306
|
+
if (this._closed) return false;
|
|
17307
|
+
const entry = this.socketPool.get("general");
|
|
17308
|
+
if (!entry) return false;
|
|
17309
|
+
try {
|
|
17310
|
+
return entry.client.isSocketConnected() && entry.client.loggedIn;
|
|
17311
|
+
} catch {
|
|
17312
|
+
return false;
|
|
17313
|
+
}
|
|
17314
|
+
}
|
|
17315
|
+
/** Promise tracking an in-flight reconnection from `ensureConnected()`. */
|
|
17316
|
+
_ensureConnectedPromise;
|
|
17317
|
+
/**
|
|
17318
|
+
* Ensure the "general" socket is connected and logged in.
|
|
17319
|
+
* If the socket is disconnected or the pool entry was destroyed, a new
|
|
17320
|
+
* general socket is created, logged in, and all event/push/guard listeners
|
|
17321
|
+
* are re-attached automatically.
|
|
17322
|
+
*
|
|
17323
|
+
* This is a **no-op** when the API is already {@link isReady}.
|
|
17324
|
+
*
|
|
17325
|
+
* @throws If `close()` was called — the API is permanently closed and a new
|
|
17326
|
+
* instance must be created.
|
|
17327
|
+
*/
|
|
17328
|
+
async ensureConnected() {
|
|
17329
|
+
if (this._closed) {
|
|
17330
|
+
throw new Error(
|
|
17331
|
+
"[ReolinkBaichuanApi] API has been closed \u2014 create a new instance to reconnect"
|
|
17332
|
+
);
|
|
17333
|
+
}
|
|
17334
|
+
if (this.isReady) return;
|
|
17335
|
+
if (this._ensureConnectedPromise) {
|
|
17336
|
+
return this._ensureConnectedPromise;
|
|
17337
|
+
}
|
|
17338
|
+
this._ensureConnectedPromise = this.reconnectGeneralSocket();
|
|
17339
|
+
try {
|
|
17340
|
+
await this._ensureConnectedPromise;
|
|
17341
|
+
} finally {
|
|
17342
|
+
this._ensureConnectedPromise = void 0;
|
|
17343
|
+
}
|
|
17344
|
+
}
|
|
17345
|
+
/**
|
|
17346
|
+
* Internal: destroy the current general socket (if any), create a new one,
|
|
17347
|
+
* login, and re-attach all listeners.
|
|
17348
|
+
*/
|
|
17349
|
+
async reconnectGeneralSocket() {
|
|
17350
|
+
const oldEntry = this.socketPool.get("general");
|
|
17351
|
+
if (oldEntry) {
|
|
17352
|
+
oldEntry.client.removeAllListeners();
|
|
17353
|
+
if (oldEntry.idleCloseTimer) clearTimeout(oldEntry.idleCloseTimer);
|
|
17354
|
+
if (oldEntry.generalPermitRelease) {
|
|
17355
|
+
try {
|
|
17356
|
+
oldEntry.generalPermitRelease();
|
|
17357
|
+
} catch {
|
|
17358
|
+
}
|
|
17359
|
+
}
|
|
17360
|
+
this.socketPool.delete("general");
|
|
17361
|
+
try {
|
|
17362
|
+
await oldEntry.client.close({ reason: "reconnect", skipLogout: true });
|
|
17363
|
+
} catch {
|
|
17364
|
+
}
|
|
17365
|
+
}
|
|
17366
|
+
const newClient = new BaichuanClient(this.clientOptions);
|
|
17367
|
+
this.socketPool.set("general", {
|
|
17368
|
+
client: newClient,
|
|
17369
|
+
refCount: 1,
|
|
17370
|
+
// general socket is always "in use"
|
|
17371
|
+
createdAt: Date.now(),
|
|
17372
|
+
lastUsedAt: Date.now(),
|
|
17373
|
+
idleCloseTimer: void 0,
|
|
17374
|
+
generalPermitRelease: void 0
|
|
17375
|
+
});
|
|
17376
|
+
this.setupGeneralClientListeners();
|
|
17377
|
+
await this.client.login();
|
|
17378
|
+
this.logger.log?.(
|
|
17379
|
+
"[ReolinkBaichuanApi] General socket reconnected successfully"
|
|
17380
|
+
);
|
|
17381
|
+
if (this.simpleEventListeners.size > 0) {
|
|
17382
|
+
this.simpleEventSubscribed = false;
|
|
17383
|
+
this.simpleEventWatchdogRecoveryAttempts = 0;
|
|
17384
|
+
this.simpleEventWatchdogLastRecoveryAt = 0;
|
|
17385
|
+
try {
|
|
17386
|
+
await this.ensureSimpleEventSubscribed();
|
|
17387
|
+
this.simpleEventLastReceivedAt = Date.now();
|
|
17388
|
+
this.logger.log?.(
|
|
17389
|
+
`[ReolinkBaichuanApi] Events re-subscribed after reconnection (listeners=${this.simpleEventListeners.size})`
|
|
17390
|
+
);
|
|
17391
|
+
} catch (e) {
|
|
17392
|
+
(this.logger.debug ?? this.logger.log).call(
|
|
17393
|
+
this.logger,
|
|
17394
|
+
`[ReolinkBaichuanApi] Event re-subscribe after reconnection failed, watchdog will retry`,
|
|
17395
|
+
formatErrorForLog(e)
|
|
17396
|
+
);
|
|
17397
|
+
}
|
|
17398
|
+
}
|
|
17399
|
+
}
|
|
17400
|
+
/**
|
|
17401
|
+
* Attach event, push, channelInfo, and guard listeners to the current
|
|
17402
|
+
* "general" client. Called from the constructor and from
|
|
17403
|
+
* {@link reconnectGeneralSocket}.
|
|
17404
|
+
*/
|
|
17405
|
+
setupGeneralClientListeners() {
|
|
17406
|
+
const client = this.client;
|
|
17407
|
+
client.on("event", (event) => {
|
|
17408
|
+
const mapped = mapToSimpleEvent(event);
|
|
17409
|
+
if (!mapped) return;
|
|
17410
|
+
this.dispatchSimpleEvent(mapped);
|
|
17411
|
+
});
|
|
17412
|
+
client.on("channelInfo", (xml) => {
|
|
17413
|
+
try {
|
|
17414
|
+
this.parseAndStoreChannelInfo(xml);
|
|
17415
|
+
} catch (e) {
|
|
17416
|
+
this.logger.warn?.(
|
|
17417
|
+
"[ReolinkBaichuanApi] Error parsing channel info from push",
|
|
17418
|
+
formatErrorForLog(e)
|
|
17419
|
+
);
|
|
17420
|
+
}
|
|
17421
|
+
});
|
|
17422
|
+
client.on("push", (frame) => {
|
|
17423
|
+
const cmdId = frame.header.cmdId;
|
|
17424
|
+
if (cmdId !== BC_CMD_ID_PUSH_VIDEO_INPUT && cmdId !== BC_CMD_ID_PUSH_SERIAL && cmdId !== BC_CMD_ID_PUSH_NET_INFO && cmdId !== BC_CMD_ID_PUSH_DINGDONG_LIST && cmdId !== BC_CMD_ID_PUSH_SLEEP_STATUS && cmdId !== BC_CMD_ID_PUSH_COORDINATE_POINT_LIST) {
|
|
17425
|
+
return;
|
|
17426
|
+
}
|
|
17427
|
+
try {
|
|
17428
|
+
if (frame.body.length === 0) return;
|
|
17429
|
+
const xml = client.tryDecryptXml(
|
|
17430
|
+
frame.body,
|
|
17431
|
+
frame.header.channelId,
|
|
17432
|
+
client.enc
|
|
17433
|
+
);
|
|
17434
|
+
if (!xml || !xml.startsWith("<?xml")) return;
|
|
17435
|
+
this.parseAndStoreSettingsPush(cmdId, xml, frame.header.channelId);
|
|
17436
|
+
} catch (e) {
|
|
17437
|
+
this.logger.debug?.(
|
|
17438
|
+
"[ReolinkBaichuanApi] Error parsing settings push",
|
|
17439
|
+
formatErrorForLog(e)
|
|
17440
|
+
);
|
|
17441
|
+
}
|
|
17442
|
+
});
|
|
17443
|
+
if (this.rebootAfterDisconnectionsPerMinute > 0) {
|
|
17444
|
+
client.on("close", () => {
|
|
17445
|
+
try {
|
|
17446
|
+
void this.maybeRebootOnDisconnectStorm();
|
|
17447
|
+
} catch {
|
|
17448
|
+
}
|
|
17449
|
+
});
|
|
17450
|
+
}
|
|
17451
|
+
if (this.rebootAfterConsecutiveEconnreset > 0) {
|
|
17452
|
+
client.on("close", () => {
|
|
17453
|
+
try {
|
|
17454
|
+
void this.maybeRebootOnEconnresetStorm();
|
|
17455
|
+
} catch {
|
|
17456
|
+
}
|
|
17457
|
+
});
|
|
17458
|
+
}
|
|
17459
|
+
if (!this.sessionGuardIntervalTimer) {
|
|
17460
|
+
client.once("push", () => {
|
|
17461
|
+
void this.logActiveSessionsOnStartup();
|
|
17462
|
+
this.sessionGuardIntervalTimer = setInterval(() => {
|
|
17463
|
+
void this.maybeRebootOnTooManySessions();
|
|
17464
|
+
}, 6e4);
|
|
17465
|
+
});
|
|
17466
|
+
}
|
|
17467
|
+
}
|
|
17058
17468
|
/**
|
|
17059
17469
|
* Cached camera UID. May be initially undefined if not provided in the constructor.
|
|
17060
17470
|
* Will be lazily populated on demand when needed (e.g. for recordings).
|
|
@@ -17995,42 +18405,6 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17995
18405
|
logger: this.logger,
|
|
17996
18406
|
debugConfig: generalClient.getDebugConfig?.()
|
|
17997
18407
|
});
|
|
17998
|
-
this.client.on("event", (event) => {
|
|
17999
|
-
const mapped = mapToSimpleEvent(event);
|
|
18000
|
-
if (!mapped) return;
|
|
18001
|
-
this.dispatchSimpleEvent(mapped);
|
|
18002
|
-
});
|
|
18003
|
-
this.client.on("channelInfo", (xml) => {
|
|
18004
|
-
try {
|
|
18005
|
-
this.parseAndStoreChannelInfo(xml);
|
|
18006
|
-
} catch (e) {
|
|
18007
|
-
this.logger.warn?.(
|
|
18008
|
-
"[ReolinkBaichuanApi] Error parsing channel info from push",
|
|
18009
|
-
formatErrorForLog(e)
|
|
18010
|
-
);
|
|
18011
|
-
}
|
|
18012
|
-
});
|
|
18013
|
-
this.client.on("push", (frame) => {
|
|
18014
|
-
const cmdId = frame.header.cmdId;
|
|
18015
|
-
if (cmdId !== BC_CMD_ID_PUSH_VIDEO_INPUT && cmdId !== BC_CMD_ID_PUSH_SERIAL && cmdId !== BC_CMD_ID_PUSH_NET_INFO && cmdId !== BC_CMD_ID_PUSH_DINGDONG_LIST && cmdId !== BC_CMD_ID_PUSH_SLEEP_STATUS && cmdId !== BC_CMD_ID_PUSH_COORDINATE_POINT_LIST) {
|
|
18016
|
-
return;
|
|
18017
|
-
}
|
|
18018
|
-
try {
|
|
18019
|
-
if (frame.body.length === 0) return;
|
|
18020
|
-
const xml = this.client.tryDecryptXml(
|
|
18021
|
-
frame.body,
|
|
18022
|
-
frame.header.channelId,
|
|
18023
|
-
this.client.enc
|
|
18024
|
-
);
|
|
18025
|
-
if (!xml || !xml.startsWith("<?xml")) return;
|
|
18026
|
-
this.parseAndStoreSettingsPush(cmdId, xml, frame.header.channelId);
|
|
18027
|
-
} catch (e) {
|
|
18028
|
-
this.logger.debug?.(
|
|
18029
|
-
"[ReolinkBaichuanApi] Error parsing settings push",
|
|
18030
|
-
formatErrorForLog(e)
|
|
18031
|
-
);
|
|
18032
|
-
}
|
|
18033
|
-
});
|
|
18034
18408
|
const maxSessions = opts.maxDedicatedSessionsBeforeReboot;
|
|
18035
18409
|
if (typeof maxSessions === "number" && Number.isFinite(maxSessions) && maxSessions > 0) {
|
|
18036
18410
|
this.maxDedicatedSessionsBeforeReboot = Math.floor(maxSessions);
|
|
@@ -18039,32 +18413,11 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18039
18413
|
if (typeof disconnectThreshold === "number" && Number.isFinite(disconnectThreshold)) {
|
|
18040
18414
|
this.rebootAfterDisconnectionsPerMinute = Math.floor(disconnectThreshold);
|
|
18041
18415
|
}
|
|
18042
|
-
if (this.rebootAfterDisconnectionsPerMinute > 0) {
|
|
18043
|
-
this.client.on("close", () => {
|
|
18044
|
-
try {
|
|
18045
|
-
void this.maybeRebootOnDisconnectStorm();
|
|
18046
|
-
} catch {
|
|
18047
|
-
}
|
|
18048
|
-
});
|
|
18049
|
-
}
|
|
18050
18416
|
const econnresetThreshold = opts.rebootAfterConsecutiveEconnreset;
|
|
18051
18417
|
if (typeof econnresetThreshold === "number" && Number.isFinite(econnresetThreshold)) {
|
|
18052
18418
|
this.rebootAfterConsecutiveEconnreset = Math.floor(econnresetThreshold);
|
|
18053
18419
|
}
|
|
18054
|
-
|
|
18055
|
-
this.client.on("close", () => {
|
|
18056
|
-
try {
|
|
18057
|
-
void this.maybeRebootOnEconnresetStorm();
|
|
18058
|
-
} catch {
|
|
18059
|
-
}
|
|
18060
|
-
});
|
|
18061
|
-
}
|
|
18062
|
-
this.client.once("push", () => {
|
|
18063
|
-
void this.logActiveSessionsOnStartup();
|
|
18064
|
-
this.sessionGuardIntervalTimer = setInterval(() => {
|
|
18065
|
-
void this.maybeRebootOnTooManySessions();
|
|
18066
|
-
}, 6e4);
|
|
18067
|
-
});
|
|
18420
|
+
this.setupGeneralClientListeners();
|
|
18068
18421
|
}
|
|
18069
18422
|
/**
|
|
18070
18423
|
* CGI forward: fetch RTSP URL for a channel via `GetRtspUrl`.
|
|
@@ -18895,6 +19248,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18895
19248
|
);
|
|
18896
19249
|
}
|
|
18897
19250
|
async close(options) {
|
|
19251
|
+
if (this._closed) return;
|
|
19252
|
+
this._closed = true;
|
|
18898
19253
|
if (this.sessionGuardIntervalTimer) {
|
|
18899
19254
|
clearInterval(this.sessionGuardIntervalTimer);
|
|
18900
19255
|
this.sessionGuardIntervalTimer = void 0;
|
|
@@ -18957,7 +19312,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18957
19312
|
}
|
|
18958
19313
|
async handleSendXml400(params, frame, retry) {
|
|
18959
19314
|
const emptyBody = frame.body.length === 0;
|
|
18960
|
-
const emptyBody400Msg = "Baichuan request failed (responseCode 400, empty body). Possible causes:
|
|
19315
|
+
const emptyBody400Msg = "Baichuan request failed (responseCode 400, empty body). Possible causes: expired session, invalid username/password, or unsupported command on NVR/Hub.";
|
|
18961
19316
|
if (this.isSendXmlFailFast400(params, frame.body.length)) {
|
|
18962
19317
|
throw new Error(emptyBody400Msg);
|
|
18963
19318
|
}
|
|
@@ -19473,11 +19828,50 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19473
19828
|
* Minimal per-channel inventory for NVR-connected devices.
|
|
19474
19829
|
*
|
|
19475
19830
|
* Intended to be fast: avoids AI/abilities and returns only the common identity + battery hints.
|
|
19831
|
+
*
|
|
19832
|
+
* @param options.source - Data source for the channel list (default: `"cgi"`):
|
|
19833
|
+
* - `"cgi"`: Uses HTTP `GetChannelstatus` — returns the channel list immediately,
|
|
19834
|
+
* no dependency on async push messages. Recommended for first-call discovery.
|
|
19835
|
+
* - `"baichuan"`: Uses the cmd_id 145 push cache populated when the NVR sends channel
|
|
19836
|
+
* info after login + event subscription. This push is *asynchronous*: if it has not
|
|
19837
|
+
* arrived yet, the result will have zero channels. Callers must retry (nvr.ts does this
|
|
19838
|
+
* with a 1-second loop). Note: explicitly requesting cmd_id 145 is not supported.
|
|
19476
19839
|
*/
|
|
19477
19840
|
async getNvrChannelsSummary(options) {
|
|
19478
|
-
const source = options?.source ?? "
|
|
19479
|
-
|
|
19480
|
-
const
|
|
19841
|
+
const source = options?.source ?? "cgi";
|
|
19842
|
+
let channels;
|
|
19843
|
+
const cgiStatusByChannel = /* @__PURE__ */ new Map();
|
|
19844
|
+
if (options?.channels?.length) {
|
|
19845
|
+
channels = options.channels.map((c) => Number(c)).filter((n) => Number.isFinite(n));
|
|
19846
|
+
} else if (source === "cgi") {
|
|
19847
|
+
try {
|
|
19848
|
+
const { channels: cgiChannels, channelsResponse } = await this.cgiApi.getChannels();
|
|
19849
|
+
const status = channelsResponse?.[0]?.value?.status ?? [];
|
|
19850
|
+
for (const s of status) {
|
|
19851
|
+
const ch = Number(s?.channel);
|
|
19852
|
+
if (!Number.isFinite(ch)) continue;
|
|
19853
|
+
cgiStatusByChannel.set(ch, {
|
|
19854
|
+
...s.name != null ? { name: s.name } : {},
|
|
19855
|
+
...s.uid != null ? { uid: s.uid } : {},
|
|
19856
|
+
sleeping: s.sleep === 1
|
|
19857
|
+
});
|
|
19858
|
+
}
|
|
19859
|
+
channels = cgiChannels;
|
|
19860
|
+
this.logger.debug?.(
|
|
19861
|
+
`[ReolinkBaichuanApi] getNvrChannelsSummary: CGI found ${channels.length} channel(s): [${channels.join(", ")}]`
|
|
19862
|
+
);
|
|
19863
|
+
} catch (e) {
|
|
19864
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
19865
|
+
this.logger.warn?.(
|
|
19866
|
+
`[ReolinkBaichuanApi] getNvrChannelsSummary: CGI GetChannelstatus failed (${msg}), returning empty`
|
|
19867
|
+
);
|
|
19868
|
+
channels = [];
|
|
19869
|
+
}
|
|
19870
|
+
} else {
|
|
19871
|
+
const pushInfo2 = this.getChannelInfoFromPushCache();
|
|
19872
|
+
channels = Array.from(pushInfo2.keys()).map((c) => Number(c)).filter((n) => Number.isFinite(n));
|
|
19873
|
+
}
|
|
19874
|
+
channels = channels.sort((a, b) => a - b);
|
|
19481
19875
|
const support = await this.getSupportInfo().catch(() => {
|
|
19482
19876
|
this.logger.error?.(
|
|
19483
19877
|
"[ReolinkBaichuanApi] getNvrChannelsSummary: failed to get support info"
|
|
@@ -19507,7 +19901,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19507
19901
|
);
|
|
19508
19902
|
}
|
|
19509
19903
|
}
|
|
19510
|
-
const cacheKey =
|
|
19904
|
+
const cacheKey = `${source}:${channels.join(",")}`;
|
|
19511
19905
|
const cached = this.nvrChannelsSummaryCache.get(cacheKey);
|
|
19512
19906
|
if (cached) {
|
|
19513
19907
|
return {
|
|
@@ -19528,8 +19922,10 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19528
19922
|
} catch {
|
|
19529
19923
|
}
|
|
19530
19924
|
}
|
|
19925
|
+
const pushInfo = this.getChannelInfoFromPushCache();
|
|
19531
19926
|
const devices = channels.map((channel) => {
|
|
19532
|
-
const
|
|
19927
|
+
const pushCached = pushInfo.get(channel);
|
|
19928
|
+
const cgiStatus = cgiStatusByChannel.get(channel);
|
|
19533
19929
|
const info = infoPerChannel.get(channel);
|
|
19534
19930
|
const networkInfo = networkInfoPerChannel.get(channel);
|
|
19535
19931
|
const isBattery = isBatteryByChannel.get(channel) ?? false;
|
|
@@ -19537,6 +19933,9 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19537
19933
|
const isDoorbell = (isDoorbellByChannel.get(channel) ?? false) || /doorbell/i.test(model);
|
|
19538
19934
|
const normalizedModel = model ? model.trim() : void 0;
|
|
19539
19935
|
const isMultifocal = normalizedModel ? isDualLenseModel(normalizedModel) : false;
|
|
19936
|
+
const name = pushCached?.name || cgiStatus?.name || "";
|
|
19937
|
+
const uid = pushCached?.uid || cgiStatus?.uid || "";
|
|
19938
|
+
const sleeping = pushCached?.sleeping ?? cgiStatus?.sleeping;
|
|
19540
19939
|
return {
|
|
19541
19940
|
channel,
|
|
19542
19941
|
isBattery,
|
|
@@ -19546,19 +19945,19 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19546
19945
|
...networkInfo?.ip ? { ip: networkInfo.ip } : {},
|
|
19547
19946
|
...networkInfo?.mac ? { mac: networkInfo.mac } : {},
|
|
19548
19947
|
...networkInfo?.activeLink ? { activeLink: networkInfo.activeLink } : {},
|
|
19549
|
-
...
|
|
19550
|
-
...
|
|
19551
|
-
...
|
|
19552
|
-
...typeof
|
|
19553
|
-
...
|
|
19554
|
-
...
|
|
19555
|
-
...
|
|
19556
|
-
...typeof
|
|
19557
|
-
...typeof
|
|
19558
|
-
...typeof
|
|
19559
|
-
...typeof
|
|
19560
|
-
...
|
|
19561
|
-
...typeof
|
|
19948
|
+
...name ? { name } : {},
|
|
19949
|
+
...uid ? { uid } : {},
|
|
19950
|
+
...pushCached?.state ? { state: pushCached.state } : {},
|
|
19951
|
+
...typeof pushCached?.index === "number" ? { index: pushCached.index } : {},
|
|
19952
|
+
...pushCached?.streamSupport?.length ? { streamSupport: pushCached.streamSupport } : {},
|
|
19953
|
+
...pushCached?.wifiState ? { wifiState: pushCached.wifiState } : {},
|
|
19954
|
+
...pushCached?.networkSegment ? { networkSegment: pushCached.networkSegment } : {},
|
|
19955
|
+
...typeof pushCached?.changed === "boolean" ? { changed: pushCached.changed } : {},
|
|
19956
|
+
...typeof pushCached?.abilityChanged === "boolean" ? { abilityChanged: pushCached.abilityChanged } : {},
|
|
19957
|
+
...typeof pushCached?.online === "boolean" ? { online: pushCached.online } : {},
|
|
19958
|
+
...typeof sleeping === "boolean" ? { sleeping } : {},
|
|
19959
|
+
...pushCached?.loginState ? { loginState: pushCached.loginState } : {},
|
|
19960
|
+
...typeof pushCached?.updatedAtMs === "number" ? { updatedAtMs: pushCached.updatedAtMs } : {}
|
|
19562
19961
|
};
|
|
19563
19962
|
});
|
|
19564
19963
|
const result = { channels, devices };
|
|
@@ -23826,13 +24225,12 @@ ${xml}`
|
|
|
23826
24225
|
]);
|
|
23827
24226
|
const support = supportResult.status === "fulfilled" ? supportResult.value : void 0;
|
|
23828
24227
|
const abilities = abilitiesResult.status === "fulfilled" ? abilitiesResult.value : void 0;
|
|
23829
|
-
const supportItem =
|
|
23830
|
-
const capabilities =
|
|
23831
|
-
ch,
|
|
23832
|
-
|
|
23833
|
-
|
|
23834
|
-
|
|
23835
|
-
);
|
|
24228
|
+
const supportItem = getSupportItemForChannel(support, ch);
|
|
24229
|
+
const capabilities = computeDeviceCapabilities({
|
|
24230
|
+
channel: ch,
|
|
24231
|
+
...support != null && { support },
|
|
24232
|
+
...abilities != null && { abilities }
|
|
24233
|
+
});
|
|
23836
24234
|
const item = supportItem;
|
|
23837
24235
|
const lightType = item?.lightType;
|
|
23838
24236
|
const ledCtrl = item?.ledCtrl;
|
|
@@ -23848,6 +24246,25 @@ ${xml}`
|
|
|
23848
24246
|
});
|
|
23849
24247
|
capabilities.hasFloodlight = probed;
|
|
23850
24248
|
}
|
|
24249
|
+
let dingDongListIds;
|
|
24250
|
+
let dingDongCfgIds;
|
|
24251
|
+
let wirelessChimeError;
|
|
24252
|
+
if (capabilities.hasWirelessChime) {
|
|
24253
|
+
try {
|
|
24254
|
+
const list = await this.getDingDongList(ch);
|
|
24255
|
+
dingDongListIds = list.map((d) => d.id);
|
|
24256
|
+
const first = list[0];
|
|
24257
|
+
const fromList = first !== void 0 && first.id >= 0;
|
|
24258
|
+
if (!fromList) {
|
|
24259
|
+
const configs = await this.getDingDongCfg(ch);
|
|
24260
|
+
dingDongCfgIds = configs.map((c) => c.id);
|
|
24261
|
+
capabilities.hasWirelessChime = configs.some((c) => c.id >= 0);
|
|
24262
|
+
}
|
|
24263
|
+
} catch (e) {
|
|
24264
|
+
capabilities.hasWirelessChime = false;
|
|
24265
|
+
wirelessChimeError = e instanceof Error ? e.message : String(e);
|
|
24266
|
+
}
|
|
24267
|
+
}
|
|
23851
24268
|
const features = this.parseFeaturesFromSupport(support);
|
|
23852
24269
|
const objects = await this.getAiDetectTypes(ch, { timeoutMs: 1500 });
|
|
23853
24270
|
const autotrackingProbed = await this.probeAutotrackingSupport(ch, {
|
|
@@ -23884,7 +24301,10 @@ ${xml}`
|
|
|
23884
24301
|
...abilities && {
|
|
23885
24302
|
abilityMergedKeyCount: Object.keys(abilities).length
|
|
23886
24303
|
},
|
|
23887
|
-
...support?.items && { supportItemCount: support.items.length }
|
|
24304
|
+
...support?.items && { supportItemCount: support.items.length },
|
|
24305
|
+
...dingDongListIds !== void 0 && { dingDongListIds },
|
|
24306
|
+
...dingDongCfgIds !== void 0 && { dingDongCfgIds },
|
|
24307
|
+
...wirelessChimeError !== void 0 && { wirelessChimeError }
|
|
23888
24308
|
};
|
|
23889
24309
|
const result = {
|
|
23890
24310
|
capabilities,
|
|
@@ -23911,90 +24331,6 @@ ${xml}`
|
|
|
23911
24331
|
this.deviceCapabilitiesCache.clear();
|
|
23912
24332
|
}
|
|
23913
24333
|
}
|
|
23914
|
-
/**
|
|
23915
|
-
* Pick the best SupportItem for a channel.
|
|
23916
|
-
* Prefers items without a name (capability items) over named items (googleHome, amazonAlexa).
|
|
23917
|
-
*/
|
|
23918
|
-
pickBestSupportItem(support, channel) {
|
|
23919
|
-
if (!support?.items?.length) return void 0;
|
|
23920
|
-
const candidates = support.items.filter((i) => i.chnID === channel);
|
|
23921
|
-
if (!candidates.length) return void 0;
|
|
23922
|
-
const score = (item) => {
|
|
23923
|
-
const anyItem = item;
|
|
23924
|
-
let result = 0;
|
|
23925
|
-
if (anyItem.name == null) result += 100;
|
|
23926
|
-
const capabilityKeys = [
|
|
23927
|
-
"ptzType",
|
|
23928
|
-
"ptzControl",
|
|
23929
|
-
"ptzPreset",
|
|
23930
|
-
"ledCtrl",
|
|
23931
|
-
"lightType",
|
|
23932
|
-
"battery",
|
|
23933
|
-
"audioVersion",
|
|
23934
|
-
"motion",
|
|
23935
|
-
"encCtrl",
|
|
23936
|
-
"newIspCfg",
|
|
23937
|
-
"remoteAbility",
|
|
23938
|
-
"aitype",
|
|
23939
|
-
"videoClip",
|
|
23940
|
-
"snap"
|
|
23941
|
-
];
|
|
23942
|
-
for (const k of capabilityKeys) {
|
|
23943
|
-
if (anyItem[k] !== void 0) result += 3;
|
|
23944
|
-
}
|
|
23945
|
-
return result;
|
|
23946
|
-
};
|
|
23947
|
-
return candidates.sort((a, b) => score(b) - score(a))[0];
|
|
23948
|
-
}
|
|
23949
|
-
/**
|
|
23950
|
-
* Parse device capabilities from SupportInfo.
|
|
23951
|
-
* Uses SupportInfo as the single source of truth with AbilityInfo as fallback.
|
|
23952
|
-
*/
|
|
23953
|
-
parseCapabilitiesFromSupport(channel, supportItem, support, abilities) {
|
|
23954
|
-
const truthy = (v) => {
|
|
23955
|
-
if (typeof v === "number") return v > 0;
|
|
23956
|
-
if (typeof v === "string") {
|
|
23957
|
-
const n = Number(v);
|
|
23958
|
-
return Number.isFinite(n) ? n > 0 : v.length > 0 && v !== "0";
|
|
23959
|
-
}
|
|
23960
|
-
return Boolean(v);
|
|
23961
|
-
};
|
|
23962
|
-
const item = supportItem;
|
|
23963
|
-
const ptzMode = support?.ptzMode?.toLowerCase();
|
|
23964
|
-
const ptzType = item ? truthy(item.ptzType) : false;
|
|
23965
|
-
const ptzControl = item ? truthy(item.ptzControl) : false;
|
|
23966
|
-
const hasPtzFromItem = ptzType || ptzControl;
|
|
23967
|
-
const hasPtzFromMode = ptzMode ? ptzMode !== "none" && ptzMode !== "0" : false;
|
|
23968
|
-
const hasPanTilt = ptzMode ? ptzMode.includes("pt") || ptzMode === "ptz" : hasPtzFromItem;
|
|
23969
|
-
const hasZoom = ptzMode ? ptzMode.includes("z") : hasPtzFromItem;
|
|
23970
|
-
const hasPresets = item ? truthy(item.ptzPreset) : false;
|
|
23971
|
-
const hasBattery = item ? truthy(item.battery) : false;
|
|
23972
|
-
const hasSiren = item ? truthy(item.audioVersion) : false;
|
|
23973
|
-
const lightType = item?.lightType;
|
|
23974
|
-
const hasFloodlight = typeof lightType === "number" ? lightType >= 2 : false;
|
|
23975
|
-
const hasPir = item ? truthy(item.rfCfg) || truthy(item.newRfCfg) || truthy(item.rfVersion) : false;
|
|
23976
|
-
const isDoorbell = item ? truthy(item.doorbellVersion) : false;
|
|
23977
|
-
const hasIntercom = truthy(support?.audioTalk) || (item ? truthy(item.ipcAudioTalk) : false);
|
|
23978
|
-
return {
|
|
23979
|
-
channel,
|
|
23980
|
-
...ptzMode && { ptzMode },
|
|
23981
|
-
hasPan: hasPanTilt,
|
|
23982
|
-
hasTilt: hasPanTilt,
|
|
23983
|
-
hasZoom,
|
|
23984
|
-
hasPresets,
|
|
23985
|
-
hasPtz: hasPtzFromItem || hasPtzFromMode || hasPanTilt || hasZoom,
|
|
23986
|
-
hasBattery,
|
|
23987
|
-
hasIntercom,
|
|
23988
|
-
hasSiren,
|
|
23989
|
-
hasFloodlight,
|
|
23990
|
-
hasPir,
|
|
23991
|
-
isDoorbell,
|
|
23992
|
-
// Autotracking: explicit flags only (autoPt or smartAI)
|
|
23993
|
-
// Note: the heuristic (ptzControl && aitype) was too aggressive and caused false positives
|
|
23994
|
-
// on cameras that have PTZ and AI detection but NOT autotracking capability.
|
|
23995
|
-
hasAutotracking: item ? truthy(item.autoPt) || truthy(item.smartAI) : false
|
|
23996
|
-
};
|
|
23997
|
-
}
|
|
23998
24334
|
/**
|
|
23999
24335
|
* Parse support features from SupportInfo.
|
|
24000
24336
|
*/
|
|
@@ -26867,6 +27203,216 @@ ${scheduleItems}
|
|
|
26867
27203
|
const channel = 0;
|
|
26868
27204
|
return await this.getSnapshot(channel);
|
|
26869
27205
|
}
|
|
27206
|
+
// --------------------
|
|
27207
|
+
// Chime / DingDong APIs
|
|
27208
|
+
// --------------------
|
|
27209
|
+
/**
|
|
27210
|
+
* Get the list of paired wireless chime devices.
|
|
27211
|
+
* cmd_id: 484 (GetDingDongList)
|
|
27212
|
+
*
|
|
27213
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27214
|
+
* @returns Array of paired chime devices
|
|
27215
|
+
*/
|
|
27216
|
+
async getDingDongList(channel) {
|
|
27217
|
+
const ch = this.normalizeChannel(channel);
|
|
27218
|
+
const xml = await this.sendXml({
|
|
27219
|
+
cmdId: BC_CMD_ID_GET_DING_DONG_LIST,
|
|
27220
|
+
channel: ch
|
|
27221
|
+
});
|
|
27222
|
+
return parseDingDongListFromXml(xml);
|
|
27223
|
+
}
|
|
27224
|
+
/**
|
|
27225
|
+
* Get parameters (name, volume, LED state) for a specific wireless chime.
|
|
27226
|
+
* cmd_id: 485 (DingDongOpt, option getParam)
|
|
27227
|
+
*
|
|
27228
|
+
* @param chimeId - The chime device ID
|
|
27229
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27230
|
+
* @returns Chime parameters
|
|
27231
|
+
*/
|
|
27232
|
+
async getDingDongParams(chimeId, channel) {
|
|
27233
|
+
const ch = this.normalizeChannel(channel);
|
|
27234
|
+
const payloadXml = buildDingDongGetParamsXml(chimeId);
|
|
27235
|
+
const xml = await this.sendXml({
|
|
27236
|
+
cmdId: BC_CMD_ID_DING_DONG_OPT,
|
|
27237
|
+
channel: ch,
|
|
27238
|
+
payloadXml
|
|
27239
|
+
});
|
|
27240
|
+
return parseDingDongParamsFromXml(xml);
|
|
27241
|
+
}
|
|
27242
|
+
/**
|
|
27243
|
+
* Set parameters (name, volume, LED state) for a specific wireless chime.
|
|
27244
|
+
* cmd_id: 485 (DingDongOpt, option setParam)
|
|
27245
|
+
*
|
|
27246
|
+
* @param chimeId - The chime device ID
|
|
27247
|
+
* @param params - Parameters to set (volLevel, ledState, name)
|
|
27248
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27249
|
+
*/
|
|
27250
|
+
async setDingDongParams(chimeId, params, channel) {
|
|
27251
|
+
const ch = this.normalizeChannel(channel);
|
|
27252
|
+
const payloadXml = buildDingDongSetParamsXml(chimeId, params);
|
|
27253
|
+
await this.sendXml({
|
|
27254
|
+
cmdId: BC_CMD_ID_DING_DONG_OPT,
|
|
27255
|
+
channel: ch,
|
|
27256
|
+
payloadXml
|
|
27257
|
+
});
|
|
27258
|
+
}
|
|
27259
|
+
/**
|
|
27260
|
+
* Trigger a wireless chime to ring with a specific ringtone.
|
|
27261
|
+
* cmd_id: 485 (DingDongOpt, option ringWithMusic)
|
|
27262
|
+
*
|
|
27263
|
+
* @param chimeId - The chime device ID
|
|
27264
|
+
* @param musicId - The ringtone/music ID to play
|
|
27265
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27266
|
+
*/
|
|
27267
|
+
async ringDingDong(chimeId, musicId, channel) {
|
|
27268
|
+
const ch = this.normalizeChannel(channel);
|
|
27269
|
+
const payloadXml = buildDingDongRingXml(chimeId, musicId);
|
|
27270
|
+
await this.sendXml({
|
|
27271
|
+
cmdId: BC_CMD_ID_DING_DONG_OPT,
|
|
27272
|
+
channel: ch,
|
|
27273
|
+
payloadXml
|
|
27274
|
+
});
|
|
27275
|
+
}
|
|
27276
|
+
/**
|
|
27277
|
+
* Get the per-event alarm configuration for paired wireless chimes.
|
|
27278
|
+
* cmd_id: 486 (GetDingDongCfg)
|
|
27279
|
+
*
|
|
27280
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27281
|
+
* @returns Array of chime configurations (one per paired chime)
|
|
27282
|
+
*/
|
|
27283
|
+
async getDingDongCfg(channel) {
|
|
27284
|
+
const ch = this.normalizeChannel(channel);
|
|
27285
|
+
const xml = await this.sendXml({
|
|
27286
|
+
cmdId: BC_CMD_ID_GET_DING_DONG_CFG,
|
|
27287
|
+
channel: ch
|
|
27288
|
+
});
|
|
27289
|
+
return parseDingDongCfgFromXml(xml);
|
|
27290
|
+
}
|
|
27291
|
+
/**
|
|
27292
|
+
* Set the per-event alarm configuration for a specific wireless chime.
|
|
27293
|
+
* cmd_id: 487 (SetDingDongCfg)
|
|
27294
|
+
*
|
|
27295
|
+
* @param chimeId - The chime ring/device ID
|
|
27296
|
+
* @param eventType - Event type string (e.g. "doorbell", "package", "people")
|
|
27297
|
+
* @param state - 0 = disabled, 1 = enabled
|
|
27298
|
+
* @param musicId - Ringtone ID to use for this event type
|
|
27299
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27300
|
+
*/
|
|
27301
|
+
async setDingDongCfg(chimeId, eventType, state, musicId, channel) {
|
|
27302
|
+
const ch = this.normalizeChannel(channel);
|
|
27303
|
+
const payloadXml = buildSetDingDongCfgXml(chimeId, eventType, state, musicId);
|
|
27304
|
+
await this.sendXml({
|
|
27305
|
+
cmdId: BC_CMD_ID_SET_DING_DONG_CFG,
|
|
27306
|
+
channel: ch,
|
|
27307
|
+
payloadXml
|
|
27308
|
+
});
|
|
27309
|
+
}
|
|
27310
|
+
/** Cache of last known hardwired chime state per channel, used to avoid re-fetching on every set. */
|
|
27311
|
+
_hardwiredChimeCache = /* @__PURE__ */ new Map();
|
|
27312
|
+
/**
|
|
27313
|
+
* Get the hardwired (wired-in) chime state.
|
|
27314
|
+
* cmd_id: 483 (GetDingDongCtrl)
|
|
27315
|
+
*
|
|
27316
|
+
* Note: calling this may briefly trigger the physical chime to rattle.
|
|
27317
|
+
*
|
|
27318
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27319
|
+
* @returns Hardwired chime state (type, enabled, time)
|
|
27320
|
+
*/
|
|
27321
|
+
async getHardwiredChime(channel) {
|
|
27322
|
+
const ch = this.normalizeChannel(channel);
|
|
27323
|
+
const payloadXml = buildGetDingDongCtrlXml();
|
|
27324
|
+
const xml = await this.sendXml({
|
|
27325
|
+
cmdId: BC_CMD_ID_DING_DONG_CTRL,
|
|
27326
|
+
channel: ch,
|
|
27327
|
+
payloadXml
|
|
27328
|
+
});
|
|
27329
|
+
const state = parseHardwiredChimeFromXml(xml);
|
|
27330
|
+
this._hardwiredChimeCache.set(ch, state);
|
|
27331
|
+
return state;
|
|
27332
|
+
}
|
|
27333
|
+
/**
|
|
27334
|
+
* Set the hardwired (wired-in) chime state.
|
|
27335
|
+
* cmd_id: 483 (SetDingDongCtrl)
|
|
27336
|
+
*
|
|
27337
|
+
* Uses the cached state from a previous getHardwiredChime call to fill in
|
|
27338
|
+
* missing type/time fields, avoiding a double round-trip on every set.
|
|
27339
|
+
* Falls back to fetching if no cache is available.
|
|
27340
|
+
*
|
|
27341
|
+
* @param params - Chime configuration (type, enabled, time)
|
|
27342
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27343
|
+
*/
|
|
27344
|
+
async setHardwiredChime(params, channel) {
|
|
27345
|
+
const ch = this.normalizeChannel(channel);
|
|
27346
|
+
let current = this._hardwiredChimeCache.get(ch);
|
|
27347
|
+
if (!current) {
|
|
27348
|
+
current = await this.getHardwiredChime(ch);
|
|
27349
|
+
}
|
|
27350
|
+
const chimeType = params.type ?? current.type;
|
|
27351
|
+
const enabled = params.enabled ? 1 : 0;
|
|
27352
|
+
const time = params.time ?? current.time;
|
|
27353
|
+
const payloadXml = buildSetDingDongCtrlXml(chimeType, enabled, time);
|
|
27354
|
+
const xml = await this.sendXml({
|
|
27355
|
+
cmdId: BC_CMD_ID_DING_DONG_CTRL,
|
|
27356
|
+
channel: ch,
|
|
27357
|
+
payloadXml
|
|
27358
|
+
});
|
|
27359
|
+
const newState = parseHardwiredChimeFromXml(xml);
|
|
27360
|
+
this._hardwiredChimeCache.set(ch, newState);
|
|
27361
|
+
return newState;
|
|
27362
|
+
}
|
|
27363
|
+
/**
|
|
27364
|
+
* Play an audio file on the doorbell / chime device.
|
|
27365
|
+
* cmd_id: 349 (QuickReplyPlay)
|
|
27366
|
+
*
|
|
27367
|
+
* @param fileId - The audio file ID to play
|
|
27368
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27369
|
+
*/
|
|
27370
|
+
async quickReplyPlay(fileId, channel) {
|
|
27371
|
+
const ch = this.normalizeChannel(channel);
|
|
27372
|
+
const payloadXml = buildQuickReplyPlayXml(ch, fileId);
|
|
27373
|
+
await this.sendXml({
|
|
27374
|
+
cmdId: BC_CMD_ID_QUICK_REPLY_PLAY,
|
|
27375
|
+
channel: ch,
|
|
27376
|
+
payloadXml
|
|
27377
|
+
});
|
|
27378
|
+
}
|
|
27379
|
+
/**
|
|
27380
|
+
* Get the silent mode state of a paired wireless chime.
|
|
27381
|
+
* cmd_id: 609 (GetDingDongSilent)
|
|
27382
|
+
*
|
|
27383
|
+
* @param chimeId - The wireless chime device ID (from getDingDongList)
|
|
27384
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27385
|
+
* @returns Wireless chime silent state (time=0 means active/not silenced)
|
|
27386
|
+
*/
|
|
27387
|
+
async getDingDongSilent(chimeId, channel) {
|
|
27388
|
+
const ch = this.normalizeChannel(channel);
|
|
27389
|
+
const payloadXml = buildGetDingDongSilentXml(chimeId);
|
|
27390
|
+
const xml = await this.sendXml({
|
|
27391
|
+
cmdId: BC_CMD_ID_GET_DING_DONG_SILENT,
|
|
27392
|
+
channel: ch,
|
|
27393
|
+
payloadXml
|
|
27394
|
+
});
|
|
27395
|
+
return parseWirelessChimeSilentFromXml(xml, chimeId);
|
|
27396
|
+
}
|
|
27397
|
+
/**
|
|
27398
|
+
* Set the silent mode of a paired wireless chime.
|
|
27399
|
+
* cmd_id: 610 (SetDingDongSilent)
|
|
27400
|
+
*
|
|
27401
|
+
* @param chimeId - The wireless chime device ID (from getDingDongList)
|
|
27402
|
+
* @param time - Silence duration in seconds. 0 = not silenced (chime active), >0 = silenced for this many seconds.
|
|
27403
|
+
* @param channel - Channel number (0-based, default 0)
|
|
27404
|
+
* @returns Updated wireless chime silent state
|
|
27405
|
+
*/
|
|
27406
|
+
async setDingDongSilent(chimeId, time, channel) {
|
|
27407
|
+
const ch = this.normalizeChannel(channel);
|
|
27408
|
+
const payloadXml = buildSetDingDongSilentXml(chimeId, time);
|
|
27409
|
+
const xml = await this.sendXml({
|
|
27410
|
+
cmdId: BC_CMD_ID_SET_DING_DONG_SILENT,
|
|
27411
|
+
channel: ch,
|
|
27412
|
+
payloadXml
|
|
27413
|
+
});
|
|
27414
|
+
return parseWirelessChimeSilentFromXml(xml, chimeId);
|
|
27415
|
+
}
|
|
26870
27416
|
};
|
|
26871
27417
|
|
|
26872
27418
|
// src/reolink/baichuan/HlsSessionManager.ts
|
|
@@ -27219,8 +27765,12 @@ function detectIosClient(userAgent) {
|
|
|
27219
27765
|
return {
|
|
27220
27766
|
isIos,
|
|
27221
27767
|
isIosInstalledApp,
|
|
27222
|
-
// iOS
|
|
27223
|
-
|
|
27768
|
+
// ALL iOS clients need HLS for reliable video clip playback.
|
|
27769
|
+
// iOS AVFoundation requires Content-Length + Range support for regular MP4,
|
|
27770
|
+
// but generating the full MP4 upfront takes too long (camera download + transcode).
|
|
27771
|
+
// HLS delivers segments progressively (~3-5s to first frame vs 25+ seconds).
|
|
27772
|
+
// Safari, AVPlayer, and InstalledApp all support HLS natively.
|
|
27773
|
+
needsHls: isIos
|
|
27224
27774
|
};
|
|
27225
27775
|
}
|
|
27226
27776
|
function buildHlsRedirectUrl(originalUrl) {
|
|
@@ -34739,6 +35289,8 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
34739
35289
|
BC_CMD_ID_COVER_STANDALONE_460,
|
|
34740
35290
|
BC_CMD_ID_COVER_STANDALONE_461,
|
|
34741
35291
|
BC_CMD_ID_COVER_STANDALONE_462,
|
|
35292
|
+
BC_CMD_ID_DING_DONG_CTRL,
|
|
35293
|
+
BC_CMD_ID_DING_DONG_OPT,
|
|
34742
35294
|
BC_CMD_ID_FILE_INFO_LIST_CLOSE,
|
|
34743
35295
|
BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO,
|
|
34744
35296
|
BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD,
|
|
@@ -34762,6 +35314,9 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
34762
35314
|
BC_CMD_ID_GET_BATTERY_INFO_LIST,
|
|
34763
35315
|
BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
|
|
34764
35316
|
BC_CMD_ID_GET_DAY_RECORDS,
|
|
35317
|
+
BC_CMD_ID_GET_DING_DONG_CFG,
|
|
35318
|
+
BC_CMD_ID_GET_DING_DONG_LIST,
|
|
35319
|
+
BC_CMD_ID_GET_DING_DONG_SILENT,
|
|
34765
35320
|
BC_CMD_ID_GET_EMAIL_TASK,
|
|
34766
35321
|
BC_CMD_ID_GET_FTP_TASK,
|
|
34767
35322
|
BC_CMD_ID_GET_HDD_INFO_LIST,
|
|
@@ -34798,9 +35353,12 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
34798
35353
|
BC_CMD_ID_PUSH_SERIAL,
|
|
34799
35354
|
BC_CMD_ID_PUSH_SLEEP_STATUS,
|
|
34800
35355
|
BC_CMD_ID_PUSH_VIDEO_INPUT,
|
|
35356
|
+
BC_CMD_ID_QUICK_REPLY_PLAY,
|
|
34801
35357
|
BC_CMD_ID_SET_AI_ALARM,
|
|
34802
35358
|
BC_CMD_ID_SET_AI_CFG,
|
|
34803
35359
|
BC_CMD_ID_SET_AUDIO_TASK,
|
|
35360
|
+
BC_CMD_ID_SET_DING_DONG_CFG,
|
|
35361
|
+
BC_CMD_ID_SET_DING_DONG_SILENT,
|
|
34804
35362
|
BC_CMD_ID_SET_MOTION_ALARM,
|
|
34805
35363
|
BC_CMD_ID_SET_PIR_INFO,
|
|
34806
35364
|
BC_CMD_ID_SET_WHITE_LED_STATE,
|
|
@@ -34917,6 +35475,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
34917
35475
|
getGlobalLogger,
|
|
34918
35476
|
getH265NalType,
|
|
34919
35477
|
getMjpegContentType,
|
|
35478
|
+
getSupportItemForChannel,
|
|
34920
35479
|
getVideoStream,
|
|
34921
35480
|
getVideoclipClientInfo,
|
|
34922
35481
|
getXmlText,
|