@camstack/addon-provider-hikvision 1.2.4 → 1.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +74 -10
- package/dist/addon.mjs +74 -10
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -12991,6 +12991,12 @@ var motionCooldownMsField = {
|
|
|
12991
12991
|
default: 3e4,
|
|
12992
12992
|
step: 500
|
|
12993
12993
|
};
|
|
12994
|
+
var maxSessionHoldMsField = {
|
|
12995
|
+
min: 0,
|
|
12996
|
+
max: 6e5,
|
|
12997
|
+
default: 12e4,
|
|
12998
|
+
step: 5e3
|
|
12999
|
+
};
|
|
12994
13000
|
var motionFpsField = {
|
|
12995
13001
|
min: 1,
|
|
12996
13002
|
max: 30,
|
|
@@ -13138,6 +13144,19 @@ var RunnerCameraConfigSchema = object({
|
|
|
13138
13144
|
"on-motion"
|
|
13139
13145
|
]).default("always-on"),
|
|
13140
13146
|
motionCooldownMs: number().min(motionCooldownMsField.min).default(motionCooldownMsField.default),
|
|
13147
|
+
/**
|
|
13148
|
+
* Orchestrator-side on-motion session-hold cap (ms). While an on-motion
|
|
13149
|
+
* detection session is active and ≥1 confirmed non-stationary track is
|
|
13150
|
+
* still live, the orchestrator keeps the session open past
|
|
13151
|
+
* `motionCooldownMs` (a slowly-moving subject can stop re-triggering the
|
|
13152
|
+
* camera's VMD yet is still being tracked frame-to-frame) — up to this many
|
|
13153
|
+
* ms since the session opened, after which it closes regardless. `0`
|
|
13154
|
+
* disables the hold (legacy cooldown-only teardown). Not consumed by the
|
|
13155
|
+
* runner itself — carried here so it shares the per-camera device-settings
|
|
13156
|
+
* surface with `motionCooldownMs`; the orchestrator reads it off the
|
|
13157
|
+
* resolved `CameraDetectionConfig`.
|
|
13158
|
+
*/
|
|
13159
|
+
maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
|
|
13141
13160
|
motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
|
|
13142
13161
|
detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
|
|
13143
13162
|
motionStreamId: string(),
|
|
@@ -13227,7 +13246,7 @@ var RunnerCameraConfigSchema = object({
|
|
|
13227
13246
|
*/
|
|
13228
13247
|
inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
13229
13248
|
});
|
|
13230
|
-
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
13249
|
+
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
13231
13250
|
/**
|
|
13232
13251
|
* Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
|
|
13233
13252
|
* load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
|
|
@@ -19996,7 +20015,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19996
20015
|
}), method(object({
|
|
19997
20016
|
eventId: string(),
|
|
19998
20017
|
kind: MediaFileKindEnum.optional()
|
|
19999
|
-
}), array(MediaFileSchema).readonly()), method(object({
|
|
20018
|
+
}), array(MediaFileSchema).readonly()), method(object({
|
|
20019
|
+
trackId: string(),
|
|
20020
|
+
kinds: array(MediaFileKindEnum).optional()
|
|
20021
|
+
}), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
20000
20022
|
deviceId: number(),
|
|
20001
20023
|
timestamp: number(),
|
|
20002
20024
|
frameWidth: number(),
|
|
@@ -31194,13 +31216,44 @@ function parseTwoWayAudioChannels(xml) {
|
|
|
31194
31216
|
return out;
|
|
31195
31217
|
}
|
|
31196
31218
|
/**
|
|
31219
|
+
* Idle watchdog for the alarm stream. Hikvision firmware emits a keep-alive
|
|
31220
|
+
* heartbeat (typically a `videoloss`/`inactive` alert) roughly every ~5s even
|
|
31221
|
+
* when nothing is happening, so a total absence of ANY bytes for this long
|
|
31222
|
+
* means the pipe is dead — either a half-open TCP connection (no FIN, no data)
|
|
31223
|
+
* that would otherwise block `reader.read()` forever, or a stalled proxy/NAT
|
|
31224
|
+
* conntrack entry between the hub and a camera on a different subnet. When it
|
|
31225
|
+
* fires we cancel the reader, surfacing a clean stream-end that the reconnect
|
|
31226
|
+
* path treats as a recoverable disconnect.
|
|
31227
|
+
*/
|
|
31228
|
+
var ALARM_STREAM_IDLE_TIMEOUT_MS = 3e4;
|
|
31229
|
+
/**
|
|
31197
31230
|
* Subscribe to the camera's alarm stream. Returns an `AbortController`
|
|
31198
31231
|
* — `controller.abort()` tears the subscription down. Reconnect logic
|
|
31199
31232
|
* is the caller's responsibility (we keep the parser simple and let
|
|
31200
31233
|
* the device class own the lifecycle / backoff timing).
|
|
31201
|
-
|
|
31202
|
-
|
|
31234
|
+
*
|
|
31235
|
+
* Every terminal outcome that is NOT a deliberate `controller.abort()` is
|
|
31236
|
+
* reported through `onError` exactly once, so the caller's reconnect logic
|
|
31237
|
+
* always fires:
|
|
31238
|
+
* - HTTP / boundary failure at subscribe time,
|
|
31239
|
+
* - a thrown error while pumping,
|
|
31240
|
+
* - AND a clean stream-end (`done`). Hikvision cameras recycle the
|
|
31241
|
+
* alertStream HTTP connection periodically (firmware keep-alive limits,
|
|
31242
|
+
* internal event-subsystem restarts, or an idle NAT/proxy hop closing the
|
|
31243
|
+
* socket). A clean close used to fall through silently — `onError` never
|
|
31244
|
+
* fired, the caller's `alarmController` stayed non-null, and the device
|
|
31245
|
+
* never resubscribed — so motion stopped permanently until the provider
|
|
31246
|
+
* restarted. Treating clean-end as a recoverable disconnect closes that gap.
|
|
31247
|
+
*/
|
|
31248
|
+
function subscribeAlarms(client, handlers, idleTimeoutMs = ALARM_STREAM_IDLE_TIMEOUT_MS) {
|
|
31203
31249
|
const controller = new AbortController();
|
|
31250
|
+
let settled = false;
|
|
31251
|
+
const fail = (err) => {
|
|
31252
|
+
if (settled) return;
|
|
31253
|
+
settled = true;
|
|
31254
|
+
if (controller.signal.aborted) return;
|
|
31255
|
+
handlers.onError(err);
|
|
31256
|
+
};
|
|
31204
31257
|
(async () => {
|
|
31205
31258
|
try {
|
|
31206
31259
|
const res = await client.request("/ISAPI/Event/notification/alertStream", {
|
|
@@ -31209,20 +31262,20 @@ function subscribeAlarms(client, handlers) {
|
|
|
31209
31262
|
timeoutMs: null
|
|
31210
31263
|
});
|
|
31211
31264
|
if (!res.ok || !res.body) {
|
|
31212
|
-
|
|
31265
|
+
fail(/* @__PURE__ */ new Error(`alarm stream HTTP ${res.status}`));
|
|
31213
31266
|
return;
|
|
31214
31267
|
}
|
|
31215
31268
|
const ct = res.headers.get("content-type") ?? "";
|
|
31216
31269
|
const boundary = parseBoundary(ct);
|
|
31217
31270
|
if (!boundary) {
|
|
31218
|
-
|
|
31271
|
+
fail(/* @__PURE__ */ new Error(`alarm stream missing multipart boundary in Content-Type: "${ct}"`));
|
|
31219
31272
|
return;
|
|
31220
31273
|
}
|
|
31221
31274
|
handlers.onConnected?.();
|
|
31222
|
-
await pumpAlarmStream(res.body, boundary, handlers);
|
|
31275
|
+
await pumpAlarmStream(res.body, boundary, handlers, idleTimeoutMs);
|
|
31276
|
+
fail(/* @__PURE__ */ new Error("alarm stream ended"));
|
|
31223
31277
|
} catch (err) {
|
|
31224
|
-
|
|
31225
|
-
handlers.onError(err);
|
|
31278
|
+
fail(err);
|
|
31226
31279
|
}
|
|
31227
31280
|
})();
|
|
31228
31281
|
return controller;
|
|
@@ -31231,14 +31284,24 @@ function parseBoundary(contentType) {
|
|
|
31231
31284
|
const m = /boundary\s*=\s*"?([^";\s]+)"?/i.exec(contentType);
|
|
31232
31285
|
return m ? m[1].trim() : null;
|
|
31233
31286
|
}
|
|
31234
|
-
async function pumpAlarmStream(stream, boundary, handlers) {
|
|
31287
|
+
async function pumpAlarmStream(stream, boundary, handlers, idleTimeoutMs = ALARM_STREAM_IDLE_TIMEOUT_MS) {
|
|
31235
31288
|
const reader = stream.getReader();
|
|
31236
31289
|
const dashBoundary = `--${boundary}`;
|
|
31237
31290
|
let bufferedBytes = new Uint8Array(0);
|
|
31291
|
+
let idleTimer = null;
|
|
31292
|
+
const armIdle = () => {
|
|
31293
|
+
if (idleTimeoutMs <= 0) return;
|
|
31294
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
31295
|
+
idleTimer = setTimeout(() => {
|
|
31296
|
+
reader.cancel().catch(() => {});
|
|
31297
|
+
}, idleTimeoutMs);
|
|
31298
|
+
};
|
|
31238
31299
|
try {
|
|
31300
|
+
armIdle();
|
|
31239
31301
|
while (true) {
|
|
31240
31302
|
const { value, done } = await reader.read();
|
|
31241
31303
|
if (done) break;
|
|
31304
|
+
armIdle();
|
|
31242
31305
|
if (!value || value.byteLength === 0) continue;
|
|
31243
31306
|
bufferedBytes = concat(bufferedBytes, value);
|
|
31244
31307
|
const parts = splitOnBoundary(bufferedBytes, dashBoundary);
|
|
@@ -31253,6 +31316,7 @@ async function pumpAlarmStream(stream, boundary, handlers) {
|
|
|
31253
31316
|
}
|
|
31254
31317
|
}
|
|
31255
31318
|
} finally {
|
|
31319
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
31256
31320
|
try {
|
|
31257
31321
|
reader.releaseLock();
|
|
31258
31322
|
} catch {}
|
package/dist/addon.mjs
CHANGED
|
@@ -12992,6 +12992,12 @@ var motionCooldownMsField = {
|
|
|
12992
12992
|
default: 3e4,
|
|
12993
12993
|
step: 500
|
|
12994
12994
|
};
|
|
12995
|
+
var maxSessionHoldMsField = {
|
|
12996
|
+
min: 0,
|
|
12997
|
+
max: 6e5,
|
|
12998
|
+
default: 12e4,
|
|
12999
|
+
step: 5e3
|
|
13000
|
+
};
|
|
12995
13001
|
var motionFpsField = {
|
|
12996
13002
|
min: 1,
|
|
12997
13003
|
max: 30,
|
|
@@ -13139,6 +13145,19 @@ var RunnerCameraConfigSchema = object({
|
|
|
13139
13145
|
"on-motion"
|
|
13140
13146
|
]).default("always-on"),
|
|
13141
13147
|
motionCooldownMs: number().min(motionCooldownMsField.min).default(motionCooldownMsField.default),
|
|
13148
|
+
/**
|
|
13149
|
+
* Orchestrator-side on-motion session-hold cap (ms). While an on-motion
|
|
13150
|
+
* detection session is active and ≥1 confirmed non-stationary track is
|
|
13151
|
+
* still live, the orchestrator keeps the session open past
|
|
13152
|
+
* `motionCooldownMs` (a slowly-moving subject can stop re-triggering the
|
|
13153
|
+
* camera's VMD yet is still being tracked frame-to-frame) — up to this many
|
|
13154
|
+
* ms since the session opened, after which it closes regardless. `0`
|
|
13155
|
+
* disables the hold (legacy cooldown-only teardown). Not consumed by the
|
|
13156
|
+
* runner itself — carried here so it shares the per-camera device-settings
|
|
13157
|
+
* surface with `motionCooldownMs`; the orchestrator reads it off the
|
|
13158
|
+
* resolved `CameraDetectionConfig`.
|
|
13159
|
+
*/
|
|
13160
|
+
maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
|
|
13142
13161
|
motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
|
|
13143
13162
|
detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
|
|
13144
13163
|
motionStreamId: string(),
|
|
@@ -13228,7 +13247,7 @@ var RunnerCameraConfigSchema = object({
|
|
|
13228
13247
|
*/
|
|
13229
13248
|
inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
13230
13249
|
});
|
|
13231
|
-
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
13250
|
+
motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
13232
13251
|
/**
|
|
13233
13252
|
* Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
|
|
13234
13253
|
* load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
|
|
@@ -19997,7 +20016,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19997
20016
|
}), method(object({
|
|
19998
20017
|
eventId: string(),
|
|
19999
20018
|
kind: MediaFileKindEnum.optional()
|
|
20000
|
-
}), array(MediaFileSchema).readonly()), method(object({
|
|
20019
|
+
}), array(MediaFileSchema).readonly()), method(object({
|
|
20020
|
+
trackId: string(),
|
|
20021
|
+
kinds: array(MediaFileKindEnum).optional()
|
|
20022
|
+
}), array(MediaFileSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), object({
|
|
20001
20023
|
deviceId: number(),
|
|
20002
20024
|
timestamp: number(),
|
|
20003
20025
|
frameWidth: number(),
|
|
@@ -31195,13 +31217,44 @@ function parseTwoWayAudioChannels(xml) {
|
|
|
31195
31217
|
return out;
|
|
31196
31218
|
}
|
|
31197
31219
|
/**
|
|
31220
|
+
* Idle watchdog for the alarm stream. Hikvision firmware emits a keep-alive
|
|
31221
|
+
* heartbeat (typically a `videoloss`/`inactive` alert) roughly every ~5s even
|
|
31222
|
+
* when nothing is happening, so a total absence of ANY bytes for this long
|
|
31223
|
+
* means the pipe is dead — either a half-open TCP connection (no FIN, no data)
|
|
31224
|
+
* that would otherwise block `reader.read()` forever, or a stalled proxy/NAT
|
|
31225
|
+
* conntrack entry between the hub and a camera on a different subnet. When it
|
|
31226
|
+
* fires we cancel the reader, surfacing a clean stream-end that the reconnect
|
|
31227
|
+
* path treats as a recoverable disconnect.
|
|
31228
|
+
*/
|
|
31229
|
+
var ALARM_STREAM_IDLE_TIMEOUT_MS = 3e4;
|
|
31230
|
+
/**
|
|
31198
31231
|
* Subscribe to the camera's alarm stream. Returns an `AbortController`
|
|
31199
31232
|
* — `controller.abort()` tears the subscription down. Reconnect logic
|
|
31200
31233
|
* is the caller's responsibility (we keep the parser simple and let
|
|
31201
31234
|
* the device class own the lifecycle / backoff timing).
|
|
31202
|
-
|
|
31203
|
-
|
|
31235
|
+
*
|
|
31236
|
+
* Every terminal outcome that is NOT a deliberate `controller.abort()` is
|
|
31237
|
+
* reported through `onError` exactly once, so the caller's reconnect logic
|
|
31238
|
+
* always fires:
|
|
31239
|
+
* - HTTP / boundary failure at subscribe time,
|
|
31240
|
+
* - a thrown error while pumping,
|
|
31241
|
+
* - AND a clean stream-end (`done`). Hikvision cameras recycle the
|
|
31242
|
+
* alertStream HTTP connection periodically (firmware keep-alive limits,
|
|
31243
|
+
* internal event-subsystem restarts, or an idle NAT/proxy hop closing the
|
|
31244
|
+
* socket). A clean close used to fall through silently — `onError` never
|
|
31245
|
+
* fired, the caller's `alarmController` stayed non-null, and the device
|
|
31246
|
+
* never resubscribed — so motion stopped permanently until the provider
|
|
31247
|
+
* restarted. Treating clean-end as a recoverable disconnect closes that gap.
|
|
31248
|
+
*/
|
|
31249
|
+
function subscribeAlarms(client, handlers, idleTimeoutMs = ALARM_STREAM_IDLE_TIMEOUT_MS) {
|
|
31204
31250
|
const controller = new AbortController();
|
|
31251
|
+
let settled = false;
|
|
31252
|
+
const fail = (err) => {
|
|
31253
|
+
if (settled) return;
|
|
31254
|
+
settled = true;
|
|
31255
|
+
if (controller.signal.aborted) return;
|
|
31256
|
+
handlers.onError(err);
|
|
31257
|
+
};
|
|
31205
31258
|
(async () => {
|
|
31206
31259
|
try {
|
|
31207
31260
|
const res = await client.request("/ISAPI/Event/notification/alertStream", {
|
|
@@ -31210,20 +31263,20 @@ function subscribeAlarms(client, handlers) {
|
|
|
31210
31263
|
timeoutMs: null
|
|
31211
31264
|
});
|
|
31212
31265
|
if (!res.ok || !res.body) {
|
|
31213
|
-
|
|
31266
|
+
fail(/* @__PURE__ */ new Error(`alarm stream HTTP ${res.status}`));
|
|
31214
31267
|
return;
|
|
31215
31268
|
}
|
|
31216
31269
|
const ct = res.headers.get("content-type") ?? "";
|
|
31217
31270
|
const boundary = parseBoundary(ct);
|
|
31218
31271
|
if (!boundary) {
|
|
31219
|
-
|
|
31272
|
+
fail(/* @__PURE__ */ new Error(`alarm stream missing multipart boundary in Content-Type: "${ct}"`));
|
|
31220
31273
|
return;
|
|
31221
31274
|
}
|
|
31222
31275
|
handlers.onConnected?.();
|
|
31223
|
-
await pumpAlarmStream(res.body, boundary, handlers);
|
|
31276
|
+
await pumpAlarmStream(res.body, boundary, handlers, idleTimeoutMs);
|
|
31277
|
+
fail(/* @__PURE__ */ new Error("alarm stream ended"));
|
|
31224
31278
|
} catch (err) {
|
|
31225
|
-
|
|
31226
|
-
handlers.onError(err);
|
|
31279
|
+
fail(err);
|
|
31227
31280
|
}
|
|
31228
31281
|
})();
|
|
31229
31282
|
return controller;
|
|
@@ -31232,14 +31285,24 @@ function parseBoundary(contentType) {
|
|
|
31232
31285
|
const m = /boundary\s*=\s*"?([^";\s]+)"?/i.exec(contentType);
|
|
31233
31286
|
return m ? m[1].trim() : null;
|
|
31234
31287
|
}
|
|
31235
|
-
async function pumpAlarmStream(stream, boundary, handlers) {
|
|
31288
|
+
async function pumpAlarmStream(stream, boundary, handlers, idleTimeoutMs = ALARM_STREAM_IDLE_TIMEOUT_MS) {
|
|
31236
31289
|
const reader = stream.getReader();
|
|
31237
31290
|
const dashBoundary = `--${boundary}`;
|
|
31238
31291
|
let bufferedBytes = new Uint8Array(0);
|
|
31292
|
+
let idleTimer = null;
|
|
31293
|
+
const armIdle = () => {
|
|
31294
|
+
if (idleTimeoutMs <= 0) return;
|
|
31295
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
31296
|
+
idleTimer = setTimeout(() => {
|
|
31297
|
+
reader.cancel().catch(() => {});
|
|
31298
|
+
}, idleTimeoutMs);
|
|
31299
|
+
};
|
|
31239
31300
|
try {
|
|
31301
|
+
armIdle();
|
|
31240
31302
|
while (true) {
|
|
31241
31303
|
const { value, done } = await reader.read();
|
|
31242
31304
|
if (done) break;
|
|
31305
|
+
armIdle();
|
|
31243
31306
|
if (!value || value.byteLength === 0) continue;
|
|
31244
31307
|
bufferedBytes = concat(bufferedBytes, value);
|
|
31245
31308
|
const parts = splitOnBoundary(bufferedBytes, dashBoundary);
|
|
@@ -31254,6 +31317,7 @@ async function pumpAlarmStream(stream, boundary, handlers) {
|
|
|
31254
31317
|
}
|
|
31255
31318
|
}
|
|
31256
31319
|
} finally {
|
|
31320
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
31257
31321
|
try {
|
|
31258
31322
|
reader.releaseLock();
|
|
31259
31323
|
} catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-hikvision",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.5",
|
|
4
4
|
"description": "Hikvision camera device provider addon for CamStack — ISAPI over HTTP(S) with digest auth (snapshot, alarm stream, RTSP discovery)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|