@camstack/addon-provider-hikvision 1.1.18 → 1.1.20
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 +305 -231
- package/dist/addon.mjs +305 -231
- package/package.json +1 -1
package/dist/addon.mjs
CHANGED
|
@@ -4636,7 +4636,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4636
4636
|
return inst;
|
|
4637
4637
|
}
|
|
4638
4638
|
//#endregion
|
|
4639
|
-
//#region ../types/dist/sleep-
|
|
4639
|
+
//#region ../types/dist/sleep-Cc14_yxc.mjs
|
|
4640
4640
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4641
4641
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4642
4642
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -4822,6 +4822,18 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
4822
4822
|
*/
|
|
4823
4823
|
EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
|
|
4824
4824
|
/**
|
|
4825
|
+
* The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
|
|
4826
|
+
* Emitted by addon-pipeline-orchestrator whenever it (re)derives node
|
|
4827
|
+
* capabilities — at boot, on agent online/offline, and on an ingest-node
|
|
4828
|
+
* flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
|
|
4829
|
+
* keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
|
|
4830
|
+
* cross-process `getIngestOwner` query (push the authority's decision instead
|
|
4831
|
+
* of polling it on the hot path). Idempotent state — re-emitted on every
|
|
4832
|
+
* topology change, so a dropped event self-heals on the next one (plus the
|
|
4833
|
+
* broker's long backstop reconcile query).
|
|
4834
|
+
*/
|
|
4835
|
+
EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
|
|
4836
|
+
/**
|
|
4825
4837
|
* Periodic snapshot of per-node pipeline-runner load
|
|
4826
4838
|
* (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
|
|
4827
4839
|
* subscribe instead of polling `pipelineRunner.getLocalLoad`.
|
|
@@ -6905,6 +6917,36 @@ var ModelFormatsSchema = object({
|
|
|
6905
6917
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6906
6918
|
pt: ModelFormatEntrySchema.optional()
|
|
6907
6919
|
});
|
|
6920
|
+
/**
|
|
6921
|
+
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
6922
|
+
* the reduced `PipelineModelOption` returned in `pipeline.getSchema()` so the
|
|
6923
|
+
* grouped Family→Tier→Variant picker renders identically in the config UI and
|
|
6924
|
+
* in the pipeline/device steppers. The flat `id` stays the source of truth for
|
|
6925
|
+
* resolution/download/persistence; this is a presentation overlay resolved back
|
|
6926
|
+
* to an `id`.
|
|
6927
|
+
*/
|
|
6928
|
+
var ModelVariantGroupSchema = object({
|
|
6929
|
+
/** Top-level family, e.g. `yolo26` (later `d-fine`, `rf-detr`). */
|
|
6930
|
+
family: string(),
|
|
6931
|
+
/** Size within the family, e.g. `n` | `s` | `m` | `l`. */
|
|
6932
|
+
tier: string(),
|
|
6933
|
+
/** Quantization axis. Omit ⇒ the fp32 base build. */
|
|
6934
|
+
precision: _enum(["fp32", "int8"]).optional(),
|
|
6935
|
+
/**
|
|
6936
|
+
* Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
|
|
6937
|
+
* latency-optimized export (e.g. ReLU-activation variant) — the slot the
|
|
6938
|
+
* future performance variants plug into.
|
|
6939
|
+
*/
|
|
6940
|
+
optimization: _enum(["standard", "fast"]).optional(),
|
|
6941
|
+
/**
|
|
6942
|
+
* Input-resolution axis (square input side, px). Omit ⇒ the family's native
|
|
6943
|
+
* resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
|
|
6944
|
+
* cheap latency lever — especially on Apple ANE and the Intel N100 — at a
|
|
6945
|
+
* small-object accuracy cost. Mirrors the model's `inputSize` but lifted onto
|
|
6946
|
+
* the group so the selector can offer it as a variant axis.
|
|
6947
|
+
*/
|
|
6948
|
+
resolution: number().int().positive().optional()
|
|
6949
|
+
});
|
|
6908
6950
|
var ModelCatalogEntrySchema = object({
|
|
6909
6951
|
id: string(),
|
|
6910
6952
|
name: string(),
|
|
@@ -6934,7 +6976,43 @@ var ModelCatalogEntrySchema = object({
|
|
|
6934
6976
|
* Auxiliary files required at runtime (labels JSON, charset dict, etc.).
|
|
6935
6977
|
* Downloaded into the same modelsDir alongside the model file.
|
|
6936
6978
|
*/
|
|
6937
|
-
extraFiles: array(ModelExtraFileSchema).readonly().optional()
|
|
6979
|
+
extraFiles: array(ModelExtraFileSchema).readonly().optional(),
|
|
6980
|
+
/**
|
|
6981
|
+
* LEGACY entry — retained in the catalog so a persisted operator selection
|
|
6982
|
+
* still RESOLVES (and can be re-activated), but hidden from the selectable
|
|
6983
|
+
* model list and excluded from the auto format-default pick. Set on the
|
|
6984
|
+
* superseded / consolidated models (older lineages, redundant fp16 IRs) so
|
|
6985
|
+
* the active lineup stays the coherent curated ladder without deleting a
|
|
6986
|
+
* model anyone may still be pinned to. `resolveModelForFormat` keeps honoring
|
|
6987
|
+
* an explicit legacy id that has a build for the node's format.
|
|
6988
|
+
*/
|
|
6989
|
+
legacy: boolean().optional(),
|
|
6990
|
+
/**
|
|
6991
|
+
* Measured quality/latency metadata — populated from the benchmark addon on
|
|
6992
|
+
* the real node classes. Absent = not yet measured (most entries today; the
|
|
6993
|
+
* catalog historically carried only `sizeMB`, a poor cross-architecture
|
|
6994
|
+
* speed proxy). `p95LatencyMs` is keyed by node class (e.g. `n100`, `mac`).
|
|
6995
|
+
*/
|
|
6996
|
+
metrics: object({
|
|
6997
|
+
map50: number().optional(),
|
|
6998
|
+
p95LatencyMs: record(string(), number()).optional()
|
|
6999
|
+
}).optional(),
|
|
7000
|
+
/**
|
|
7001
|
+
* SPDX-ish license id of the model weights (e.g. `AGPL-3.0` for Ultralytics
|
|
7002
|
+
* YOLO26, `GPL-3.0` for YOLOv9, `Apache-2.0` for D-FINE/RF-DETR). Matters for
|
|
7003
|
+
* the retraining addon and any future commercial distribution.
|
|
7004
|
+
*/
|
|
7005
|
+
license: string().optional(),
|
|
7006
|
+
/**
|
|
7007
|
+
* Variant-selector grouping. The UI groups models by `family` + `tier` and
|
|
7008
|
+
* offers `precision` / `optimization` as variant axes WITHIN a tier — so all
|
|
7009
|
+
* of a family's sizes and quantizations collapse into one grouped picker
|
|
7010
|
+
* instead of a flat list of `yolo26s`, `yolo26s-int8`, … Absent ⇒ ungrouped
|
|
7011
|
+
* (legacy / custom models) — never shown in the grouped selector. The flat
|
|
7012
|
+
* `id` stays the source of truth for resolution/download/persistence; grouping
|
|
7013
|
+
* is a presentation overlay resolved back to an `id`.
|
|
7014
|
+
*/
|
|
7015
|
+
group: ModelVariantGroupSchema.optional()
|
|
6938
7016
|
});
|
|
6939
7017
|
var ConvertTargetSchema = discriminatedUnion("format", [object({
|
|
6940
7018
|
format: literal("openvino"),
|
|
@@ -7170,8 +7248,8 @@ var RecordingModeSchema = _enum([
|
|
|
7170
7248
|
"onAudioThreshold"
|
|
7171
7249
|
]);
|
|
7172
7250
|
/**
|
|
7173
|
-
* First-class, authoritative per-camera storage mode — the
|
|
7174
|
-
* reads directly (never inferred from `rules`):
|
|
7251
|
+
* First-class, authoritative per-camera storage mode — the explicit choice the
|
|
7252
|
+
* UI reads directly (never inferred from `rules`):
|
|
7175
7253
|
* - `off` — not recording.
|
|
7176
7254
|
* - `events` — record only around triggers (motion / audio threshold),
|
|
7177
7255
|
* with pre/post-buffer.
|
|
@@ -8710,6 +8788,72 @@ function createRuntimeStateBridge(params) {
|
|
|
8710
8788
|
getStatus
|
|
8711
8789
|
};
|
|
8712
8790
|
}
|
|
8791
|
+
/** Reject after `ms`; always clears its own timer. */
|
|
8792
|
+
async function withTimeout$1(promise, ms, label) {
|
|
8793
|
+
let timer;
|
|
8794
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
8795
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`${label} timed out after ${String(ms)}ms`)), ms);
|
|
8796
|
+
});
|
|
8797
|
+
try {
|
|
8798
|
+
return await Promise.race([promise, timeout]);
|
|
8799
|
+
} finally {
|
|
8800
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
8801
|
+
}
|
|
8802
|
+
}
|
|
8803
|
+
/**
|
|
8804
|
+
* Start the reachability poll loop. Returns a handle whose `stop()` clears the
|
|
8805
|
+
* timer and prevents any further ticks. Start on device activation, stop on
|
|
8806
|
+
* device teardown (`removeDevice`) so no timer leaks.
|
|
8807
|
+
*/
|
|
8808
|
+
function startReachabilityPoll(options) {
|
|
8809
|
+
const intervalMs = options.intervalMs ?? 3e4;
|
|
8810
|
+
const failuresToOffline = options.failuresToOffline ?? 3;
|
|
8811
|
+
const probeTimeoutMs = options.probeTimeoutMs ?? 1e4;
|
|
8812
|
+
const runImmediately = options.runImmediately ?? true;
|
|
8813
|
+
let stopped = false;
|
|
8814
|
+
let running = false;
|
|
8815
|
+
let consecutiveFailures = 0;
|
|
8816
|
+
let timer;
|
|
8817
|
+
const tick = async () => {
|
|
8818
|
+
if (stopped) return;
|
|
8819
|
+
if (running) return;
|
|
8820
|
+
if (options.isEnabled && !options.isEnabled()) return;
|
|
8821
|
+
running = true;
|
|
8822
|
+
try {
|
|
8823
|
+
const reachable = await withTimeout$1(Promise.resolve().then(options.probe), probeTimeoutMs, "reachability probe");
|
|
8824
|
+
if (stopped) return;
|
|
8825
|
+
if (reachable) {
|
|
8826
|
+
consecutiveFailures = 0;
|
|
8827
|
+
options.setOnline(true);
|
|
8828
|
+
} else registerFailure("probe resolved unreachable");
|
|
8829
|
+
} catch (error) {
|
|
8830
|
+
if (stopped) return;
|
|
8831
|
+
registerFailure(error instanceof Error ? error.message : "probe threw");
|
|
8832
|
+
} finally {
|
|
8833
|
+
running = false;
|
|
8834
|
+
}
|
|
8835
|
+
};
|
|
8836
|
+
const registerFailure = (reason) => {
|
|
8837
|
+
consecutiveFailures += 1;
|
|
8838
|
+
options.logger?.debug("reachability probe failed", {
|
|
8839
|
+
reason,
|
|
8840
|
+
consecutiveFailures,
|
|
8841
|
+
failuresToOffline
|
|
8842
|
+
});
|
|
8843
|
+
if (consecutiveFailures >= failuresToOffline) options.setOnline(false);
|
|
8844
|
+
};
|
|
8845
|
+
timer = setInterval(() => {
|
|
8846
|
+
tick();
|
|
8847
|
+
}, intervalMs);
|
|
8848
|
+
if (typeof timer === "object" && timer !== null && "unref" in timer) timer.unref();
|
|
8849
|
+
if (runImmediately) tick();
|
|
8850
|
+
return { stop: () => {
|
|
8851
|
+
if (stopped) return;
|
|
8852
|
+
stopped = true;
|
|
8853
|
+
if (timer !== void 0) clearInterval(timer);
|
|
8854
|
+
timer = void 0;
|
|
8855
|
+
} };
|
|
8856
|
+
}
|
|
8713
8857
|
/**
|
|
8714
8858
|
* Generic device-level status snapshot. Auto-registered by `BaseDevice`
|
|
8715
8859
|
* for every device, regardless of provider — the kernel needs a uniform
|
|
@@ -9373,26 +9517,13 @@ onBrightnessChanged: { data: object({
|
|
|
9373
9517
|
*/
|
|
9374
9518
|
runtimeState: BrightnessStatusSchema
|
|
9375
9519
|
};
|
|
9520
|
+
/** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
|
|
9376
9521
|
var StreamFormatSchema = _enum([
|
|
9377
9522
|
"webrtc",
|
|
9378
9523
|
"hls",
|
|
9379
9524
|
"mjpeg",
|
|
9380
9525
|
"rtsp"
|
|
9381
9526
|
]);
|
|
9382
|
-
var StreamInfoSchema = object({
|
|
9383
|
-
streamId: string(),
|
|
9384
|
-
format: StreamFormatSchema,
|
|
9385
|
-
url: string().nullable(),
|
|
9386
|
-
active: boolean()
|
|
9387
|
-
});
|
|
9388
|
-
method(object({
|
|
9389
|
-
streamId: string(),
|
|
9390
|
-
sourceUrl: string(),
|
|
9391
|
-
codec: string().optional()
|
|
9392
|
-
}), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
|
|
9393
|
-
streamId: string(),
|
|
9394
|
-
format: StreamFormatSchema
|
|
9395
|
-
}), string().nullable()), method(_void(), array(StreamInfoSchema));
|
|
9396
9527
|
var RtspRestreamEntrySchema = object({
|
|
9397
9528
|
brokerId: string(),
|
|
9398
9529
|
url: string(),
|
|
@@ -10260,37 +10391,7 @@ var consumablesCapability = {
|
|
|
10260
10391
|
scope: "device",
|
|
10261
10392
|
deviceNative: true,
|
|
10262
10393
|
mode: "singleton",
|
|
10263
|
-
deviceTypes:
|
|
10264
|
-
DeviceType.Camera,
|
|
10265
|
-
DeviceType.Hub,
|
|
10266
|
-
DeviceType.Light,
|
|
10267
|
-
DeviceType.Siren,
|
|
10268
|
-
DeviceType.Switch,
|
|
10269
|
-
DeviceType.Sensor,
|
|
10270
|
-
DeviceType.Thermostat,
|
|
10271
|
-
DeviceType.Button,
|
|
10272
|
-
DeviceType.EventEmitter,
|
|
10273
|
-
DeviceType.Update,
|
|
10274
|
-
DeviceType.Generic,
|
|
10275
|
-
DeviceType.Notifier,
|
|
10276
|
-
DeviceType.Script,
|
|
10277
|
-
DeviceType.Automation,
|
|
10278
|
-
DeviceType.Lock,
|
|
10279
|
-
DeviceType.Cover,
|
|
10280
|
-
DeviceType.Valve,
|
|
10281
|
-
DeviceType.Humidifier,
|
|
10282
|
-
DeviceType.WaterHeater,
|
|
10283
|
-
DeviceType.Fan,
|
|
10284
|
-
DeviceType.MediaPlayer,
|
|
10285
|
-
DeviceType.AlarmPanel,
|
|
10286
|
-
DeviceType.Control,
|
|
10287
|
-
DeviceType.Presence,
|
|
10288
|
-
DeviceType.Weather,
|
|
10289
|
-
DeviceType.Vacuum,
|
|
10290
|
-
DeviceType.LawnMower,
|
|
10291
|
-
DeviceType.Container,
|
|
10292
|
-
DeviceType.Image
|
|
10293
|
-
],
|
|
10394
|
+
deviceTypes: Object.values(DeviceType),
|
|
10294
10395
|
deviceConfig: { ui: {
|
|
10295
10396
|
kind: "widget",
|
|
10296
10397
|
widgetId: "host/consumables-panel",
|
|
@@ -11883,7 +11984,6 @@ var PipelineDefaultStepSchema = lazy(() => object({
|
|
|
11883
11984
|
enabled: boolean(),
|
|
11884
11985
|
modelId: string(),
|
|
11885
11986
|
children: array(PipelineDefaultStepSchema).readonly(),
|
|
11886
|
-
engine: PipelineEngineChoiceSchema.optional(),
|
|
11887
11987
|
group: string().optional(),
|
|
11888
11988
|
settings: record(string(), unknown()).optional()
|
|
11889
11989
|
}));
|
|
@@ -11908,7 +12008,9 @@ var PipelineModelOptionSchema = object({
|
|
|
11908
12008
|
formats: record(string(), object({
|
|
11909
12009
|
downloaded: boolean(),
|
|
11910
12010
|
sizeMB: number()
|
|
11911
|
-
}))
|
|
12011
|
+
})),
|
|
12012
|
+
group: ModelVariantGroupSchema.optional(),
|
|
12013
|
+
legacy: boolean().optional()
|
|
11912
12014
|
});
|
|
11913
12015
|
var ConfigFieldBridge = custom();
|
|
11914
12016
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -11959,15 +12061,42 @@ var EngineProvisioningSchema = object({
|
|
|
11959
12061
|
]),
|
|
11960
12062
|
progress: number().optional(),
|
|
11961
12063
|
error: string().optional(),
|
|
11962
|
-
nextRetryAt: number().optional()
|
|
12064
|
+
nextRetryAt: number().optional(),
|
|
12065
|
+
/**
|
|
12066
|
+
* Gate A (config-correctness gate at engine change): human-readable
|
|
12067
|
+
* config issues surfaced EAGERLY when the node's engine changes — model
|
|
12068
|
+
* substitutions ("chose X, running Y") and zero-build steps ("no model
|
|
12069
|
+
* has a <format> build"). Additive/optional: informational only, never
|
|
12070
|
+
* enforced here — `assertEngineReady` (readiness) still gates inference.
|
|
12071
|
+
* Absent/empty when the node-default tree resolves cleanly.
|
|
12072
|
+
*/
|
|
12073
|
+
configIssues: array(string()).optional()
|
|
11963
12074
|
});
|
|
11964
12075
|
var PipelineStepInputSchema = lazy(() => object({
|
|
11965
12076
|
addonId: string(),
|
|
11966
|
-
modelId: string(),
|
|
12077
|
+
modelId: string().optional(),
|
|
11967
12078
|
enabled: boolean().default(true),
|
|
11968
12079
|
children: array(PipelineStepInputSchema).optional(),
|
|
11969
12080
|
settings: record(string(), unknown()).optional()
|
|
11970
12081
|
}));
|
|
12082
|
+
var ModelSubstitutionSchema = object({
|
|
12083
|
+
addonId: string(),
|
|
12084
|
+
chosen: string(),
|
|
12085
|
+
running: string(),
|
|
12086
|
+
format: string()
|
|
12087
|
+
});
|
|
12088
|
+
var PipelineValidationIssueSchema = object({
|
|
12089
|
+
addonId: string(),
|
|
12090
|
+
kind: _enum(["unknown-addon", "no-format-build"]),
|
|
12091
|
+
detail: string()
|
|
12092
|
+
});
|
|
12093
|
+
var PipelineValidationResultSchema = object({
|
|
12094
|
+
ok: boolean(),
|
|
12095
|
+
issues: array(PipelineValidationIssueSchema).readonly(),
|
|
12096
|
+
substitutions: array(ModelSubstitutionSchema).readonly(),
|
|
12097
|
+
/** The node's `currentEngine.format` this validation ran against. */
|
|
12098
|
+
format: string()
|
|
12099
|
+
});
|
|
11971
12100
|
var ReferenceImageEntrySchema = object({
|
|
11972
12101
|
filename: string(),
|
|
11973
12102
|
stepIds: array(string()).readonly().optional()
|
|
@@ -12038,7 +12167,13 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
12038
12167
|
})) }), object({ success: literal(true) }), {
|
|
12039
12168
|
kind: "mutation",
|
|
12040
12169
|
auth: "admin"
|
|
12041
|
-
}), method(
|
|
12170
|
+
}), method(object({ nodeId: string() }), object({
|
|
12171
|
+
success: literal(true),
|
|
12172
|
+
regeneratedModelId: string().nullable()
|
|
12173
|
+
}), {
|
|
12174
|
+
kind: "mutation",
|
|
12175
|
+
auth: "admin"
|
|
12176
|
+
}), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
|
|
12042
12177
|
name: string(),
|
|
12043
12178
|
steps: array(PipelineTemplateStepSchema).readonly(),
|
|
12044
12179
|
engine: PipelineEngineChoiceSchema
|
|
@@ -12331,6 +12466,13 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
|
|
|
12331
12466
|
kind: literal("remote-restream"),
|
|
12332
12467
|
/** The camera's source-owner node (slice 1: always the hub). */
|
|
12333
12468
|
ownerNodeId: string(),
|
|
12469
|
+
/**
|
|
12470
|
+
* The owner's LAN-reachable host, resolved by the orchestrator from the
|
|
12471
|
+
* per-node `reachableHost` override (Cluster UI). When present the runner
|
|
12472
|
+
* dials THIS host for the owner's restream, in preference to the
|
|
12473
|
+
* `CAMSTACK_HUB_URL`-derived default. Absent → auto-detect fallback.
|
|
12474
|
+
*/
|
|
12475
|
+
ownerReachableHost: string().optional(),
|
|
12334
12476
|
/** Operator override for the owner host the runner dials. */
|
|
12335
12477
|
hubHostnameOverride: string().optional()
|
|
12336
12478
|
})]).describe("Per-camera frame-source mode for the runner (P2c)");
|
|
@@ -12339,13 +12481,11 @@ var RunnerFrameSourceSchema = discriminatedUnion("kind", [object({ kind: literal
|
|
|
12339
12481
|
* specific runner instance via `attachCamera`. Carries everything the
|
|
12340
12482
|
* runner needs to subscribe to the local broker and execute inference.
|
|
12341
12483
|
*
|
|
12342
|
-
* Stateless-pipeline model: the
|
|
12343
|
-
*
|
|
12344
|
-
*
|
|
12345
|
-
*
|
|
12346
|
-
*
|
|
12347
|
-
* `engine`/`steps`/`audio` are optional during the additive migration
|
|
12348
|
-
* window; once orchestrator + UI are migrated they become required.
|
|
12484
|
+
* Stateless-pipeline model: the pipeline content (`steps`, optional
|
|
12485
|
+
* `audio`) travels with the attach payload. The runner keeps it in RAM
|
|
12486
|
+
* for the lifetime of the attach — on rebalance, edit, or restart the
|
|
12487
|
+
* orchestrator re-sends the latest snapshot. Engine is NOT carried: it is
|
|
12488
|
+
* node-local, resolved by the executing runner at dispatch time.
|
|
12349
12489
|
*/
|
|
12350
12490
|
var RunnerCameraConfigSchema = object({
|
|
12351
12491
|
deviceId: number(),
|
|
@@ -12396,14 +12536,11 @@ var RunnerCameraConfigSchema = object({
|
|
|
12396
12536
|
*/
|
|
12397
12537
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
12398
12538
|
pipelineEnabled: boolean().default(true),
|
|
12399
|
-
/** Engine choice for video steps (runtime+backend+format). */
|
|
12400
|
-
engine: PipelineEngineChoiceSchema.optional(),
|
|
12401
12539
|
/** Ordered tree of video steps. Absent → runner skips video detection. */
|
|
12402
12540
|
steps: array(PipelineStepInputSchema).readonly().optional(),
|
|
12403
12541
|
/** Audio classification branch. `enabled:false` disables, null skips. */
|
|
12404
12542
|
audio: object({
|
|
12405
|
-
|
|
12406
|
-
modelId: string(),
|
|
12543
|
+
modelId: string().optional(),
|
|
12407
12544
|
enabled: boolean()
|
|
12408
12545
|
}).nullable().optional(),
|
|
12409
12546
|
/**
|
|
@@ -18278,11 +18415,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18278
18415
|
timestamp: number()
|
|
18279
18416
|
});
|
|
18280
18417
|
var CameraPipelineConfigSchema = object({
|
|
18281
|
-
engine: PipelineEngineChoiceSchema,
|
|
18418
|
+
engine: PipelineEngineChoiceSchema.optional(),
|
|
18282
18419
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
18283
18420
|
audio: object({
|
|
18284
|
-
engine: PipelineEngineChoiceSchema,
|
|
18285
|
-
modelId: string(),
|
|
18421
|
+
engine: PipelineEngineChoiceSchema.optional(),
|
|
18422
|
+
modelId: string().optional(),
|
|
18286
18423
|
enabled: boolean(),
|
|
18287
18424
|
settings: record(string(), unknown()).readonly().optional()
|
|
18288
18425
|
}).nullable().optional()
|
|
@@ -18297,7 +18434,7 @@ var PipelineTemplateSchema = object({
|
|
|
18297
18434
|
});
|
|
18298
18435
|
var AgentAddonConfigSchema = object({
|
|
18299
18436
|
enabled: boolean(),
|
|
18300
|
-
modelId: string(),
|
|
18437
|
+
modelId: string().optional(),
|
|
18301
18438
|
settings: record(string(), unknown()).readonly()
|
|
18302
18439
|
});
|
|
18303
18440
|
var AgentPipelineSettingsSchema = object({
|
|
@@ -18312,7 +18449,15 @@ var AgentPipelineSettingsSchema = object({
|
|
|
18312
18449
|
/** Node is eligible to run audio-analyzer sessions. */
|
|
18313
18450
|
audio: boolean().optional(),
|
|
18314
18451
|
/** Node is eligible to be the ingest / source-owner (serve the restream). */
|
|
18315
|
-
ingest: boolean().optional()
|
|
18452
|
+
ingest: boolean().optional(),
|
|
18453
|
+
/**
|
|
18454
|
+
* Operator override for the LAN host a cross-node decoder dials to reach
|
|
18455
|
+
* THIS node's restream (Cluster UI). Absent → auto-detect: a remote runner
|
|
18456
|
+
* falls back to its `CAMSTACK_HUB_URL`-derived host (the Moleculer address
|
|
18457
|
+
* it already uses to reach the hub). Set this only when the auto-detected
|
|
18458
|
+
* address is wrong (multi-homed host, NAT, custom interface).
|
|
18459
|
+
*/
|
|
18460
|
+
reachableHost: string().optional()
|
|
18316
18461
|
});
|
|
18317
18462
|
var CameraPipelineForAgentSchema = object({
|
|
18318
18463
|
steps: array(PipelineStepInputSchema).readonly(),
|
|
@@ -18418,6 +18563,15 @@ var GlobalMetricsSchema = object({
|
|
|
18418
18563
|
* capability providers.
|
|
18419
18564
|
*/
|
|
18420
18565
|
var CapabilityBindingsSchema = record(string(), string());
|
|
18566
|
+
/**
|
|
18567
|
+
* The cluster's single camera-source owner (`clusterRoles.ingestNode`) plus
|
|
18568
|
+
* its LAN-reachable host, if one is registered. See `getIngestOwner`.
|
|
18569
|
+
*/
|
|
18570
|
+
var IngestOwnerSchema = object({
|
|
18571
|
+
ownerNodeId: string(),
|
|
18572
|
+
reachableHost: string().optional(),
|
|
18573
|
+
configIssue: string().optional()
|
|
18574
|
+
});
|
|
18421
18575
|
/** Source block — always present; derives from the stream catalog. */
|
|
18422
18576
|
var CameraSourceStatusSchema = object({ streams: array(object({
|
|
18423
18577
|
camStreamId: string(),
|
|
@@ -18432,6 +18586,14 @@ var CameraAssignmentStatusSchema = object({
|
|
|
18432
18586
|
detectionNodeId: string().nullable(),
|
|
18433
18587
|
decoderNodeId: string().nullable(),
|
|
18434
18588
|
audioNodeId: string().nullable(),
|
|
18589
|
+
/**
|
|
18590
|
+
* The node that OWNS this camera's physical source pull (dials the RTSP and
|
|
18591
|
+
* hosts the broker/restream) — the cluster ingest owner today
|
|
18592
|
+
* (`clusterRoles.ingestNode`), per-camera once source assignment lands. Lets
|
|
18593
|
+
* the UI show WHERE a camera is sourced without SSH/logs, and is the node the
|
|
18594
|
+
* broker block below was read from (pinned). Nullable only pre-wiring.
|
|
18595
|
+
*/
|
|
18596
|
+
sourceNodeId: string().nullable(),
|
|
18435
18597
|
pinned: object({
|
|
18436
18598
|
detection: boolean(),
|
|
18437
18599
|
decoder: boolean(),
|
|
@@ -18564,7 +18726,7 @@ method(object({
|
|
|
18564
18726
|
}), object({ success: literal(true) }), {
|
|
18565
18727
|
kind: "mutation",
|
|
18566
18728
|
auth: "admin"
|
|
18567
|
-
}), method(object({
|
|
18729
|
+
}), method(_void(), IngestOwnerSchema), method(object({
|
|
18568
18730
|
deviceId: number(),
|
|
18569
18731
|
nodeId: string()
|
|
18570
18732
|
}), _void(), {
|
|
@@ -18633,6 +18795,12 @@ method(object({
|
|
|
18633
18795
|
}), object({ success: literal(true) }), {
|
|
18634
18796
|
kind: "mutation",
|
|
18635
18797
|
auth: "admin"
|
|
18798
|
+
}), method(object({
|
|
18799
|
+
agentNodeId: string(),
|
|
18800
|
+
reachableHost: string().nullable()
|
|
18801
|
+
}), object({ success: literal(true) }), {
|
|
18802
|
+
kind: "mutation",
|
|
18803
|
+
auth: "admin"
|
|
18636
18804
|
}), method(object({ deviceId: number() }), CameraPipelineSettingsSchema.nullable()), method(object({
|
|
18637
18805
|
deviceId: number(),
|
|
18638
18806
|
addonId: string(),
|
|
@@ -18677,22 +18845,6 @@ method(object({
|
|
|
18677
18845
|
kind: "mutation",
|
|
18678
18846
|
auth: "admin"
|
|
18679
18847
|
});
|
|
18680
|
-
var RegisteredStreamSchema = object({
|
|
18681
|
-
streamId: string(),
|
|
18682
|
-
label: string().optional(),
|
|
18683
|
-
codec: string(),
|
|
18684
|
-
type: _enum(["video", "audio"]),
|
|
18685
|
-
sourceUrl: string()
|
|
18686
|
-
});
|
|
18687
|
-
var ExposedResourceSchema = object({
|
|
18688
|
-
streamId: string(),
|
|
18689
|
-
format: string(),
|
|
18690
|
-
value: string()
|
|
18691
|
-
});
|
|
18692
|
-
method(object({
|
|
18693
|
-
deviceId: number(),
|
|
18694
|
-
streams: array(RegisteredStreamSchema).readonly()
|
|
18695
|
-
}), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), { kind: "mutation" }), method(object({ deviceId: number() }), array(ExposedResourceSchema).readonly());
|
|
18696
18848
|
/**
|
|
18697
18849
|
* Query filter for settings-store collections.
|
|
18698
18850
|
*/
|
|
@@ -18845,9 +18997,9 @@ method(SendEmailInputSchema, SendEmailResultSchema, {
|
|
|
18845
18997
|
/**
|
|
18846
18998
|
* A single device snapshot returned as base64 JPEG/PNG.
|
|
18847
18999
|
*
|
|
18848
|
-
*
|
|
18849
|
-
*
|
|
18850
|
-
*
|
|
19000
|
+
* The `SnapshotAddon` wrapper returns this shape whether the frame came from
|
|
19001
|
+
* the device-native provider (onboard capture) or from the stream-broker
|
|
19002
|
+
* prebuffer fallback.
|
|
18851
19003
|
*/
|
|
18852
19004
|
var SnapshotImageSchema = object({
|
|
18853
19005
|
base64: string(),
|
|
@@ -18922,10 +19074,6 @@ var snapshotCapability = {
|
|
|
18922
19074
|
kind: "poll"
|
|
18923
19075
|
}
|
|
18924
19076
|
};
|
|
18925
|
-
method(object({ deviceId: number() }), boolean()), method(object({
|
|
18926
|
-
deviceId: number(),
|
|
18927
|
-
streamId: string().optional()
|
|
18928
|
-
}), SnapshotImageSchema.nullable());
|
|
18929
19077
|
/**
|
|
18930
19078
|
* `sso-bridge` — internal hub-only cap that lets SSO-style auth
|
|
18931
19079
|
* providers (OIDC, SAML, magic-link, …) mint an HMAC-signed token
|
|
@@ -19286,9 +19434,10 @@ method(object({
|
|
|
19286
19434
|
auth: "admin"
|
|
19287
19435
|
});
|
|
19288
19436
|
/**
|
|
19289
|
-
* Optional client-side hints sent at session creation to help the
|
|
19290
|
-
*
|
|
19291
|
-
*
|
|
19437
|
+
* Optional client-side hints sent at session creation to help the provider
|
|
19438
|
+
* pick the best native source. All fields optional — a viewer that knows
|
|
19439
|
+
* nothing still gets a sane default. (Relocated from the retired `webrtc`
|
|
19440
|
+
* collection cap; this `webrtc-session` cap is the live signaling surface.)
|
|
19292
19441
|
*/
|
|
19293
19442
|
var webrtcClientHintsSchema = object({
|
|
19294
19443
|
viewportWidth: number().int().positive().optional(),
|
|
@@ -19299,22 +19448,6 @@ var webrtcClientHintsSchema = object({
|
|
|
19299
19448
|
/** Hard tier override; takes precedence over scoring when registered. */
|
|
19300
19449
|
prefersTier: string().optional()
|
|
19301
19450
|
}).partial();
|
|
19302
|
-
method(object({
|
|
19303
|
-
streamId: string(),
|
|
19304
|
-
sdpOffer: string()
|
|
19305
|
-
}), string(), { kind: "mutation" }), method(object({ streamId: string() }), boolean()), method(object({
|
|
19306
|
-
streamId: string(),
|
|
19307
|
-
codec: string()
|
|
19308
|
-
}), _void(), { kind: "mutation" }), method(object({ streamId: string() }), _void(), { kind: "mutation" }), method(object({
|
|
19309
|
-
streamId: string(),
|
|
19310
|
-
hints: webrtcClientHintsSchema.optional()
|
|
19311
|
-
}), object({
|
|
19312
|
-
sessionId: string(),
|
|
19313
|
-
sdpOffer: string()
|
|
19314
|
-
}), { kind: "mutation" }), method(object({
|
|
19315
|
-
sessionId: string(),
|
|
19316
|
-
sdpAnswer: string()
|
|
19317
|
-
}), _void(), { kind: "mutation" }), method(object({ sessionId: string() }), _void(), { kind: "mutation" }), method(object({ streamId: string() }), boolean());
|
|
19318
19451
|
/**
|
|
19319
19452
|
* Discriminated target for a WebRTC session. The client sends this
|
|
19320
19453
|
* structured object instead of building / parsing brokerId strings;
|
|
@@ -19801,7 +19934,15 @@ var FrameworkPackageStatusSchema = object({
|
|
|
19801
19934
|
latestVersion: string().nullable(),
|
|
19802
19935
|
hasUpdate: boolean(),
|
|
19803
19936
|
/** Optional manifest description for the row tooltip. */
|
|
19804
|
-
description: string().optional()
|
|
19937
|
+
description: string().optional(),
|
|
19938
|
+
/**
|
|
19939
|
+
* Content build-id (md5 of the resolved `dist/` tree) of the code the hub
|
|
19940
|
+
* ACTUALLY loaded. Framework packages ship code changes without always
|
|
19941
|
+
* bumping `currentVersion`, so semver alone hides "same version, new code".
|
|
19942
|
+
* `null` when the dist can't be hashed (not installed / empty). The admin-UI
|
|
19943
|
+
* surfaces this so a stale-code hub is visible even at an unchanged version.
|
|
19944
|
+
*/
|
|
19945
|
+
buildId: string().nullable()
|
|
19805
19946
|
});
|
|
19806
19947
|
var LogStreamEntrySchema = object({
|
|
19807
19948
|
timestamp: string(),
|
|
@@ -24724,6 +24865,12 @@ Object.freeze({
|
|
|
24724
24865
|
addonId: null,
|
|
24725
24866
|
access: "create"
|
|
24726
24867
|
},
|
|
24868
|
+
"pipelineExecutor.resetToDefault": {
|
|
24869
|
+
capName: "pipeline-executor",
|
|
24870
|
+
capScope: "system",
|
|
24871
|
+
addonId: null,
|
|
24872
|
+
access: "delete"
|
|
24873
|
+
},
|
|
24727
24874
|
"pipelineExecutor.runAudioTest": {
|
|
24728
24875
|
capName: "pipeline-executor",
|
|
24729
24876
|
capScope: "system",
|
|
@@ -24772,6 +24919,12 @@ Object.freeze({
|
|
|
24772
24919
|
addonId: null,
|
|
24773
24920
|
access: "create"
|
|
24774
24921
|
},
|
|
24922
|
+
"pipelineExecutor.validatePipeline": {
|
|
24923
|
+
capName: "pipeline-executor",
|
|
24924
|
+
capScope: "system",
|
|
24925
|
+
addonId: null,
|
|
24926
|
+
access: "view"
|
|
24927
|
+
},
|
|
24775
24928
|
"pipelineOrchestrator.assignAudio": {
|
|
24776
24929
|
capName: "pipeline-orchestrator",
|
|
24777
24930
|
capScope: "system",
|
|
@@ -24880,6 +25033,12 @@ Object.freeze({
|
|
|
24880
25033
|
addonId: null,
|
|
24881
25034
|
access: "view"
|
|
24882
25035
|
},
|
|
25036
|
+
"pipelineOrchestrator.getIngestOwner": {
|
|
25037
|
+
capName: "pipeline-orchestrator",
|
|
25038
|
+
capScope: "system",
|
|
25039
|
+
addonId: null,
|
|
25040
|
+
access: "view"
|
|
25041
|
+
},
|
|
24883
25042
|
"pipelineOrchestrator.getPipelineAssignment": {
|
|
24884
25043
|
capName: "pipeline-orchestrator",
|
|
24885
25044
|
capScope: "system",
|
|
@@ -24952,6 +25111,12 @@ Object.freeze({
|
|
|
24952
25111
|
addonId: null,
|
|
24953
25112
|
access: "create"
|
|
24954
25113
|
},
|
|
25114
|
+
"pipelineOrchestrator.setAgentReachableHost": {
|
|
25115
|
+
capName: "pipeline-orchestrator",
|
|
25116
|
+
capScope: "system",
|
|
25117
|
+
addonId: null,
|
|
25118
|
+
access: "create"
|
|
25119
|
+
},
|
|
24955
25120
|
"pipelineOrchestrator.setCameraPipelineForAgent": {
|
|
24956
25121
|
capName: "pipeline-orchestrator",
|
|
24957
25122
|
capScope: "system",
|
|
@@ -25282,24 +25447,6 @@ Object.freeze({
|
|
|
25282
25447
|
addonId: null,
|
|
25283
25448
|
access: "create"
|
|
25284
25449
|
},
|
|
25285
|
-
"restreamer.getExposedResources": {
|
|
25286
|
-
capName: "restreamer",
|
|
25287
|
-
capScope: "system",
|
|
25288
|
-
addonId: null,
|
|
25289
|
-
access: "view"
|
|
25290
|
-
},
|
|
25291
|
-
"restreamer.registerDevice": {
|
|
25292
|
-
capName: "restreamer",
|
|
25293
|
-
capScope: "system",
|
|
25294
|
-
addonId: null,
|
|
25295
|
-
access: "create"
|
|
25296
|
-
},
|
|
25297
|
-
"restreamer.unregisterDevice": {
|
|
25298
|
-
capName: "restreamer",
|
|
25299
|
-
capScope: "system",
|
|
25300
|
-
addonId: null,
|
|
25301
|
-
access: "delete"
|
|
25302
|
-
},
|
|
25303
25450
|
"scriptRunner.run": {
|
|
25304
25451
|
capName: "script-runner",
|
|
25305
25452
|
capScope: "device",
|
|
@@ -25402,18 +25549,6 @@ Object.freeze({
|
|
|
25402
25549
|
addonId: null,
|
|
25403
25550
|
access: "create"
|
|
25404
25551
|
},
|
|
25405
|
-
"snapshotProvider.getSnapshot": {
|
|
25406
|
-
capName: "snapshot-provider",
|
|
25407
|
-
capScope: "system",
|
|
25408
|
-
addonId: null,
|
|
25409
|
-
access: "view"
|
|
25410
|
-
},
|
|
25411
|
-
"snapshotProvider.supportsDevice": {
|
|
25412
|
-
capName: "snapshot-provider",
|
|
25413
|
-
capScope: "system",
|
|
25414
|
-
addonId: null,
|
|
25415
|
-
access: "view"
|
|
25416
|
-
},
|
|
25417
25552
|
"ssoBridge.signBridgeToken": {
|
|
25418
25553
|
capName: "sso-bridge",
|
|
25419
25554
|
capScope: "system",
|
|
@@ -25840,30 +25975,6 @@ Object.freeze({
|
|
|
25840
25975
|
addonId: null,
|
|
25841
25976
|
access: "view"
|
|
25842
25977
|
},
|
|
25843
|
-
"streamingEngine.getStreamUrl": {
|
|
25844
|
-
capName: "streaming-engine",
|
|
25845
|
-
capScope: "system",
|
|
25846
|
-
addonId: null,
|
|
25847
|
-
access: "view"
|
|
25848
|
-
},
|
|
25849
|
-
"streamingEngine.listStreams": {
|
|
25850
|
-
capName: "streaming-engine",
|
|
25851
|
-
capScope: "system",
|
|
25852
|
-
addonId: null,
|
|
25853
|
-
access: "view"
|
|
25854
|
-
},
|
|
25855
|
-
"streamingEngine.registerStream": {
|
|
25856
|
-
capName: "streaming-engine",
|
|
25857
|
-
capScope: "system",
|
|
25858
|
-
addonId: null,
|
|
25859
|
-
access: "create"
|
|
25860
|
-
},
|
|
25861
|
-
"streamingEngine.unregisterStream": {
|
|
25862
|
-
capName: "streaming-engine",
|
|
25863
|
-
capScope: "system",
|
|
25864
|
-
addonId: null,
|
|
25865
|
-
access: "delete"
|
|
25866
|
-
},
|
|
25867
25978
|
"streamParams.getConfigSchema": {
|
|
25868
25979
|
capName: "stream-params",
|
|
25869
25980
|
capScope: "device",
|
|
@@ -26230,54 +26341,6 @@ Object.freeze({
|
|
|
26230
26341
|
addonId: null,
|
|
26231
26342
|
access: "create"
|
|
26232
26343
|
},
|
|
26233
|
-
"webrtc.closeSession": {
|
|
26234
|
-
capName: "webrtc",
|
|
26235
|
-
capScope: "system",
|
|
26236
|
-
addonId: null,
|
|
26237
|
-
access: "create"
|
|
26238
|
-
},
|
|
26239
|
-
"webrtc.createSession": {
|
|
26240
|
-
capName: "webrtc",
|
|
26241
|
-
capScope: "system",
|
|
26242
|
-
addonId: null,
|
|
26243
|
-
access: "create"
|
|
26244
|
-
},
|
|
26245
|
-
"webrtc.handleAnswer": {
|
|
26246
|
-
capName: "webrtc",
|
|
26247
|
-
capScope: "system",
|
|
26248
|
-
addonId: null,
|
|
26249
|
-
access: "create"
|
|
26250
|
-
},
|
|
26251
|
-
"webrtc.handleOffer": {
|
|
26252
|
-
capName: "webrtc",
|
|
26253
|
-
capScope: "system",
|
|
26254
|
-
addonId: null,
|
|
26255
|
-
access: "create"
|
|
26256
|
-
},
|
|
26257
|
-
"webrtc.hasAdaptiveBitrate": {
|
|
26258
|
-
capName: "webrtc",
|
|
26259
|
-
capScope: "system",
|
|
26260
|
-
addonId: null,
|
|
26261
|
-
access: "view"
|
|
26262
|
-
},
|
|
26263
|
-
"webrtc.registerStream": {
|
|
26264
|
-
capName: "webrtc",
|
|
26265
|
-
capScope: "system",
|
|
26266
|
-
addonId: null,
|
|
26267
|
-
access: "create"
|
|
26268
|
-
},
|
|
26269
|
-
"webrtc.supportsStream": {
|
|
26270
|
-
capName: "webrtc",
|
|
26271
|
-
capScope: "system",
|
|
26272
|
-
addonId: null,
|
|
26273
|
-
access: "view"
|
|
26274
|
-
},
|
|
26275
|
-
"webrtc.unregisterStream": {
|
|
26276
|
-
capName: "webrtc",
|
|
26277
|
-
capScope: "system",
|
|
26278
|
-
addonId: null,
|
|
26279
|
-
access: "delete"
|
|
26280
|
-
},
|
|
26281
26344
|
"webrtcSession.addIceCandidate": {
|
|
26282
26345
|
capName: "webrtc-session",
|
|
26283
26346
|
capScope: "device",
|
|
@@ -30343,6 +30406,10 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
30343
30406
|
* universal overlay UI. */
|
|
30344
30407
|
osdPollTimer = null;
|
|
30345
30408
|
static OSD_POLL_INTERVAL_MS = 3e5;
|
|
30409
|
+
/** Control-plane reachability poll — drives `device.online` from ISAPI
|
|
30410
|
+
* `getDeviceInfo` liveness, decoupled from stream-broker video health.
|
|
30411
|
+
* Started in `onActivate`, stopped in `removeDevice`. */
|
|
30412
|
+
reachabilityPoll = null;
|
|
30346
30413
|
constructor(ctx) {
|
|
30347
30414
|
super(ctx, hikvisionCameraSchema, { type: DeviceType.Camera });
|
|
30348
30415
|
this.registerNativeCapabilities();
|
|
@@ -30827,6 +30894,25 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
30827
30894
|
if (this.osdPollTimer === null) this.osdPollTimer = setInterval(() => {
|
|
30828
30895
|
this.refreshOsdSnapshot();
|
|
30829
30896
|
}, HikvisionCamera.OSD_POLL_INTERVAL_MS);
|
|
30897
|
+
this.startReachabilityPolling();
|
|
30898
|
+
}
|
|
30899
|
+
/** Start the control-plane reachability poll: an ISAPI `getDeviceInfo`
|
|
30900
|
+
* round-trip every 30s drives `device.online`, with hysteresis. This
|
|
30901
|
+
* replaces the old stream-health→online mirror so an on-demand (idle)
|
|
30902
|
+
* camera that is reachable still reports ONLINE. Idempotent. */
|
|
30903
|
+
startReachabilityPolling() {
|
|
30904
|
+
if (this.reachabilityPoll) return;
|
|
30905
|
+
this.reachabilityPoll = startReachabilityPoll({
|
|
30906
|
+
probe: async () => {
|
|
30907
|
+
await this.ensureClient().getDeviceInfo();
|
|
30908
|
+
return true;
|
|
30909
|
+
},
|
|
30910
|
+
setOnline: (online) => {
|
|
30911
|
+
this.markOnline(online);
|
|
30912
|
+
},
|
|
30913
|
+
isEnabled: () => !this.disabled,
|
|
30914
|
+
logger: this.ctx.logger
|
|
30915
|
+
});
|
|
30830
30916
|
}
|
|
30831
30917
|
/** Single-flight OSD config fetch; updates the in-memory snapshot
|
|
30832
30918
|
* consumed by the readonly OSD section in `getSettingsUISchema`. */
|
|
@@ -33219,6 +33305,8 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
33219
33305
|
clearInterval(this.osdPollTimer);
|
|
33220
33306
|
this.osdPollTimer = null;
|
|
33221
33307
|
}
|
|
33308
|
+
this.reachabilityPoll?.stop();
|
|
33309
|
+
this.reachabilityPoll = null;
|
|
33222
33310
|
await this.disconnectAll();
|
|
33223
33311
|
for (const camStreamId of this.published.keys()) try {
|
|
33224
33312
|
await this.ctx.api.streamBroker.retractCameraStream.mutate({
|
|
@@ -42581,21 +42669,7 @@ var HikvisionProviderAddon = class extends BaseDeviceProvider {
|
|
|
42581
42669
|
throw new Error(`Hikvision: ${reason}`);
|
|
42582
42670
|
}
|
|
42583
42671
|
async onInitialize() {
|
|
42584
|
-
|
|
42585
|
-
this.subscribe({ category: EventCategory.DeviceStateChanged }, (event) => {
|
|
42586
|
-
const data = event.data;
|
|
42587
|
-
if (data.capName !== "camera-streams") return;
|
|
42588
|
-
const deviceId = data.deviceId;
|
|
42589
|
-
if (typeof deviceId !== "number") return;
|
|
42590
|
-
const registry = this.ctx.kernel.deviceRegistry;
|
|
42591
|
-
if (!registry) return;
|
|
42592
|
-
if (registry.getAddonId(deviceId) !== this.addonId) return;
|
|
42593
|
-
const device = registry.getById(deviceId);
|
|
42594
|
-
if (!device) return;
|
|
42595
|
-
const online = data.slice?.online === true;
|
|
42596
|
-
if (device.online !== online) device.online = online;
|
|
42597
|
-
});
|
|
42598
|
-
return regs;
|
|
42672
|
+
return await super.onInitialize();
|
|
42599
42673
|
}
|
|
42600
42674
|
async supportsDiscovery() {
|
|
42601
42675
|
return true;
|