@camstack/addon-provider-hikvision 1.2.141 → 1.2.143
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 +257 -79
- package/dist/addon.mjs +257 -79
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -10802,6 +10802,57 @@ var ClipSearchStrictnessSchema = _enum([
|
|
|
10802
10802
|
"balanced",
|
|
10803
10803
|
"strict"
|
|
10804
10804
|
]);
|
|
10805
|
+
new Set([
|
|
10806
|
+
{
|
|
10807
|
+
id: "person",
|
|
10808
|
+
name: "Person"
|
|
10809
|
+
},
|
|
10810
|
+
{
|
|
10811
|
+
id: "vehicle",
|
|
10812
|
+
name: "Vehicle"
|
|
10813
|
+
},
|
|
10814
|
+
{
|
|
10815
|
+
id: "animal",
|
|
10816
|
+
name: "Animal"
|
|
10817
|
+
},
|
|
10818
|
+
{
|
|
10819
|
+
id: "package",
|
|
10820
|
+
name: "Package"
|
|
10821
|
+
}
|
|
10822
|
+
].map((l) => l.id));
|
|
10823
|
+
var COCO_TO_MACRO = {
|
|
10824
|
+
mapping: {
|
|
10825
|
+
person: "person",
|
|
10826
|
+
bicycle: "vehicle",
|
|
10827
|
+
car: "vehicle",
|
|
10828
|
+
motorcycle: "vehicle",
|
|
10829
|
+
airplane: "vehicle",
|
|
10830
|
+
bus: "vehicle",
|
|
10831
|
+
train: "vehicle",
|
|
10832
|
+
truck: "vehicle",
|
|
10833
|
+
boat: "vehicle",
|
|
10834
|
+
bird: "animal",
|
|
10835
|
+
cat: "animal",
|
|
10836
|
+
dog: "animal",
|
|
10837
|
+
horse: "animal",
|
|
10838
|
+
sheep: "animal",
|
|
10839
|
+
cow: "animal",
|
|
10840
|
+
elephant: "animal",
|
|
10841
|
+
bear: "animal",
|
|
10842
|
+
zebra: "animal",
|
|
10843
|
+
giraffe: "animal",
|
|
10844
|
+
suitcase: "package",
|
|
10845
|
+
backpack: "package",
|
|
10846
|
+
handbag: "package"
|
|
10847
|
+
},
|
|
10848
|
+
preserveOriginal: false
|
|
10849
|
+
};
|
|
10850
|
+
/** Steps a root detector may declare it fills. */
|
|
10851
|
+
var FILLABLE_STEP_IDS = [
|
|
10852
|
+
"animal-classifier",
|
|
10853
|
+
"vehicle-classifier",
|
|
10854
|
+
"package-detection"
|
|
10855
|
+
];
|
|
10805
10856
|
var MODEL_FORMATS = [
|
|
10806
10857
|
"onnx",
|
|
10807
10858
|
"coreml",
|
|
@@ -10934,6 +10985,12 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
|
|
|
10934
10985
|
/** True when the weights or their data forbid commercial use. */
|
|
10935
10986
|
nonCommercial: boolean().optional()
|
|
10936
10987
|
}));
|
|
10988
|
+
/** One `fillsClassifierEffects` entry — see `ModelCatalogEntry.fillsClassifierEffects`. */
|
|
10989
|
+
var FillsClassifierEffectSchema = object({
|
|
10990
|
+
stepId: _enum(FILLABLE_STEP_IDS),
|
|
10991
|
+
labelToEffect: record(string(), string()),
|
|
10992
|
+
uncovered: array(string()).readonly()
|
|
10993
|
+
});
|
|
10937
10994
|
var ModelCatalogEntrySchema = object({
|
|
10938
10995
|
id: string(),
|
|
10939
10996
|
name: string(),
|
|
@@ -11060,6 +11117,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
11060
11117
|
*/
|
|
11061
11118
|
classMap: DetectionCatalogClassMapSchema.optional(),
|
|
11062
11119
|
/**
|
|
11120
|
+
* The pipeline steps this ROOT detector already does the work of (D715):
|
|
11121
|
+
* its labels name the effect the step exists to produce (`deer` for
|
|
11122
|
+
* `animal-classifier`, `package` for `package-detection`). Derived from the
|
|
11123
|
+
* labelmap by `deriveClassifierFills`, never hand-written; a COCO model
|
|
11124
|
+
* declares none. Whether a declaration is honoured is the step's own
|
|
11125
|
+
* `labelLink` policy (`resolveLabelLink`). `labelToEffect` values are in
|
|
11126
|
+
* `FILLABLE_STEP_VOCABULARIES` (guard `check-label-link-vocabulary`).
|
|
11127
|
+
*/
|
|
11128
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional(),
|
|
11129
|
+
/**
|
|
11063
11130
|
* The model's CLIP feature-space contract (text encoder, dimension, vector
|
|
11064
11131
|
* index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
|
|
11065
11132
|
* CLIP image encoder; its absence is what "not a CLIP model" means to the
|
|
@@ -14532,7 +14599,7 @@ DeviceType.Camera;
|
|
|
14532
14599
|
* This matters here more than anywhere else in the repo, because every provider
|
|
14533
14600
|
* that knows a device's host knows its PASSWORD too: `{ host, port, username,
|
|
14534
14601
|
* password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
|
|
14535
|
-
* and `http://admin:hunter2@192.168.
|
|
14602
|
+
* and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
|
|
14536
14603
|
* camera's own page will ask for its own login. That is correct, and pre-
|
|
14537
14604
|
* filling it is the operator's business, not ours.
|
|
14538
14605
|
*
|
|
@@ -18402,51 +18469,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
18402
18469
|
kind: "mutation",
|
|
18403
18470
|
auth: "admin"
|
|
18404
18471
|
});
|
|
18405
|
-
new Set([
|
|
18406
|
-
{
|
|
18407
|
-
id: "person",
|
|
18408
|
-
name: "Person"
|
|
18409
|
-
},
|
|
18410
|
-
{
|
|
18411
|
-
id: "vehicle",
|
|
18412
|
-
name: "Vehicle"
|
|
18413
|
-
},
|
|
18414
|
-
{
|
|
18415
|
-
id: "animal",
|
|
18416
|
-
name: "Animal"
|
|
18417
|
-
},
|
|
18418
|
-
{
|
|
18419
|
-
id: "package",
|
|
18420
|
-
name: "Package"
|
|
18421
|
-
}
|
|
18422
|
-
].map((l) => l.id));
|
|
18423
|
-
var COCO_TO_MACRO = {
|
|
18424
|
-
mapping: {
|
|
18425
|
-
person: "person",
|
|
18426
|
-
bicycle: "vehicle",
|
|
18427
|
-
car: "vehicle",
|
|
18428
|
-
motorcycle: "vehicle",
|
|
18429
|
-
airplane: "vehicle",
|
|
18430
|
-
bus: "vehicle",
|
|
18431
|
-
train: "vehicle",
|
|
18432
|
-
truck: "vehicle",
|
|
18433
|
-
boat: "vehicle",
|
|
18434
|
-
bird: "animal",
|
|
18435
|
-
cat: "animal",
|
|
18436
|
-
dog: "animal",
|
|
18437
|
-
horse: "animal",
|
|
18438
|
-
sheep: "animal",
|
|
18439
|
-
cow: "animal",
|
|
18440
|
-
elephant: "animal",
|
|
18441
|
-
bear: "animal",
|
|
18442
|
-
zebra: "animal",
|
|
18443
|
-
giraffe: "animal",
|
|
18444
|
-
suitcase: "package",
|
|
18445
|
-
backpack: "package",
|
|
18446
|
-
handbag: "package"
|
|
18447
|
-
},
|
|
18448
|
-
preserveOriginal: false
|
|
18449
|
-
};
|
|
18450
18472
|
var AUDIO_MACRO_LABELS = [
|
|
18451
18473
|
{
|
|
18452
18474
|
id: "speech",
|
|
@@ -20340,8 +20362,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
|
|
|
20340
20362
|
disabledTargetIds: array(string()).default([])
|
|
20341
20363
|
});
|
|
20342
20364
|
/**
|
|
20343
|
-
* The state-summary SECTIONS (D647 / D698): the D637 kind groups that
|
|
20344
|
-
* still-true state. `
|
|
20365
|
+
* The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
|
|
20366
|
+
* report a still-true state. `addons` is the crash-looped addons only and
|
|
20367
|
+
* `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
|
|
20345
20368
|
* job is not an open state, and the alarm stays instant.
|
|
20346
20369
|
*/
|
|
20347
20370
|
var NcStateSummarySectionIdSchema = _enum([
|
|
@@ -20353,6 +20376,7 @@ var NcStateSummarySectionIdSchema = _enum([
|
|
|
20353
20376
|
"detection-blind",
|
|
20354
20377
|
"nodes",
|
|
20355
20378
|
"addons",
|
|
20379
|
+
"addon-updates",
|
|
20356
20380
|
"updates"
|
|
20357
20381
|
]);
|
|
20358
20382
|
/** One section of a previewed state summary. `unavailable` = its state could not be read. */
|
|
@@ -20833,7 +20857,19 @@ var NcAlarmModeDelaysListSchema = array(object({
|
|
|
20833
20857
|
* When this mode may be armed. Absent / null = always allowed (same as a
|
|
20834
20858
|
* rule with no `schedule`). Evaluated with {@link isScheduleActive}.
|
|
20835
20859
|
*/
|
|
20836
|
-
schedule: NcScheduleSchema.optional()
|
|
20860
|
+
schedule: NcScheduleSchema.optional(),
|
|
20861
|
+
/**
|
|
20862
|
+
* D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
|
|
20863
|
+
* intrusion naming the locations)? Absent = false — the entry-delay notification is the
|
|
20864
|
+
* only one, and the matches ride the triggered notification's mosaic (D705).
|
|
20865
|
+
*/
|
|
20866
|
+
notifyDuringEntryDelay: boolean().optional(),
|
|
20867
|
+
/**
|
|
20868
|
+
* D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
|
|
20869
|
+
* Absent = false (logged, not sent). Even when true it is not sent when that same sensor
|
|
20870
|
+
* is the one that starts the entry delay — the entry-delay notification already names it.
|
|
20871
|
+
*/
|
|
20872
|
+
notifySensorRewatched: boolean().optional()
|
|
20837
20873
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
|
|
20838
20874
|
var NcAlarmModeDelaysEditListSchema = array(object({
|
|
20839
20875
|
mode: AlarmArmModeSchema,
|
|
@@ -20842,7 +20878,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
|
|
|
20842
20878
|
triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
|
|
20843
20879
|
postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
|
|
20844
20880
|
/** Set a schedule, or `null` to clear (mode always allowed again). */
|
|
20845
|
-
schedule: NcScheduleSchema.nullable().optional()
|
|
20881
|
+
schedule: NcScheduleSchema.nullable().optional(),
|
|
20882
|
+
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20883
|
+
notifyDuringEntryDelay: boolean().nullable().optional(),
|
|
20884
|
+
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20885
|
+
notifySensorRewatched: boolean().nullable().optional()
|
|
20846
20886
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
|
|
20847
20887
|
/** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
|
|
20848
20888
|
var NcAlarmNonBlockingEditSchema = object({
|
|
@@ -23870,7 +23910,8 @@ var PipelineModelOptionSchema = object({
|
|
|
23870
23910
|
group: ModelVariantGroupSchema.optional(),
|
|
23871
23911
|
legacy: boolean().optional(),
|
|
23872
23912
|
provider: ModelProviderIdSchema.optional(),
|
|
23873
|
-
defaultConfidence: number().optional()
|
|
23913
|
+
defaultConfidence: number().optional(),
|
|
23914
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional()
|
|
23874
23915
|
});
|
|
23875
23916
|
var ConfigFieldBridge = custom();
|
|
23876
23917
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -24703,6 +24744,22 @@ var MotionSourceEnum = _enum([
|
|
|
24703
24744
|
"device-activity"
|
|
24704
24745
|
]);
|
|
24705
24746
|
/**
|
|
24747
|
+
* What opened an on-motion detection SESSION: the motion source that passed the
|
|
24748
|
+
* camera's `motionSources` gate, and what raised it (the camera itself, or a
|
|
24749
|
+
* sensor the operator attached — D672). `sensorName` is the attached sensor's
|
|
24750
|
+
* device name as the orchestrator's camera-sensors resolution last read it;
|
|
24751
|
+
* absent when unknown or when the camera itself triggered.
|
|
24752
|
+
*
|
|
24753
|
+
* Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
|
|
24754
|
+
* can say what started the session (D672 rule 11, amended 2026-10-06). The
|
|
24755
|
+
* runner never decides anything on it.
|
|
24756
|
+
*/
|
|
24757
|
+
var SessionTriggerSchema = object({
|
|
24758
|
+
source: MotionSourceEnum,
|
|
24759
|
+
triggeredBy: MotionTriggeredBySchema,
|
|
24760
|
+
sensorName: string().optional()
|
|
24761
|
+
});
|
|
24762
|
+
/**
|
|
24706
24763
|
* List of motion sources active on a camera. Empty array is valid:
|
|
24707
24764
|
* "no source" — happens for battery cams without firmware motion when
|
|
24708
24765
|
* the operator hasn't opted into the SW analyzer (would hold the
|
|
@@ -24954,6 +25011,14 @@ var RunnerCameraConfigSchema = object({
|
|
|
24954
25011
|
*/
|
|
24955
25012
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
24956
25013
|
/**
|
|
25014
|
+
* What opened this attach, on a SESSION attach only (see
|
|
25015
|
+
* {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
|
|
25016
|
+
* standing attach, an occupancy burst, a re-dispatch with no motion event
|
|
25017
|
+
* behind it, and every attach from an orchestrator older than the field — the
|
|
25018
|
+
* runner then names no source on the phase event.
|
|
25019
|
+
*/
|
|
25020
|
+
sessionTrigger: SessionTriggerSchema.optional(),
|
|
25021
|
+
/**
|
|
24957
25022
|
* WHICH root detector runs for this camera. The detection counterpart of
|
|
24958
25023
|
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
24959
25024
|
* of sources the system understands, vendor-agnostic, owned here.
|
|
@@ -32864,7 +32929,7 @@ var LocalInterfaceSchema = object({
|
|
|
32864
32929
|
name: string(),
|
|
32865
32930
|
family: _enum(["IPv4", "IPv6"]),
|
|
32866
32931
|
address: string(),
|
|
32867
|
-
/** CIDR shorthand (e.g. `192.168.
|
|
32932
|
+
/** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
|
|
32868
32933
|
cidr: string(),
|
|
32869
32934
|
/** OS netmask. Stable across calls so the SDK can hash on it. */
|
|
32870
32935
|
netmask: string(),
|
|
@@ -32918,7 +32983,8 @@ var ConnectionEndpointSchema = object({
|
|
|
32918
32983
|
"lan-ipv4",
|
|
32919
32984
|
"lan-ipv6",
|
|
32920
32985
|
"public",
|
|
32921
|
-
"loopback"
|
|
32986
|
+
"loopback",
|
|
32987
|
+
"app-link"
|
|
32922
32988
|
]),
|
|
32923
32989
|
/**
|
|
32924
32990
|
* Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
|
|
@@ -32987,15 +33053,27 @@ var GetConnectionEndpointsResultSchema = object({
|
|
|
32987
33053
|
localPort: AdvertisedLocalPortSchema
|
|
32988
33054
|
});
|
|
32989
33055
|
/**
|
|
32990
|
-
* The
|
|
32991
|
-
*
|
|
32992
|
-
*
|
|
33056
|
+
* The address flagged for notification links. `baseUrl: null` = AUTO, which
|
|
33057
|
+
* since D709 is the CamStack app link; `resolved` reports the effective value,
|
|
33058
|
+
* so the UI can show it either way.
|
|
32993
33059
|
*/
|
|
32994
33060
|
var NotificationEndpointSchema = object({
|
|
32995
33061
|
/** The operator's explicit choice, or null for AUTO. */
|
|
32996
33062
|
baseUrl: string().nullable(),
|
|
32997
|
-
/**
|
|
32998
|
-
|
|
33063
|
+
/**
|
|
33064
|
+
* The effective flagged address: the explicit choice, else (AUTO) the
|
|
33065
|
+
* CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
|
|
33066
|
+
* answered the public tunnel / mesh here, or null.
|
|
33067
|
+
*/
|
|
33068
|
+
resolved: string().nullable(),
|
|
33069
|
+
/**
|
|
33070
|
+
* The hub origin an app link carries as `hub=` (D709): the hub's best
|
|
33071
|
+
* public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
|
|
33072
|
+
* picked from this cap's own palette by `pickAppLinkHub`. `null` when the
|
|
33073
|
+
* hub advertises neither; the app link is then minted without `hub=`.
|
|
33074
|
+
* Absent on a pre-D709 hub.
|
|
33075
|
+
*/
|
|
33076
|
+
appLinkHub: string().nullable()
|
|
32999
33077
|
});
|
|
33000
33078
|
/**
|
|
33001
33079
|
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
@@ -33049,7 +33127,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
33049
33127
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
33050
33128
|
* to avoid mixed-content blocks in the browser. The public
|
|
33051
33129
|
* tunnel always emits `https://` regardless. */
|
|
33052
|
-
scheme: _enum(["http", "https"]).optional()
|
|
33130
|
+
scheme: _enum(["http", "https"]).optional(),
|
|
33131
|
+
/** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
|
|
33132
|
+
* an address notification links can be flagged with, not a hub
|
|
33133
|
+
* route. Default `false`: racers and rankers never see it. */
|
|
33134
|
+
includeAppLink: boolean().optional()
|
|
33053
33135
|
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
|
|
33054
33136
|
kind: "mutation",
|
|
33055
33137
|
auth: "admin"
|
|
@@ -33378,7 +33460,7 @@ var MeshPeerSchema = object({
|
|
|
33378
33460
|
tags: array(string()).readonly(),
|
|
33379
33461
|
/** True when the peer advertises itself as an exit-node candidate. */
|
|
33380
33462
|
exitNodeOption: boolean(),
|
|
33381
|
-
/** Subnet routes the peer advertises (e.g. `["192.168.
|
|
33463
|
+
/** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
|
|
33382
33464
|
advertisedRoutes: array(string()).readonly(),
|
|
33383
33465
|
/** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
|
|
33384
33466
|
userLogin: string().nullable(),
|
|
@@ -53855,12 +53937,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
53855
53937
|
{
|
|
53856
53938
|
id: "addons",
|
|
53857
53939
|
family: "system",
|
|
53858
|
-
label: "Addons",
|
|
53859
|
-
kinds: [
|
|
53860
|
-
|
|
53861
|
-
|
|
53862
|
-
|
|
53863
|
-
|
|
53940
|
+
label: "Addons stopped",
|
|
53941
|
+
kinds: ["addon-crash-loop"]
|
|
53942
|
+
},
|
|
53943
|
+
{
|
|
53944
|
+
id: "addon-updates",
|
|
53945
|
+
family: "system",
|
|
53946
|
+
label: "Addon updates",
|
|
53947
|
+
kinds: ["addon-update-available", "addon-updated"]
|
|
53864
53948
|
},
|
|
53865
53949
|
{
|
|
53866
53950
|
id: "updates",
|
|
@@ -54020,13 +54104,15 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
54020
54104
|
},
|
|
54021
54105
|
{
|
|
54022
54106
|
name: "summaryAddons",
|
|
54023
|
-
label: "Summary: addons",
|
|
54024
|
-
example: "
|
|
54025
|
-
kinds: [
|
|
54026
|
-
|
|
54027
|
-
|
|
54028
|
-
|
|
54029
|
-
|
|
54107
|
+
label: "Summary: addons stopped",
|
|
54108
|
+
example: "hub — recorder stopped after repeated crashes",
|
|
54109
|
+
kinds: ["addon-crash-loop"]
|
|
54110
|
+
},
|
|
54111
|
+
{
|
|
54112
|
+
name: "summaryAddonUpdates",
|
|
54113
|
+
label: "Summary: addon updates",
|
|
54114
|
+
example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
|
|
54115
|
+
kinds: ["addon-update-available", "addon-updated"]
|
|
54030
54116
|
},
|
|
54031
54117
|
{
|
|
54032
54118
|
name: "summaryUpdates",
|
|
@@ -54110,6 +54196,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
54110
54196
|
[...RULE_BODY_ONLY],
|
|
54111
54197
|
[...RULE_TEXT],
|
|
54112
54198
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
54199
|
+
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
54113
54200
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
54114
54201
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
54115
54202
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
@@ -59847,6 +59934,75 @@ function createAccessoryDevice(kind, ctx, parent) {
|
|
|
59847
59934
|
default: throw new Error(`Hikvision provider does not implement accessory kind "${String(kind)}"`);
|
|
59848
59935
|
}
|
|
59849
59936
|
}
|
|
59937
|
+
var ALARM_INGRESS_MESSAGE = "hikvision: alarm event received";
|
|
59938
|
+
var countsRecord = (counts) => Object.fromEntries(counts);
|
|
59939
|
+
function createAlarmIngressLog(deps) {
|
|
59940
|
+
const intervalMs = deps.intervalMs ?? 6e4;
|
|
59941
|
+
const bursts = /* @__PURE__ */ new Map();
|
|
59942
|
+
const tags = { deviceId: deps.deviceId };
|
|
59943
|
+
const received = (ev) => {
|
|
59944
|
+
const existing = bursts.get(ev.camStreamId);
|
|
59945
|
+
const burst = existing ?? {
|
|
59946
|
+
startedAt: ev.at,
|
|
59947
|
+
lastLineAt: ev.at,
|
|
59948
|
+
counts: /* @__PURE__ */ new Map(),
|
|
59949
|
+
types: /* @__PURE__ */ new Set(),
|
|
59950
|
+
ticks: 0
|
|
59951
|
+
};
|
|
59952
|
+
if (!existing) bursts.set(ev.camStreamId, burst);
|
|
59953
|
+
burst.ticks += 1;
|
|
59954
|
+
if (!burst.types.has(ev.type)) {
|
|
59955
|
+
burst.types.add(ev.type);
|
|
59956
|
+
deps.logger.info(ALARM_INGRESS_MESSAGE, {
|
|
59957
|
+
tags,
|
|
59958
|
+
meta: {
|
|
59959
|
+
edge: "start",
|
|
59960
|
+
type: ev.type,
|
|
59961
|
+
state: ev.state,
|
|
59962
|
+
camStreamId: ev.camStreamId,
|
|
59963
|
+
...ev.aiClass === null ? {} : { aiClass: ev.aiClass },
|
|
59964
|
+
...existing ? { burstAgeMs: ev.at - burst.startedAt } : {}
|
|
59965
|
+
}
|
|
59966
|
+
});
|
|
59967
|
+
burst.lastLineAt = Math.max(burst.lastLineAt, ev.at);
|
|
59968
|
+
return;
|
|
59969
|
+
}
|
|
59970
|
+
burst.counts.set(ev.type, (burst.counts.get(ev.type) ?? 0) + 1);
|
|
59971
|
+
if (ev.at - burst.lastLineAt < intervalMs) return;
|
|
59972
|
+
deps.logger.info(ALARM_INGRESS_MESSAGE, {
|
|
59973
|
+
tags,
|
|
59974
|
+
meta: {
|
|
59975
|
+
edge: "ongoing",
|
|
59976
|
+
camStreamId: ev.camStreamId,
|
|
59977
|
+
counts: countsRecord(burst.counts),
|
|
59978
|
+
windowMs: ev.at - burst.lastLineAt,
|
|
59979
|
+
burstAgeMs: ev.at - burst.startedAt
|
|
59980
|
+
}
|
|
59981
|
+
});
|
|
59982
|
+
burst.counts.clear();
|
|
59983
|
+
burst.lastLineAt = ev.at;
|
|
59984
|
+
};
|
|
59985
|
+
const ended = (camStreamId, at) => {
|
|
59986
|
+
const burst = bursts.get(camStreamId);
|
|
59987
|
+
if (!burst) return;
|
|
59988
|
+
bursts.delete(camStreamId);
|
|
59989
|
+
deps.logger.info(ALARM_INGRESS_MESSAGE, {
|
|
59990
|
+
tags,
|
|
59991
|
+
meta: {
|
|
59992
|
+
edge: "end",
|
|
59993
|
+
camStreamId,
|
|
59994
|
+
counts: countsRecord(burst.counts),
|
|
59995
|
+
ticks: burst.ticks,
|
|
59996
|
+
types: [...burst.types],
|
|
59997
|
+
durationMs: at - burst.startedAt
|
|
59998
|
+
}
|
|
59999
|
+
});
|
|
60000
|
+
};
|
|
60001
|
+
return {
|
|
60002
|
+
received,
|
|
60003
|
+
ended
|
|
60004
|
+
};
|
|
60005
|
+
}
|
|
59850
60006
|
function createAlarmTypeTally(deps) {
|
|
59851
60007
|
const intervalMs = deps.intervalMs ?? 3e5;
|
|
59852
60008
|
const lastLoggedAt = /* @__PURE__ */ new Map();
|
|
@@ -64233,6 +64389,20 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
64233
64389
|
});
|
|
64234
64390
|
return this.unhandledAlarmsTally;
|
|
64235
64391
|
}
|
|
64392
|
+
/**
|
|
64393
|
+
* Sampled info-level ingress line for the motion-bearing alarms (VMD and
|
|
64394
|
+
* smart events): one at each burst's start, one per minute while it lasts,
|
|
64395
|
+
* one at its end — never one per ~1 Hz tick. Lazy for the same reason as
|
|
64396
|
+
* `unhandledAlarms`.
|
|
64397
|
+
*/
|
|
64398
|
+
motionIngressLog = null;
|
|
64399
|
+
get motionIngress() {
|
|
64400
|
+
this.motionIngressLog ??= createAlarmIngressLog({
|
|
64401
|
+
logger: this.ctx.logger,
|
|
64402
|
+
deviceId: this.id
|
|
64403
|
+
});
|
|
64404
|
+
return this.motionIngressLog;
|
|
64405
|
+
}
|
|
64236
64406
|
/** Hysteresis: drop motion-active back to inactive after this many ms with
|
|
64237
64407
|
* no fresh `VMD` event. Hikvision fires VMD every ~1s while motion is
|
|
64238
64408
|
* active; missing 2 ticks signals the burst is over. */
|
|
@@ -66808,6 +66978,13 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
66808
66978
|
handleMotionEvent(ev, camStreamId) {
|
|
66809
66979
|
const now = ev.observedAt;
|
|
66810
66980
|
this.lastMotionAt.set(camStreamId, now);
|
|
66981
|
+
this.motionIngress.received({
|
|
66982
|
+
type: ev.type,
|
|
66983
|
+
state: ev.state,
|
|
66984
|
+
aiClass: ev.aiClass,
|
|
66985
|
+
camStreamId,
|
|
66986
|
+
at: now
|
|
66987
|
+
});
|
|
66811
66988
|
this.ctx.eventBus.emit(providerMotionEvent(this.eventSource, {
|
|
66812
66989
|
deviceId: this.id,
|
|
66813
66990
|
detected: true,
|
|
@@ -66820,6 +66997,7 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
66820
66997
|
const clearedAt = Date.now();
|
|
66821
66998
|
this.lastMotionAt.set(camStreamId, 0);
|
|
66822
66999
|
this.motionInactiveTimers.delete(camStreamId);
|
|
67000
|
+
this.motionIngress.ended(camStreamId, clearedAt);
|
|
66823
67001
|
this.ctx.eventBus.emit(providerMotionEvent(this.eventSource, {
|
|
66824
67002
|
deviceId: this.id,
|
|
66825
67003
|
detected: false,
|
|
@@ -66857,7 +67035,7 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
66857
67035
|
key: "host",
|
|
66858
67036
|
label: "Camera IP",
|
|
66859
67037
|
required: true,
|
|
66860
|
-
placeholder: "192.168.
|
|
67038
|
+
placeholder: "192.168.10.100"
|
|
66861
67039
|
},
|
|
66862
67040
|
{
|
|
66863
67041
|
type: "number",
|
|
@@ -76906,13 +77084,13 @@ function enumerateHosts(cidr) {
|
|
|
76906
77084
|
for (let d = 1; d <= 254; d += 1) out.push(`${a}.${b}.${c}.${d}`);
|
|
76907
77085
|
return out;
|
|
76908
77086
|
}
|
|
76909
|
-
/** Best-effort local /24 from the first non-internal IPv4 interface (e.g. `192.168.
|
|
77087
|
+
/** Best-effort local /24 from the first non-internal IPv4 interface (e.g. `192.168.10.0/24`). */
|
|
76910
77088
|
function detectLocalCidr() {
|
|
76911
77089
|
for (const addrs of Object.values((0, node_os.networkInterfaces)())) for (const addr of addrs ?? []) if (addr.family === "IPv4" && !addr.internal) {
|
|
76912
77090
|
const [a, b, c] = addr.address.split(".");
|
|
76913
77091
|
return `${a}.${b}.${c}.0/24`;
|
|
76914
77092
|
}
|
|
76915
|
-
return "192.168.
|
|
77093
|
+
return "192.168.10.0/24";
|
|
76916
77094
|
}
|
|
76917
77095
|
/** Flatten a host into a flat row-key slug — must match `generateStableId`'s host fallback so a
|
|
76918
77096
|
* re-scan of a host-added camera is detected as already onboarded. */
|
|
@@ -76982,7 +77160,7 @@ function buildCreationFormSchema() {
|
|
|
76982
77160
|
key: "host",
|
|
76983
77161
|
label: "Camera IP",
|
|
76984
77162
|
required: true,
|
|
76985
|
-
placeholder: "192.168.
|
|
77163
|
+
placeholder: "192.168.10.100",
|
|
76986
77164
|
inputType: "text"
|
|
76987
77165
|
},
|
|
76988
77166
|
{
|
|
@@ -77032,14 +77210,14 @@ function buildDiscoveryParamsFormSchema() {
|
|
|
77032
77210
|
}, {
|
|
77033
77211
|
id: "scan",
|
|
77034
77212
|
title: "Scan options",
|
|
77035
|
-
description: "The credentialed ISAPI subnet scan is the reliable path (multicast WS-Discovery is not received on a macvlan/br0 hub). IMPORTANT: leaving the network blank scans only the HUB’s own subnet — if your cameras are on a DIFFERENT subnet than the hub, you MUST enter their CIDR here (e.g. 192.168.
|
|
77213
|
+
description: "The credentialed ISAPI subnet scan is the reliable path (multicast WS-Discovery is not received on a macvlan/br0 hub). IMPORTANT: leaving the network blank scans only the HUB’s own subnet — if your cameras are on a DIFFERENT subnet than the hub, you MUST enter their CIDR here (e.g. 192.168.60.0/24), otherwise the scan returns nothing.",
|
|
77036
77214
|
columns: 1,
|
|
77037
77215
|
fields: [{
|
|
77038
77216
|
type: "text",
|
|
77039
77217
|
key: "networkCidr",
|
|
77040
77218
|
label: "Network (CIDR) — required if cameras are on a different subnet than the hub",
|
|
77041
77219
|
required: false,
|
|
77042
|
-
placeholder: "192.168.
|
|
77220
|
+
placeholder: "192.168.60.0/24"
|
|
77043
77221
|
}, {
|
|
77044
77222
|
type: "number",
|
|
77045
77223
|
key: "timeoutMs",
|
package/dist/addon.mjs
CHANGED
|
@@ -10781,6 +10781,57 @@ var ClipSearchStrictnessSchema = _enum([
|
|
|
10781
10781
|
"balanced",
|
|
10782
10782
|
"strict"
|
|
10783
10783
|
]);
|
|
10784
|
+
new Set([
|
|
10785
|
+
{
|
|
10786
|
+
id: "person",
|
|
10787
|
+
name: "Person"
|
|
10788
|
+
},
|
|
10789
|
+
{
|
|
10790
|
+
id: "vehicle",
|
|
10791
|
+
name: "Vehicle"
|
|
10792
|
+
},
|
|
10793
|
+
{
|
|
10794
|
+
id: "animal",
|
|
10795
|
+
name: "Animal"
|
|
10796
|
+
},
|
|
10797
|
+
{
|
|
10798
|
+
id: "package",
|
|
10799
|
+
name: "Package"
|
|
10800
|
+
}
|
|
10801
|
+
].map((l) => l.id));
|
|
10802
|
+
var COCO_TO_MACRO = {
|
|
10803
|
+
mapping: {
|
|
10804
|
+
person: "person",
|
|
10805
|
+
bicycle: "vehicle",
|
|
10806
|
+
car: "vehicle",
|
|
10807
|
+
motorcycle: "vehicle",
|
|
10808
|
+
airplane: "vehicle",
|
|
10809
|
+
bus: "vehicle",
|
|
10810
|
+
train: "vehicle",
|
|
10811
|
+
truck: "vehicle",
|
|
10812
|
+
boat: "vehicle",
|
|
10813
|
+
bird: "animal",
|
|
10814
|
+
cat: "animal",
|
|
10815
|
+
dog: "animal",
|
|
10816
|
+
horse: "animal",
|
|
10817
|
+
sheep: "animal",
|
|
10818
|
+
cow: "animal",
|
|
10819
|
+
elephant: "animal",
|
|
10820
|
+
bear: "animal",
|
|
10821
|
+
zebra: "animal",
|
|
10822
|
+
giraffe: "animal",
|
|
10823
|
+
suitcase: "package",
|
|
10824
|
+
backpack: "package",
|
|
10825
|
+
handbag: "package"
|
|
10826
|
+
},
|
|
10827
|
+
preserveOriginal: false
|
|
10828
|
+
};
|
|
10829
|
+
/** Steps a root detector may declare it fills. */
|
|
10830
|
+
var FILLABLE_STEP_IDS = [
|
|
10831
|
+
"animal-classifier",
|
|
10832
|
+
"vehicle-classifier",
|
|
10833
|
+
"package-detection"
|
|
10834
|
+
];
|
|
10784
10835
|
var MODEL_FORMATS = [
|
|
10785
10836
|
"onnx",
|
|
10786
10837
|
"coreml",
|
|
@@ -10913,6 +10964,12 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
|
|
|
10913
10964
|
/** True when the weights or their data forbid commercial use. */
|
|
10914
10965
|
nonCommercial: boolean().optional()
|
|
10915
10966
|
}));
|
|
10967
|
+
/** One `fillsClassifierEffects` entry — see `ModelCatalogEntry.fillsClassifierEffects`. */
|
|
10968
|
+
var FillsClassifierEffectSchema = object({
|
|
10969
|
+
stepId: _enum(FILLABLE_STEP_IDS),
|
|
10970
|
+
labelToEffect: record(string(), string()),
|
|
10971
|
+
uncovered: array(string()).readonly()
|
|
10972
|
+
});
|
|
10916
10973
|
var ModelCatalogEntrySchema = object({
|
|
10917
10974
|
id: string(),
|
|
10918
10975
|
name: string(),
|
|
@@ -11039,6 +11096,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
11039
11096
|
*/
|
|
11040
11097
|
classMap: DetectionCatalogClassMapSchema.optional(),
|
|
11041
11098
|
/**
|
|
11099
|
+
* The pipeline steps this ROOT detector already does the work of (D715):
|
|
11100
|
+
* its labels name the effect the step exists to produce (`deer` for
|
|
11101
|
+
* `animal-classifier`, `package` for `package-detection`). Derived from the
|
|
11102
|
+
* labelmap by `deriveClassifierFills`, never hand-written; a COCO model
|
|
11103
|
+
* declares none. Whether a declaration is honoured is the step's own
|
|
11104
|
+
* `labelLink` policy (`resolveLabelLink`). `labelToEffect` values are in
|
|
11105
|
+
* `FILLABLE_STEP_VOCABULARIES` (guard `check-label-link-vocabulary`).
|
|
11106
|
+
*/
|
|
11107
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional(),
|
|
11108
|
+
/**
|
|
11042
11109
|
* The model's CLIP feature-space contract (text encoder, dimension, vector
|
|
11043
11110
|
* index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
|
|
11044
11111
|
* CLIP image encoder; its absence is what "not a CLIP model" means to the
|
|
@@ -14511,7 +14578,7 @@ DeviceType.Camera;
|
|
|
14511
14578
|
* This matters here more than anywhere else in the repo, because every provider
|
|
14512
14579
|
* that knows a device's host knows its PASSWORD too: `{ host, port, username,
|
|
14513
14580
|
* password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
|
|
14514
|
-
* and `http://admin:hunter2@192.168.
|
|
14581
|
+
* and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
|
|
14515
14582
|
* camera's own page will ask for its own login. That is correct, and pre-
|
|
14516
14583
|
* filling it is the operator's business, not ours.
|
|
14517
14584
|
*
|
|
@@ -18381,51 +18448,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
|
|
|
18381
18448
|
kind: "mutation",
|
|
18382
18449
|
auth: "admin"
|
|
18383
18450
|
});
|
|
18384
|
-
new Set([
|
|
18385
|
-
{
|
|
18386
|
-
id: "person",
|
|
18387
|
-
name: "Person"
|
|
18388
|
-
},
|
|
18389
|
-
{
|
|
18390
|
-
id: "vehicle",
|
|
18391
|
-
name: "Vehicle"
|
|
18392
|
-
},
|
|
18393
|
-
{
|
|
18394
|
-
id: "animal",
|
|
18395
|
-
name: "Animal"
|
|
18396
|
-
},
|
|
18397
|
-
{
|
|
18398
|
-
id: "package",
|
|
18399
|
-
name: "Package"
|
|
18400
|
-
}
|
|
18401
|
-
].map((l) => l.id));
|
|
18402
|
-
var COCO_TO_MACRO = {
|
|
18403
|
-
mapping: {
|
|
18404
|
-
person: "person",
|
|
18405
|
-
bicycle: "vehicle",
|
|
18406
|
-
car: "vehicle",
|
|
18407
|
-
motorcycle: "vehicle",
|
|
18408
|
-
airplane: "vehicle",
|
|
18409
|
-
bus: "vehicle",
|
|
18410
|
-
train: "vehicle",
|
|
18411
|
-
truck: "vehicle",
|
|
18412
|
-
boat: "vehicle",
|
|
18413
|
-
bird: "animal",
|
|
18414
|
-
cat: "animal",
|
|
18415
|
-
dog: "animal",
|
|
18416
|
-
horse: "animal",
|
|
18417
|
-
sheep: "animal",
|
|
18418
|
-
cow: "animal",
|
|
18419
|
-
elephant: "animal",
|
|
18420
|
-
bear: "animal",
|
|
18421
|
-
zebra: "animal",
|
|
18422
|
-
giraffe: "animal",
|
|
18423
|
-
suitcase: "package",
|
|
18424
|
-
backpack: "package",
|
|
18425
|
-
handbag: "package"
|
|
18426
|
-
},
|
|
18427
|
-
preserveOriginal: false
|
|
18428
|
-
};
|
|
18429
18451
|
var AUDIO_MACRO_LABELS = [
|
|
18430
18452
|
{
|
|
18431
18453
|
id: "speech",
|
|
@@ -20319,8 +20341,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
|
|
|
20319
20341
|
disabledTargetIds: array(string()).default([])
|
|
20320
20342
|
});
|
|
20321
20343
|
/**
|
|
20322
|
-
* The state-summary SECTIONS (D647 / D698): the D637 kind groups that
|
|
20323
|
-
* still-true state. `
|
|
20344
|
+
* The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
|
|
20345
|
+
* report a still-true state. `addons` is the crash-looped addons only and
|
|
20346
|
+
* `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
|
|
20324
20347
|
* job is not an open state, and the alarm stays instant.
|
|
20325
20348
|
*/
|
|
20326
20349
|
var NcStateSummarySectionIdSchema = _enum([
|
|
@@ -20332,6 +20355,7 @@ var NcStateSummarySectionIdSchema = _enum([
|
|
|
20332
20355
|
"detection-blind",
|
|
20333
20356
|
"nodes",
|
|
20334
20357
|
"addons",
|
|
20358
|
+
"addon-updates",
|
|
20335
20359
|
"updates"
|
|
20336
20360
|
]);
|
|
20337
20361
|
/** One section of a previewed state summary. `unavailable` = its state could not be read. */
|
|
@@ -20812,7 +20836,19 @@ var NcAlarmModeDelaysListSchema = array(object({
|
|
|
20812
20836
|
* When this mode may be armed. Absent / null = always allowed (same as a
|
|
20813
20837
|
* rule with no `schedule`). Evaluated with {@link isScheduleActive}.
|
|
20814
20838
|
*/
|
|
20815
|
-
schedule: NcScheduleSchema.optional()
|
|
20839
|
+
schedule: NcScheduleSchema.optional(),
|
|
20840
|
+
/**
|
|
20841
|
+
* D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
|
|
20842
|
+
* intrusion naming the locations)? Absent = false — the entry-delay notification is the
|
|
20843
|
+
* only one, and the matches ride the triggered notification's mosaic (D705).
|
|
20844
|
+
*/
|
|
20845
|
+
notifyDuringEntryDelay: boolean().optional(),
|
|
20846
|
+
/**
|
|
20847
|
+
* D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
|
|
20848
|
+
* Absent = false (logged, not sent). Even when true it is not sent when that same sensor
|
|
20849
|
+
* is the one that starts the entry delay — the entry-delay notification already names it.
|
|
20850
|
+
*/
|
|
20851
|
+
notifySensorRewatched: boolean().optional()
|
|
20816
20852
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
|
|
20817
20853
|
var NcAlarmModeDelaysEditListSchema = array(object({
|
|
20818
20854
|
mode: AlarmArmModeSchema,
|
|
@@ -20821,7 +20857,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
|
|
|
20821
20857
|
triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
|
|
20822
20858
|
postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
|
|
20823
20859
|
/** Set a schedule, or `null` to clear (mode always allowed again). */
|
|
20824
|
-
schedule: NcScheduleSchema.nullable().optional()
|
|
20860
|
+
schedule: NcScheduleSchema.nullable().optional(),
|
|
20861
|
+
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20862
|
+
notifyDuringEntryDelay: boolean().nullable().optional(),
|
|
20863
|
+
/** D706: set the switch, or `null` to clear it (back to the default, false). */
|
|
20864
|
+
notifySensorRewatched: boolean().nullable().optional()
|
|
20825
20865
|
})).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
|
|
20826
20866
|
/** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
|
|
20827
20867
|
var NcAlarmNonBlockingEditSchema = object({
|
|
@@ -23849,7 +23889,8 @@ var PipelineModelOptionSchema = object({
|
|
|
23849
23889
|
group: ModelVariantGroupSchema.optional(),
|
|
23850
23890
|
legacy: boolean().optional(),
|
|
23851
23891
|
provider: ModelProviderIdSchema.optional(),
|
|
23852
|
-
defaultConfidence: number().optional()
|
|
23892
|
+
defaultConfidence: number().optional(),
|
|
23893
|
+
fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional()
|
|
23853
23894
|
});
|
|
23854
23895
|
var ConfigFieldBridge = custom();
|
|
23855
23896
|
var PipelineAddonSchemaSchema = object({
|
|
@@ -24682,6 +24723,22 @@ var MotionSourceEnum = _enum([
|
|
|
24682
24723
|
"device-activity"
|
|
24683
24724
|
]);
|
|
24684
24725
|
/**
|
|
24726
|
+
* What opened an on-motion detection SESSION: the motion source that passed the
|
|
24727
|
+
* camera's `motionSources` gate, and what raised it (the camera itself, or a
|
|
24728
|
+
* sensor the operator attached — D672). `sensorName` is the attached sensor's
|
|
24729
|
+
* device name as the orchestrator's camera-sensors resolution last read it;
|
|
24730
|
+
* absent when unknown or when the camera itself triggered.
|
|
24731
|
+
*
|
|
24732
|
+
* Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
|
|
24733
|
+
* can say what started the session (D672 rule 11, amended 2026-10-06). The
|
|
24734
|
+
* runner never decides anything on it.
|
|
24735
|
+
*/
|
|
24736
|
+
var SessionTriggerSchema = object({
|
|
24737
|
+
source: MotionSourceEnum,
|
|
24738
|
+
triggeredBy: MotionTriggeredBySchema,
|
|
24739
|
+
sensorName: string().optional()
|
|
24740
|
+
});
|
|
24741
|
+
/**
|
|
24685
24742
|
* List of motion sources active on a camera. Empty array is valid:
|
|
24686
24743
|
* "no source" — happens for battery cams without firmware motion when
|
|
24687
24744
|
* the operator hasn't opted into the SW analyzer (would hold the
|
|
@@ -24933,6 +24990,14 @@ var RunnerCameraConfigSchema = object({
|
|
|
24933
24990
|
*/
|
|
24934
24991
|
motionSources: MotionSourcesSchema.default(["analyzer"]),
|
|
24935
24992
|
/**
|
|
24993
|
+
* What opened this attach, on a SESSION attach only (see
|
|
24994
|
+
* {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
|
|
24995
|
+
* standing attach, an occupancy burst, a re-dispatch with no motion event
|
|
24996
|
+
* behind it, and every attach from an orchestrator older than the field — the
|
|
24997
|
+
* runner then names no source on the phase event.
|
|
24998
|
+
*/
|
|
24999
|
+
sessionTrigger: SessionTriggerSchema.optional(),
|
|
25000
|
+
/**
|
|
24936
25001
|
* WHICH root detector runs for this camera. The detection counterpart of
|
|
24937
25002
|
* {@link motionSources}, same shape and same discipline: a per-camera list
|
|
24938
25003
|
* of sources the system understands, vendor-agnostic, owned here.
|
|
@@ -32843,7 +32908,7 @@ var LocalInterfaceSchema = object({
|
|
|
32843
32908
|
name: string(),
|
|
32844
32909
|
family: _enum(["IPv4", "IPv6"]),
|
|
32845
32910
|
address: string(),
|
|
32846
|
-
/** CIDR shorthand (e.g. `192.168.
|
|
32911
|
+
/** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
|
|
32847
32912
|
cidr: string(),
|
|
32848
32913
|
/** OS netmask. Stable across calls so the SDK can hash on it. */
|
|
32849
32914
|
netmask: string(),
|
|
@@ -32897,7 +32962,8 @@ var ConnectionEndpointSchema = object({
|
|
|
32897
32962
|
"lan-ipv4",
|
|
32898
32963
|
"lan-ipv6",
|
|
32899
32964
|
"public",
|
|
32900
|
-
"loopback"
|
|
32965
|
+
"loopback",
|
|
32966
|
+
"app-link"
|
|
32901
32967
|
]),
|
|
32902
32968
|
/**
|
|
32903
32969
|
* Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
|
|
@@ -32966,15 +33032,27 @@ var GetConnectionEndpointsResultSchema = object({
|
|
|
32966
33032
|
localPort: AdvertisedLocalPortSchema
|
|
32967
33033
|
});
|
|
32968
33034
|
/**
|
|
32969
|
-
* The
|
|
32970
|
-
*
|
|
32971
|
-
*
|
|
33035
|
+
* The address flagged for notification links. `baseUrl: null` = AUTO, which
|
|
33036
|
+
* since D709 is the CamStack app link; `resolved` reports the effective value,
|
|
33037
|
+
* so the UI can show it either way.
|
|
32972
33038
|
*/
|
|
32973
33039
|
var NotificationEndpointSchema = object({
|
|
32974
33040
|
/** The operator's explicit choice, or null for AUTO. */
|
|
32975
33041
|
baseUrl: string().nullable(),
|
|
32976
|
-
/**
|
|
32977
|
-
|
|
33042
|
+
/**
|
|
33043
|
+
* The effective flagged address: the explicit choice, else (AUTO) the
|
|
33044
|
+
* CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
|
|
33045
|
+
* answered the public tunnel / mesh here, or null.
|
|
33046
|
+
*/
|
|
33047
|
+
resolved: string().nullable(),
|
|
33048
|
+
/**
|
|
33049
|
+
* The hub origin an app link carries as `hub=` (D709): the hub's best
|
|
33050
|
+
* public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
|
|
33051
|
+
* picked from this cap's own palette by `pickAppLinkHub`. `null` when the
|
|
33052
|
+
* hub advertises neither; the app link is then minted without `hub=`.
|
|
33053
|
+
* Absent on a pre-D709 hub.
|
|
33054
|
+
*/
|
|
33055
|
+
appLinkHub: string().nullable()
|
|
32978
33056
|
});
|
|
32979
33057
|
/**
|
|
32980
33058
|
* The URLs the SDK / viewer should race for API access. `baseUrls` empty =
|
|
@@ -33028,7 +33106,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
|
|
|
33028
33106
|
* Pass `'https'` when the caller is itself loaded over HTTPS
|
|
33029
33107
|
* to avoid mixed-content blocks in the browser. The public
|
|
33030
33108
|
* tunnel always emits `https://` regardless. */
|
|
33031
|
-
scheme: _enum(["http", "https"]).optional()
|
|
33109
|
+
scheme: _enum(["http", "https"]).optional(),
|
|
33110
|
+
/** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
|
|
33111
|
+
* an address notification links can be flagged with, not a hub
|
|
33112
|
+
* route. Default `false`: racers and rankers never see it. */
|
|
33113
|
+
includeAppLink: boolean().optional()
|
|
33032
33114
|
}), GetConnectionEndpointsResultSchema), method(_void(), NotificationEndpointSchema), method(object({ baseUrl: string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }), method(_void(), ViewerEndpointsSchema), method(object({ baseUrls: array(string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }), method(_void(), AllowedAddressesSchema), method(AllowedAddressesSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), AllowedAddressesSchema, { kind: "mutation" }), method(_void(), TlsStatusSchema), method(object({ reason: string().optional() }), TlsStatusSchema, {
|
|
33033
33115
|
kind: "mutation",
|
|
33034
33116
|
auth: "admin"
|
|
@@ -33357,7 +33439,7 @@ var MeshPeerSchema = object({
|
|
|
33357
33439
|
tags: array(string()).readonly(),
|
|
33358
33440
|
/** True when the peer advertises itself as an exit-node candidate. */
|
|
33359
33441
|
exitNodeOption: boolean(),
|
|
33360
|
-
/** Subnet routes the peer advertises (e.g. `["192.168.
|
|
33442
|
+
/** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
|
|
33361
33443
|
advertisedRoutes: array(string()).readonly(),
|
|
33362
33444
|
/** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
|
|
33363
33445
|
userLogin: string().nullable(),
|
|
@@ -53834,12 +53916,14 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
|
|
|
53834
53916
|
{
|
|
53835
53917
|
id: "addons",
|
|
53836
53918
|
family: "system",
|
|
53837
|
-
label: "Addons",
|
|
53838
|
-
kinds: [
|
|
53839
|
-
|
|
53840
|
-
|
|
53841
|
-
|
|
53842
|
-
|
|
53919
|
+
label: "Addons stopped",
|
|
53920
|
+
kinds: ["addon-crash-loop"]
|
|
53921
|
+
},
|
|
53922
|
+
{
|
|
53923
|
+
id: "addon-updates",
|
|
53924
|
+
family: "system",
|
|
53925
|
+
label: "Addon updates",
|
|
53926
|
+
kinds: ["addon-update-available", "addon-updated"]
|
|
53843
53927
|
},
|
|
53844
53928
|
{
|
|
53845
53929
|
id: "updates",
|
|
@@ -53999,13 +54083,15 @@ var STATE_SUMMARY_SECTION_VARS = [
|
|
|
53999
54083
|
},
|
|
54000
54084
|
{
|
|
54001
54085
|
name: "summaryAddons",
|
|
54002
|
-
label: "Summary: addons",
|
|
54003
|
-
example: "
|
|
54004
|
-
kinds: [
|
|
54005
|
-
|
|
54006
|
-
|
|
54007
|
-
|
|
54008
|
-
|
|
54086
|
+
label: "Summary: addons stopped",
|
|
54087
|
+
example: "hub — recorder stopped after repeated crashes",
|
|
54088
|
+
kinds: ["addon-crash-loop"]
|
|
54089
|
+
},
|
|
54090
|
+
{
|
|
54091
|
+
name: "summaryAddonUpdates",
|
|
54092
|
+
label: "Summary: addon updates",
|
|
54093
|
+
example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
|
|
54094
|
+
kinds: ["addon-update-available", "addon-updated"]
|
|
54009
54095
|
},
|
|
54010
54096
|
{
|
|
54011
54097
|
name: "summaryUpdates",
|
|
@@ -54089,6 +54175,7 @@ var STATE_SUMMARY_VARS = [
|
|
|
54089
54175
|
[...RULE_BODY_ONLY],
|
|
54090
54176
|
[...RULE_TEXT],
|
|
54091
54177
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
54178
|
+
([...RULE_TEXT], [...COMBINED_ALARM]),
|
|
54092
54179
|
([...RULE_TEXT], [...DEVICE_EVENTS]),
|
|
54093
54180
|
([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
|
|
54094
54181
|
([...RULE_TEXT], [...CONSUMABLE]),
|
|
@@ -59826,6 +59913,75 @@ function createAccessoryDevice(kind, ctx, parent) {
|
|
|
59826
59913
|
default: throw new Error(`Hikvision provider does not implement accessory kind "${String(kind)}"`);
|
|
59827
59914
|
}
|
|
59828
59915
|
}
|
|
59916
|
+
var ALARM_INGRESS_MESSAGE = "hikvision: alarm event received";
|
|
59917
|
+
var countsRecord = (counts) => Object.fromEntries(counts);
|
|
59918
|
+
function createAlarmIngressLog(deps) {
|
|
59919
|
+
const intervalMs = deps.intervalMs ?? 6e4;
|
|
59920
|
+
const bursts = /* @__PURE__ */ new Map();
|
|
59921
|
+
const tags = { deviceId: deps.deviceId };
|
|
59922
|
+
const received = (ev) => {
|
|
59923
|
+
const existing = bursts.get(ev.camStreamId);
|
|
59924
|
+
const burst = existing ?? {
|
|
59925
|
+
startedAt: ev.at,
|
|
59926
|
+
lastLineAt: ev.at,
|
|
59927
|
+
counts: /* @__PURE__ */ new Map(),
|
|
59928
|
+
types: /* @__PURE__ */ new Set(),
|
|
59929
|
+
ticks: 0
|
|
59930
|
+
};
|
|
59931
|
+
if (!existing) bursts.set(ev.camStreamId, burst);
|
|
59932
|
+
burst.ticks += 1;
|
|
59933
|
+
if (!burst.types.has(ev.type)) {
|
|
59934
|
+
burst.types.add(ev.type);
|
|
59935
|
+
deps.logger.info(ALARM_INGRESS_MESSAGE, {
|
|
59936
|
+
tags,
|
|
59937
|
+
meta: {
|
|
59938
|
+
edge: "start",
|
|
59939
|
+
type: ev.type,
|
|
59940
|
+
state: ev.state,
|
|
59941
|
+
camStreamId: ev.camStreamId,
|
|
59942
|
+
...ev.aiClass === null ? {} : { aiClass: ev.aiClass },
|
|
59943
|
+
...existing ? { burstAgeMs: ev.at - burst.startedAt } : {}
|
|
59944
|
+
}
|
|
59945
|
+
});
|
|
59946
|
+
burst.lastLineAt = Math.max(burst.lastLineAt, ev.at);
|
|
59947
|
+
return;
|
|
59948
|
+
}
|
|
59949
|
+
burst.counts.set(ev.type, (burst.counts.get(ev.type) ?? 0) + 1);
|
|
59950
|
+
if (ev.at - burst.lastLineAt < intervalMs) return;
|
|
59951
|
+
deps.logger.info(ALARM_INGRESS_MESSAGE, {
|
|
59952
|
+
tags,
|
|
59953
|
+
meta: {
|
|
59954
|
+
edge: "ongoing",
|
|
59955
|
+
camStreamId: ev.camStreamId,
|
|
59956
|
+
counts: countsRecord(burst.counts),
|
|
59957
|
+
windowMs: ev.at - burst.lastLineAt,
|
|
59958
|
+
burstAgeMs: ev.at - burst.startedAt
|
|
59959
|
+
}
|
|
59960
|
+
});
|
|
59961
|
+
burst.counts.clear();
|
|
59962
|
+
burst.lastLineAt = ev.at;
|
|
59963
|
+
};
|
|
59964
|
+
const ended = (camStreamId, at) => {
|
|
59965
|
+
const burst = bursts.get(camStreamId);
|
|
59966
|
+
if (!burst) return;
|
|
59967
|
+
bursts.delete(camStreamId);
|
|
59968
|
+
deps.logger.info(ALARM_INGRESS_MESSAGE, {
|
|
59969
|
+
tags,
|
|
59970
|
+
meta: {
|
|
59971
|
+
edge: "end",
|
|
59972
|
+
camStreamId,
|
|
59973
|
+
counts: countsRecord(burst.counts),
|
|
59974
|
+
ticks: burst.ticks,
|
|
59975
|
+
types: [...burst.types],
|
|
59976
|
+
durationMs: at - burst.startedAt
|
|
59977
|
+
}
|
|
59978
|
+
});
|
|
59979
|
+
};
|
|
59980
|
+
return {
|
|
59981
|
+
received,
|
|
59982
|
+
ended
|
|
59983
|
+
};
|
|
59984
|
+
}
|
|
59829
59985
|
function createAlarmTypeTally(deps) {
|
|
59830
59986
|
const intervalMs = deps.intervalMs ?? 3e5;
|
|
59831
59987
|
const lastLoggedAt = /* @__PURE__ */ new Map();
|
|
@@ -64212,6 +64368,20 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
64212
64368
|
});
|
|
64213
64369
|
return this.unhandledAlarmsTally;
|
|
64214
64370
|
}
|
|
64371
|
+
/**
|
|
64372
|
+
* Sampled info-level ingress line for the motion-bearing alarms (VMD and
|
|
64373
|
+
* smart events): one at each burst's start, one per minute while it lasts,
|
|
64374
|
+
* one at its end — never one per ~1 Hz tick. Lazy for the same reason as
|
|
64375
|
+
* `unhandledAlarms`.
|
|
64376
|
+
*/
|
|
64377
|
+
motionIngressLog = null;
|
|
64378
|
+
get motionIngress() {
|
|
64379
|
+
this.motionIngressLog ??= createAlarmIngressLog({
|
|
64380
|
+
logger: this.ctx.logger,
|
|
64381
|
+
deviceId: this.id
|
|
64382
|
+
});
|
|
64383
|
+
return this.motionIngressLog;
|
|
64384
|
+
}
|
|
64215
64385
|
/** Hysteresis: drop motion-active back to inactive after this many ms with
|
|
64216
64386
|
* no fresh `VMD` event. Hikvision fires VMD every ~1s while motion is
|
|
64217
64387
|
* active; missing 2 ticks signals the burst is over. */
|
|
@@ -66787,6 +66957,13 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
66787
66957
|
handleMotionEvent(ev, camStreamId) {
|
|
66788
66958
|
const now = ev.observedAt;
|
|
66789
66959
|
this.lastMotionAt.set(camStreamId, now);
|
|
66960
|
+
this.motionIngress.received({
|
|
66961
|
+
type: ev.type,
|
|
66962
|
+
state: ev.state,
|
|
66963
|
+
aiClass: ev.aiClass,
|
|
66964
|
+
camStreamId,
|
|
66965
|
+
at: now
|
|
66966
|
+
});
|
|
66790
66967
|
this.ctx.eventBus.emit(providerMotionEvent(this.eventSource, {
|
|
66791
66968
|
deviceId: this.id,
|
|
66792
66969
|
detected: true,
|
|
@@ -66799,6 +66976,7 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
66799
66976
|
const clearedAt = Date.now();
|
|
66800
66977
|
this.lastMotionAt.set(camStreamId, 0);
|
|
66801
66978
|
this.motionInactiveTimers.delete(camStreamId);
|
|
66979
|
+
this.motionIngress.ended(camStreamId, clearedAt);
|
|
66802
66980
|
this.ctx.eventBus.emit(providerMotionEvent(this.eventSource, {
|
|
66803
66981
|
deviceId: this.id,
|
|
66804
66982
|
detected: false,
|
|
@@ -66836,7 +67014,7 @@ var HikvisionCamera = class HikvisionCamera extends BaseDevice {
|
|
|
66836
67014
|
key: "host",
|
|
66837
67015
|
label: "Camera IP",
|
|
66838
67016
|
required: true,
|
|
66839
|
-
placeholder: "192.168.
|
|
67017
|
+
placeholder: "192.168.10.100"
|
|
66840
67018
|
},
|
|
66841
67019
|
{
|
|
66842
67020
|
type: "number",
|
|
@@ -76885,13 +77063,13 @@ function enumerateHosts(cidr) {
|
|
|
76885
77063
|
for (let d = 1; d <= 254; d += 1) out.push(`${a}.${b}.${c}.${d}`);
|
|
76886
77064
|
return out;
|
|
76887
77065
|
}
|
|
76888
|
-
/** Best-effort local /24 from the first non-internal IPv4 interface (e.g. `192.168.
|
|
77066
|
+
/** Best-effort local /24 from the first non-internal IPv4 interface (e.g. `192.168.10.0/24`). */
|
|
76889
77067
|
function detectLocalCidr() {
|
|
76890
77068
|
for (const addrs of Object.values(networkInterfaces())) for (const addr of addrs ?? []) if (addr.family === "IPv4" && !addr.internal) {
|
|
76891
77069
|
const [a, b, c] = addr.address.split(".");
|
|
76892
77070
|
return `${a}.${b}.${c}.0/24`;
|
|
76893
77071
|
}
|
|
76894
|
-
return "192.168.
|
|
77072
|
+
return "192.168.10.0/24";
|
|
76895
77073
|
}
|
|
76896
77074
|
/** Flatten a host into a flat row-key slug — must match `generateStableId`'s host fallback so a
|
|
76897
77075
|
* re-scan of a host-added camera is detected as already onboarded. */
|
|
@@ -76961,7 +77139,7 @@ function buildCreationFormSchema() {
|
|
|
76961
77139
|
key: "host",
|
|
76962
77140
|
label: "Camera IP",
|
|
76963
77141
|
required: true,
|
|
76964
|
-
placeholder: "192.168.
|
|
77142
|
+
placeholder: "192.168.10.100",
|
|
76965
77143
|
inputType: "text"
|
|
76966
77144
|
},
|
|
76967
77145
|
{
|
|
@@ -77011,14 +77189,14 @@ function buildDiscoveryParamsFormSchema() {
|
|
|
77011
77189
|
}, {
|
|
77012
77190
|
id: "scan",
|
|
77013
77191
|
title: "Scan options",
|
|
77014
|
-
description: "The credentialed ISAPI subnet scan is the reliable path (multicast WS-Discovery is not received on a macvlan/br0 hub). IMPORTANT: leaving the network blank scans only the HUB’s own subnet — if your cameras are on a DIFFERENT subnet than the hub, you MUST enter their CIDR here (e.g. 192.168.
|
|
77192
|
+
description: "The credentialed ISAPI subnet scan is the reliable path (multicast WS-Discovery is not received on a macvlan/br0 hub). IMPORTANT: leaving the network blank scans only the HUB’s own subnet — if your cameras are on a DIFFERENT subnet than the hub, you MUST enter their CIDR here (e.g. 192.168.60.0/24), otherwise the scan returns nothing.",
|
|
77015
77193
|
columns: 1,
|
|
77016
77194
|
fields: [{
|
|
77017
77195
|
type: "text",
|
|
77018
77196
|
key: "networkCidr",
|
|
77019
77197
|
label: "Network (CIDR) — required if cameras are on a different subnet than the hub",
|
|
77020
77198
|
required: false,
|
|
77021
|
-
placeholder: "192.168.
|
|
77199
|
+
placeholder: "192.168.60.0/24"
|
|
77022
77200
|
}, {
|
|
77023
77201
|
type: "number",
|
|
77024
77202
|
key: "timeoutMs",
|
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.143",
|
|
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",
|