@camstack/addon-provider-amcrest 0.2.129 → 0.2.131

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.
Files changed (3) hide show
  1. package/dist/addon.js +249 -140
  2. package/dist/addon.mjs +249 -140
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -10510,6 +10510,57 @@ var ClipSearchStrictnessSchema = _enum([
10510
10510
  "balanced",
10511
10511
  "strict"
10512
10512
  ]);
10513
+ new Set([
10514
+ {
10515
+ id: "person",
10516
+ name: "Person"
10517
+ },
10518
+ {
10519
+ id: "vehicle",
10520
+ name: "Vehicle"
10521
+ },
10522
+ {
10523
+ id: "animal",
10524
+ name: "Animal"
10525
+ },
10526
+ {
10527
+ id: "package",
10528
+ name: "Package"
10529
+ }
10530
+ ].map((l) => l.id));
10531
+ var COCO_TO_MACRO = {
10532
+ mapping: {
10533
+ person: "person",
10534
+ bicycle: "vehicle",
10535
+ car: "vehicle",
10536
+ motorcycle: "vehicle",
10537
+ airplane: "vehicle",
10538
+ bus: "vehicle",
10539
+ train: "vehicle",
10540
+ truck: "vehicle",
10541
+ boat: "vehicle",
10542
+ bird: "animal",
10543
+ cat: "animal",
10544
+ dog: "animal",
10545
+ horse: "animal",
10546
+ sheep: "animal",
10547
+ cow: "animal",
10548
+ elephant: "animal",
10549
+ bear: "animal",
10550
+ zebra: "animal",
10551
+ giraffe: "animal",
10552
+ suitcase: "package",
10553
+ backpack: "package",
10554
+ handbag: "package"
10555
+ },
10556
+ preserveOriginal: false
10557
+ };
10558
+ /** Steps a root detector may declare it fills. */
10559
+ var FILLABLE_STEP_IDS = [
10560
+ "animal-classifier",
10561
+ "vehicle-classifier",
10562
+ "package-detection"
10563
+ ];
10513
10564
  var MODEL_FORMATS = [
10514
10565
  "onnx",
10515
10566
  "coreml",
@@ -10579,11 +10630,11 @@ var ModelVariantGroupSchema = object({
10579
10630
  /** Quantization axis. Omit ⇒ the fp32 base build. */
10580
10631
  precision: _enum(["fp32", "int8"]).optional(),
10581
10632
  /**
10582
- * Speed-optimization axis. Omit ⇒ the standard build. `fast` marks a
10583
- * latency-optimized export (e.g. ReLU-activation variant) — the slot the
10584
- * future performance variants plug into.
10633
+ * Training / latency-variant axis. Omit ⇒ the standard build. `test` marks
10634
+ * an alternate training of the same family (Scrypted `relu_test`); chip
10635
+ * label is "Test".
10585
10636
  */
10586
- optimization: _enum(["standard", "fast"]).optional(),
10637
+ optimization: _enum(["standard", "test"]).optional(),
10587
10638
  /**
10588
10639
  * Input-resolution axis (square input side, px). Omit ⇒ the family's native
10589
10640
  * resolution (640 for yolo26). Reduced-input builds (320 / 256) are a big,
@@ -10606,9 +10657,10 @@ var ModelProviderIdSchema = _enum([
10606
10657
  * are per upstream. A persisted custom-model row written when this field was a
10607
10658
  * string still parses — the string is lifted into `{ weights }`.
10608
10659
  *
10609
- * Every licence surface (the admin "Models and licenses" page,
10610
- * `THIRD_PARTY_MODELS.md`, the Hugging Face model table) is GENERATED from
10611
- * these records by `scripts/gen-model-licenses.ts` (D661).
10660
+ * Every machine licence surface (`THIRD_PARTY_MODELS.md`, the Hugging Face
10661
+ * model table) is GENERATED from these records by
10662
+ * `scripts/gen-model-licenses.ts` (D661). Operator-facing docs live in
10663
+ * `camstack/docs` → `/docs/detection/models`.
10612
10664
  */
10613
10665
  var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { weights: value } : value, object({
10614
10666
  /** SPDX id of the WEIGHTS' terms, a `LicenseRef-*` for non-SPDX terms, or `UNKNOWN`. */
@@ -10641,6 +10693,12 @@ var ModelLicenseSchema = preprocess((value) => typeof value === "string" ? { wei
10641
10693
  /** True when the weights or their data forbid commercial use. */
10642
10694
  nonCommercial: boolean().optional()
10643
10695
  }));
10696
+ /** One `fillsClassifierEffects` entry — see `ModelCatalogEntry.fillsClassifierEffects`. */
10697
+ var FillsClassifierEffectSchema = object({
10698
+ stepId: _enum(FILLABLE_STEP_IDS),
10699
+ labelToEffect: record(string(), string()),
10700
+ uncovered: array(string()).readonly()
10701
+ });
10644
10702
  var ModelCatalogEntrySchema = object({
10645
10703
  id: string(),
10646
10704
  name: string(),
@@ -10767,6 +10825,16 @@ var ModelCatalogEntrySchema = object({
10767
10825
  */
10768
10826
  classMap: DetectionCatalogClassMapSchema.optional(),
10769
10827
  /**
10828
+ * The pipeline steps this ROOT detector already does the work of (D715):
10829
+ * its labels name the effect the step exists to produce (`deer` for
10830
+ * `animal-classifier`, `package` for `package-detection`). Derived from the
10831
+ * labelmap by `deriveClassifierFills`, never hand-written; a COCO model
10832
+ * declares none. Whether a declaration is honoured is the step's own
10833
+ * `labelLink` policy (`resolveLabelLink`). `labelToEffect` values are in
10834
+ * `FILLABLE_STEP_VOCABULARIES` (guard `check-label-link-vocabulary`).
10835
+ */
10836
+ fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional(),
10837
+ /**
10770
10838
  * The model's CLIP feature-space contract (text encoder, dimension, vector
10771
10839
  * index, token window, search floor) — see `clip-model.ts`. Present ONLY on a
10772
10840
  * CLIP image encoder; its absence is what "not a CLIP model" means to the
@@ -14239,7 +14307,7 @@ DeviceType.Camera;
14239
14307
  * This matters here more than anywhere else in the repo, because every provider
14240
14308
  * that knows a device's host knows its PASSWORD too: `{ host, port, username,
14241
14309
  * password }` sit in one object on Hikvision, Amcrest, Reolink and ONVIF alike,
14242
- * and `http://admin:hunter2@192.168.x.x/` is a URL a browser accepts. The
14310
+ * and `http://admin:hunter2@192.168.60.139/` is a URL a browser accepts. The
14243
14311
  * camera's own page will ask for its own login. That is correct, and pre-
14244
14312
  * filling it is the operator's business, not ours.
14245
14313
  *
@@ -17524,7 +17592,7 @@ method(object({
17524
17592
  timeoutMs: 6e5
17525
17593
  });
17526
17594
  method(object({
17527
- nodeId: string(),
17595
+ targetNodeId: string(),
17528
17596
  modelId: string(),
17529
17597
  format: _enum(MODEL_FORMATS),
17530
17598
  entry: ModelCatalogEntrySchema
@@ -18090,51 +18158,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
18090
18158
  kind: "mutation",
18091
18159
  auth: "admin"
18092
18160
  });
18093
- new Set([
18094
- {
18095
- id: "person",
18096
- name: "Person"
18097
- },
18098
- {
18099
- id: "vehicle",
18100
- name: "Vehicle"
18101
- },
18102
- {
18103
- id: "animal",
18104
- name: "Animal"
18105
- },
18106
- {
18107
- id: "package",
18108
- name: "Package"
18109
- }
18110
- ].map((l) => l.id));
18111
- var COCO_TO_MACRO = {
18112
- mapping: {
18113
- person: "person",
18114
- bicycle: "vehicle",
18115
- car: "vehicle",
18116
- motorcycle: "vehicle",
18117
- airplane: "vehicle",
18118
- bus: "vehicle",
18119
- train: "vehicle",
18120
- truck: "vehicle",
18121
- boat: "vehicle",
18122
- bird: "animal",
18123
- cat: "animal",
18124
- dog: "animal",
18125
- horse: "animal",
18126
- sheep: "animal",
18127
- cow: "animal",
18128
- elephant: "animal",
18129
- bear: "animal",
18130
- zebra: "animal",
18131
- giraffe: "animal",
18132
- suitcase: "package",
18133
- backpack: "package",
18134
- handbag: "package"
18135
- },
18136
- preserveOriginal: false
18137
- };
18138
18161
  var AUDIO_MACRO_LABELS = [
18139
18162
  {
18140
18163
  id: "speech",
@@ -18930,6 +18953,8 @@ var NcSystemEventKindSchema = _enum([
18930
18953
  "stream-offline",
18931
18954
  "node-online",
18932
18955
  "node-offline",
18956
+ "node-restarting",
18957
+ "node-updating",
18933
18958
  "node-inference-unavailable",
18934
18959
  "detection-blind",
18935
18960
  "addon-crash-loop",
@@ -20028,8 +20053,9 @@ var NcRuleSchema = NcRuleInputSchema.extend({
20028
20053
  disabledTargetIds: array(string()).default([])
20029
20054
  });
20030
20055
  /**
20031
- * The state-summary SECTIONS (D647 / D698): the D637 kind groups that report a
20032
- * still-true state. `backups-exports` and `alarm` are not sections — a completed
20056
+ * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
20057
+ * report a still-true state. `addons` is the crash-looped addons only and
20058
+ * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
20033
20059
  * job is not an open state, and the alarm stays instant.
20034
20060
  */
20035
20061
  var NcStateSummarySectionIdSchema = _enum([
@@ -20041,6 +20067,7 @@ var NcStateSummarySectionIdSchema = _enum([
20041
20067
  "detection-blind",
20042
20068
  "nodes",
20043
20069
  "addons",
20070
+ "addon-updates",
20044
20071
  "updates"
20045
20072
  ]);
20046
20073
  /** One section of a previewed state summary. `unavailable` = its state could not be read. */
@@ -20521,7 +20548,19 @@ var NcAlarmModeDelaysListSchema = array(object({
20521
20548
  * When this mode may be armed. Absent / null = always allowed (same as a
20522
20549
  * rule with no `schedule`). Evaluated with {@link isScheduleActive}.
20523
20550
  */
20524
- schedule: NcScheduleSchema.optional()
20551
+ schedule: NcScheduleSchema.optional(),
20552
+ /**
20553
+ * D706: while this mode's ENTRY DELAY runs, do the alarm rules' own pushes go out (as an
20554
+ * intrusion naming the locations)? Absent = false — the entry-delay notification is the
20555
+ * only one, and the matches ride the triggered notification's mosaic (D705).
20556
+ */
20557
+ notifyDuringEntryDelay: boolean().optional(),
20558
+ /**
20559
+ * D706: notify "watched again" when a sensor this arm bypassed closes and opens again.
20560
+ * Absent = false (logged, not sent). Even when true it is not sent when that same sensor
20561
+ * is the one that starts the entry delay — the entry-delay notification already names it.
20562
+ */
20563
+ notifySensorRewatched: boolean().optional()
20525
20564
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one entry per arm mode" });
20526
20565
  var NcAlarmModeDelaysEditListSchema = array(object({
20527
20566
  mode: AlarmArmModeSchema,
@@ -20530,7 +20569,11 @@ var NcAlarmModeDelaysEditListSchema = array(object({
20530
20569
  triggeredDurationSec: NcAlarmTriggeredSecSchema.nullable().optional(),
20531
20570
  postTriggeredAction: NcAlarmPostTriggeredActionSchema.nullable().optional(),
20532
20571
  /** Set a schedule, or `null` to clear (mode always allowed again). */
20533
- schedule: NcScheduleSchema.nullable().optional()
20572
+ schedule: NcScheduleSchema.nullable().optional(),
20573
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20574
+ notifyDuringEntryDelay: boolean().nullable().optional(),
20575
+ /** D706: set the switch, or `null` to clear it (back to the default, false). */
20576
+ notifySensorRewatched: boolean().nullable().optional()
20534
20577
  })).max(8).refine(uniqueModes, { message: "modeDelays: at most one edit per arm mode" });
20535
20578
  /** One switch of the alarm table: put ONE device on, or take it off, ONE mode's list. */
20536
20579
  var NcAlarmNonBlockingEditSchema = object({
@@ -21491,39 +21534,19 @@ Object.freeze(Object.fromEntries([{
21491
21534
  label: "Face recognition model",
21492
21535
  defaultModelId: "auraface-r100",
21493
21536
  indexName: "the enrolled face gallery",
21494
- options: [
21495
- {
21496
- id: "auraface-r100",
21497
- label: "AuraFace R100"
21498
- },
21499
- {
21500
- id: "arcface-r100",
21501
- label: "ArcFace (ResNet34)"
21502
- },
21503
- {
21504
- id: "inception-resnet-v1",
21505
- label: "Inception ResNet V1"
21506
- }
21507
- ]
21537
+ options: [{
21538
+ id: "auraface-r100",
21539
+ label: "AuraFace R100"
21540
+ }]
21508
21541
  }, {
21509
21542
  stepId: "clip-embedding",
21510
21543
  label: "Semantic search model",
21511
- defaultModelId: "mobileclip-s1",
21544
+ defaultModelId: "siglip2-b16-224",
21512
21545
  indexName: "the object semantic-search index",
21513
- options: [
21514
- {
21515
- id: "mobileclip-s1",
21516
- label: "MobileCLIP S1"
21517
- },
21518
- {
21519
- id: "mobileclip-s2",
21520
- label: "MobileCLIP S2"
21521
- },
21522
- {
21523
- id: "siglip2-b16-224",
21524
- label: "SigLIP2 B/16 (Apache-2.0)"
21525
- }
21526
- ]
21546
+ options: [{
21547
+ id: "siglip2-b16-224",
21548
+ label: "SigLIP2 B/16 (Apache-2.0)"
21549
+ }]
21527
21550
  }].map((s) => [s.stepId, s.defaultModelId])));
21528
21551
  var CLUSTER_STEP_SETTING_FIELDS = [{
21529
21552
  stepId: "face-embedding",
@@ -23578,7 +23601,8 @@ var PipelineModelOptionSchema = object({
23578
23601
  group: ModelVariantGroupSchema.optional(),
23579
23602
  legacy: boolean().optional(),
23580
23603
  provider: ModelProviderIdSchema.optional(),
23581
- defaultConfidence: number().optional()
23604
+ defaultConfidence: number().optional(),
23605
+ fillsClassifierEffects: array(FillsClassifierEffectSchema).readonly().optional()
23582
23606
  });
23583
23607
  var ConfigFieldBridge = custom();
23584
23608
  var PipelineAddonSchemaSchema = object({
@@ -24411,6 +24435,22 @@ var MotionSourceEnum = _enum([
24411
24435
  "device-activity"
24412
24436
  ]);
24413
24437
  /**
24438
+ * What opened an on-motion detection SESSION: the motion source that passed the
24439
+ * camera's `motionSources` gate, and what raised it (the camera itself, or a
24440
+ * sensor the operator attached — D672). `sensorName` is the attached sensor's
24441
+ * device name as the orchestrator's camera-sensors resolution last read it;
24442
+ * absent when unknown or when the camera itself triggered.
24443
+ *
24444
+ * Carried on the session attach ONLY so the runner's `DetectionPhaseTransition`
24445
+ * can say what started the session (D672 rule 11, amended 2026-10-06). The
24446
+ * runner never decides anything on it.
24447
+ */
24448
+ var SessionTriggerSchema = object({
24449
+ source: MotionSourceEnum,
24450
+ triggeredBy: MotionTriggeredBySchema,
24451
+ sensorName: string().optional()
24452
+ });
24453
+ /**
24414
24454
  * List of motion sources active on a camera. Empty array is valid:
24415
24455
  * "no source" — happens for battery cams without firmware motion when
24416
24456
  * the operator hasn't opted into the SW analyzer (would hold the
@@ -24662,6 +24702,14 @@ var RunnerCameraConfigSchema = object({
24662
24702
  */
24663
24703
  motionSources: MotionSourcesSchema.default(["analyzer"]),
24664
24704
  /**
24705
+ * What opened this attach, on a SESSION attach only (see
24706
+ * {@link SessionTriggerSchema}). OPTIONAL with no default: absent on a
24707
+ * standing attach, an occupancy burst, a re-dispatch with no motion event
24708
+ * behind it, and every attach from an orchestrator older than the field — the
24709
+ * runner then names no source on the phase event.
24710
+ */
24711
+ sessionTrigger: SessionTriggerSchema.optional(),
24712
+ /**
24665
24713
  * WHICH root detector runs for this camera. The detection counterpart of
24666
24714
  * {@link motionSources}, same shape and same discipline: a per-camera list
24667
24715
  * of sources the system understands, vendor-agnostic, owned here.
@@ -32563,7 +32611,7 @@ var LocalInterfaceSchema = object({
32563
32611
  name: string(),
32564
32612
  family: _enum(["IPv4", "IPv6"]),
32565
32613
  address: string(),
32566
- /** CIDR shorthand (e.g. `192.168.x.x/24`). Empty when OS doesn't expose. */
32614
+ /** CIDR shorthand (e.g. `192.168.10.10/24`). Empty when OS doesn't expose. */
32567
32615
  cidr: string(),
32568
32616
  /** OS netmask. Stable across calls so the SDK can hash on it. */
32569
32617
  netmask: string(),
@@ -32617,7 +32665,8 @@ var ConnectionEndpointSchema = object({
32617
32665
  "lan-ipv4",
32618
32666
  "lan-ipv6",
32619
32667
  "public",
32620
- "loopback"
32668
+ "loopback",
32669
+ "app-link"
32621
32670
  ]),
32622
32671
  /**
32623
32672
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -32686,15 +32735,27 @@ var GetConnectionEndpointsResultSchema = object({
32686
32735
  localPort: AdvertisedLocalPortSchema
32687
32736
  });
32688
32737
  /**
32689
- * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
32690
- * AUTO (resolved from the candidate ranking at send time); `resolved` reports
32691
- * what AUTO currently picks, so the UI can show the effective value either way.
32738
+ * The address flagged for notification links. `baseUrl: null` = AUTO, which
32739
+ * since D709 is the CamStack app link; `resolved` reports the effective value,
32740
+ * so the UI can show it either way.
32692
32741
  */
32693
32742
  var NotificationEndpointSchema = object({
32694
32743
  /** The operator's explicit choice, or null for AUTO. */
32695
32744
  baseUrl: string().nullable(),
32696
- /** What the ranking currently resolves to (null when nothing is reachable). */
32697
- resolved: string().nullable()
32745
+ /**
32746
+ * The effective flagged address: the explicit choice, else (AUTO) the
32747
+ * CamStack app link `https://camstack.zentik.app` (D709). A pre-D709 hub
32748
+ * answered the public tunnel / mesh here, or null.
32749
+ */
32750
+ resolved: string().nullable(),
32751
+ /**
32752
+ * The hub origin an app link carries as `hub=` (D709): the hub's best
32753
+ * public HTTPS address — the tunnel, then the mesh (Tailscale MagicDNS) —
32754
+ * picked from this cap's own palette by `pickAppLinkHub`. `null` when the
32755
+ * hub advertises neither; the app link is then minted without `hub=`.
32756
+ * Absent on a pre-D709 hub.
32757
+ */
32758
+ appLinkHub: string().nullable()
32698
32759
  });
32699
32760
  /**
32700
32761
  * The URLs the SDK / viewer should race for API access. `baseUrls` empty =
@@ -32748,7 +32809,11 @@ method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(obje
32748
32809
  * Pass `'https'` when the caller is itself loaded over HTTPS
32749
32810
  * to avoid mixed-content blocks in the browser. The public
32750
32811
  * tunnel always emits `https://` regardless. */
32751
- scheme: _enum(["http", "https"]).optional()
32812
+ scheme: _enum(["http", "https"]).optional(),
32813
+ /** Append the CamStack app link entry (`kind: 'app-link'`, D709) —
32814
+ * an address notification links can be flagged with, not a hub
32815
+ * route. Default `false`: racers and rankers never see it. */
32816
+ includeAppLink: boolean().optional()
32752
32817
  }), 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, {
32753
32818
  kind: "mutation",
32754
32819
  auth: "admin"
@@ -33077,7 +33142,7 @@ var MeshPeerSchema = object({
33077
33142
  tags: array(string()).readonly(),
33078
33143
  /** True when the peer advertises itself as an exit-node candidate. */
33079
33144
  exitNodeOption: boolean(),
33080
- /** Subnet routes the peer advertises (e.g. `["192.168.x.x/24"]`). */
33145
+ /** Subnet routes the peer advertises (e.g. `["192.168.10.0/24"]`). */
33081
33146
  advertisedRoutes: array(string()).readonly(),
33082
33147
  /** Owner account login (e.g. `foo@example.com`). Null for tagged peers. */
33083
33148
  userLogin: string().nullable(),
@@ -33207,7 +33272,8 @@ var MotionStatusSchema = object({
33207
33272
  * writes this slice (not even a boot seed), and neither does the runner's
33208
33273
  * phase machine: its phase is "a session is attached" for every on-motion
33209
33274
  * session, not motion, and it now has its own `DetectionPhaseTransition`.
33210
- * A provider's `isDetected` answers from the hub's slice.
33275
+ * A provider's `isDetected` and `getStatus` answer from the hub's slice
33276
+ * (`hubMotionNativeProvider`).
33211
33277
  * - **Sensors** (Home Assistant binary sensors, Homematic, Matter, Alexa
33212
33278
  * imports) — the provider pushes both edges itself, from the upstream
33213
33279
  * system's own state change. That is the sensor's own fact.
@@ -34405,7 +34471,13 @@ var OsdStatusSchema = object({ overlays: array(object({
34405
34471
  */
34406
34472
  availablePositions: array(OsdPositionEnum).readonly(),
34407
34473
  /** Firmware does not allow editing the `text` field (e.g. timestamp). */
34408
- readOnly: boolean().optional()
34474
+ readOnly: boolean().optional(),
34475
+ /**
34476
+ * Firmware text ceiling for this overlay. Absent = no known limit.
34477
+ * The OSD manager truncates here on every write — never stores its own
34478
+ * copy on the binding.
34479
+ */
34480
+ maxCharacters: number().int().positive().optional()
34409
34481
  })).readonly() });
34410
34482
  var OsdOverlayPatchSchema = object({
34411
34483
  enabled: boolean().optional(),
@@ -34502,7 +34574,9 @@ var OsdTemplateBindingSchema = object({
34502
34574
  valuePath: string().min(1).max(64),
34503
34575
  /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
34504
34576
  displayUnit: string().max(16).optional(),
34505
- maxDecimals: number().int().min(0).max(4).default(1)
34577
+ maxDecimals: number().int().min(0).max(4).default(1),
34578
+ /** Raw value → display text (same shape as slot binding). Empty string hides. */
34579
+ valueMap: record(string(), string()).optional()
34506
34580
  });
34507
34581
  /**
34508
34582
  * A named template: bindings (alias → leaf field) + a parser that
@@ -34552,18 +34626,11 @@ var OsdSlotBindingSchema = object({
34552
34626
  source: OsdSourceSchema,
34553
34627
  /** `${value}` and `${unit}` are substituted; every occurrence. */
34554
34628
  template: string().max(96).default("${value}"),
34555
- /** Truncate with an ellipsis past this length. Absent = no limit. */
34556
- maxCharacters: number().int().min(4).max(64).optional(),
34557
34629
  /**
34558
34630
  * Decimal places for a numeric value. `0` yields an integer — the
34559
34631
  * documented workaround for firmwares that reject `.` in overlay text.
34560
34632
  */
34561
34633
  maxDecimals: number().int().min(0).max(4).default(1),
34562
- /**
34563
- * Appended via `${unit}` when no unit conversion produces a label.
34564
- * Prefer `source.displayUnit` for converted device-state values.
34565
- */
34566
- unitLabel: string().max(8).optional(),
34567
34634
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
34568
34635
  valueMap: record(string(), string()).optional(),
34569
34636
  /** Time windows in which the slot is shown. Absent = always. */
@@ -34572,9 +34639,7 @@ var OsdSlotBindingSchema = object({
34572
34639
  * Display gate, in the notification centre's condition vocabulary.
34573
34640
  * Only the keys reported by `getConditionSupport` are accepted.
34574
34641
  */
34575
- conditions: NcConditionsSchema.optional(),
34576
- /** Rendered when the gate is closed or the value unreadable. Empty = hide. */
34577
- fallbackText: string().max(64).default("")
34642
+ conditions: NcConditionsSchema.optional()
34578
34643
  });
34579
34644
  /** One camera slot, as the operator sees it: firmware truth + our binding. */
34580
34645
  var OsdSlotViewSchema = object({
@@ -53346,6 +53411,8 @@ new Map(Object.entries({
53346
53411
  "detection-blind": "device-events",
53347
53412
  "node-online": "system",
53348
53413
  "node-offline": "system",
53414
+ "node-restarting": "system",
53415
+ "node-updating": "system",
53349
53416
  "node-inference-unavailable": "system",
53350
53417
  "addon-crash-loop": "system",
53351
53418
  "addon-update-available": "system",
@@ -53423,18 +53490,21 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
53423
53490
  kinds: [
53424
53491
  "node-offline",
53425
53492
  "node-online",
53493
+ "node-restarting",
53426
53494
  "node-inference-unavailable"
53427
53495
  ]
53428
53496
  },
53429
53497
  {
53430
53498
  id: "addons",
53431
53499
  family: "system",
53432
- label: "Addons",
53433
- kinds: [
53434
- "addon-crash-loop",
53435
- "addon-update-available",
53436
- "addon-updated"
53437
- ]
53500
+ label: "Addons stopped",
53501
+ kinds: ["addon-crash-loop"]
53502
+ },
53503
+ {
53504
+ id: "addon-updates",
53505
+ family: "system",
53506
+ label: "Addon updates",
53507
+ kinds: ["addon-update-available", "addon-updated"]
53438
53508
  },
53439
53509
  {
53440
53510
  id: "updates",
@@ -53442,6 +53512,7 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
53442
53512
  label: "Server & container updates",
53443
53513
  kinds: [
53444
53514
  "server-update-available",
53515
+ "node-updating",
53445
53516
  "server-updated",
53446
53517
  "wrapper-update-available"
53447
53518
  ]
@@ -53589,18 +53660,21 @@ var STATE_SUMMARY_SECTION_VARS = [
53589
53660
  kinds: [
53590
53661
  "node-offline",
53591
53662
  "node-online",
53663
+ "node-restarting",
53592
53664
  "node-inference-unavailable"
53593
53665
  ]
53594
53666
  },
53595
53667
  {
53596
53668
  name: "summaryAddons",
53597
- label: "Summary: addons",
53598
- example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
53599
- kinds: [
53600
- "addon-crash-loop",
53601
- "addon-update-available",
53602
- "addon-updated"
53603
- ]
53669
+ label: "Summary: addons stopped",
53670
+ example: "hub — recorder stopped after repeated crashes",
53671
+ kinds: ["addon-crash-loop"]
53672
+ },
53673
+ {
53674
+ name: "summaryAddonUpdates",
53675
+ label: "Summary: addon updates",
53676
+ example: "hub:\n• @camstack/addon-recorder 1.2.3 → 1.2.4",
53677
+ kinds: ["addon-update-available", "addon-updated"]
53604
53678
  },
53605
53679
  {
53606
53680
  name: "summaryUpdates",
@@ -53608,6 +53682,7 @@ var STATE_SUMMARY_SECTION_VARS = [
53608
53682
  example: "server 1.2.377 → 1.2.378",
53609
53683
  kinds: [
53610
53684
  "server-update-available",
53685
+ "node-updating",
53611
53686
  "server-updated",
53612
53687
  "wrapper-update-available"
53613
53688
  ]
@@ -53684,6 +53759,7 @@ var STATE_SUMMARY_VARS = [
53684
53759
  [...RULE_BODY_ONLY],
53685
53760
  [...RULE_TEXT],
53686
53761
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
53762
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
53687
53763
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
53688
53764
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
53689
53765
  ([...RULE_TEXT], [...CONSUMABLE]),
@@ -53724,6 +53800,8 @@ var STATE_SUMMARY_VARS = [
53724
53800
  [...RULE_TEXT],
53725
53801
  [...RULE_TEXT],
53726
53802
  [...RULE_TEXT],
53803
+ [...RULE_TEXT],
53804
+ [...RULE_TEXT],
53727
53805
  ...STATE_SUMMARY_VARS
53728
53806
  ];
53729
53807
  new Set([
@@ -54060,10 +54138,9 @@ TimelapseRuleInputSchema.extend({
54060
54138
  createdAt: number(),
54061
54139
  updatedAt: number()
54062
54140
  });
54063
- async function readHubMotionDetected(deviceState, deviceId) {
54064
- let slice;
54141
+ async function readSlice(deviceState, deviceId) {
54065
54142
  try {
54066
- slice = await deviceState.getCapSlice.query({
54143
+ return await deviceState.getCapSlice.query({
54067
54144
  deviceId,
54068
54145
  capName: "motion"
54069
54146
  });
@@ -54071,10 +54148,44 @@ async function readHubMotionDetected(deviceState, deviceId) {
54071
54148
  const reason = err instanceof Error ? err.message : String(err);
54072
54149
  throw new Error(`motion state of device ${deviceId} is unreadable: ${reason}`, { cause: err });
54073
54150
  }
54074
- const detected = slice?.["detected"];
54151
+ }
54152
+ /**
54153
+ * The hub's `motion` slice as a `MotionStatus`, or `null` when nobody has
54154
+ * published it since the hub started. Throws when the hub does not answer or
54155
+ * the slice is malformed — never a made-up "no motion".
54156
+ */
54157
+ async function readHubMotionStatus(deviceState, deviceId) {
54158
+ const slice = await readSlice(deviceState, deviceId);
54159
+ if (slice === null) return null;
54160
+ const parsed = MotionStatusSchema.safeParse(slice);
54161
+ if (!parsed.success) throw new Error(`motion state of device ${deviceId} is malformed on the hub: ${parsed.error.message}`);
54162
+ return parsed.data;
54163
+ }
54164
+ async function readHubMotionDetected(deviceState, deviceId) {
54165
+ const detected = (await readSlice(deviceState, deviceId))?.["detected"];
54075
54166
  if (typeof detected !== "boolean") throw new Error(`motion state of device ${deviceId} is not published yet (unknown)`);
54076
54167
  return detected;
54077
54168
  }
54169
+ /**
54170
+ * The native `motion` provider of a camera whose slice the orchestrator writes
54171
+ * (D696): `isDetected` AND `getStatus`, both from the hub. `owner` names the
54172
+ * provider class in the deviceId-mismatch error.
54173
+ */
54174
+ function hubMotionNativeProvider(deviceState, deviceId, owner) {
54175
+ const ownDevice = (asked) => {
54176
+ if (asked !== deviceId) throw new Error(`${owner}: motion deviceId mismatch, expected ${deviceId}, got ${asked}`);
54177
+ };
54178
+ return {
54179
+ isDetected: async ({ deviceId: asked }) => {
54180
+ ownDevice(asked);
54181
+ return readHubMotionDetected(deviceState, deviceId);
54182
+ },
54183
+ getStatus: async ({ deviceId: asked }) => {
54184
+ ownDevice(asked);
54185
+ return readHubMotionStatus(deviceState, deviceId);
54186
+ }
54187
+ };
54188
+ }
54078
54189
  MotionOnMotionChangedDataSchema.omit({ triggeredBy: true });
54079
54190
  function providerMotionEvent(source, data) {
54080
54191
  return createEvent(EventCategory.ProviderMotion, source, data);
@@ -56448,14 +56559,12 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
56448
56559
  }
56449
56560
  /** Register the native `motion` cap. Its mere presence (a `kind:'native'`
56450
56561
  * `motion` binding) is what the orchestrator reads to default a
56451
- * freshly-adopted camera to `motionSources:['onboard']`. `isDetected`
56452
- * answers from the HUB's slice; an unreadable hub, or a slice nobody has
56453
- * published yet, fails the call — never `false` for unknown (D49, D393). */
56562
+ * freshly-adopted camera to `motionSources:['onboard']`. `isDetected` and
56563
+ * `getStatus` answer from the HUB's slice (`hubMotionNativeProvider`); an
56564
+ * unreadable hub fails both — never `false` for unknown (D49, D393). */
56454
56565
  registerMotionCap() {
56455
- this.ctx.registerNativeCap(motionCapability, { isDetected: async ({ deviceId }) => {
56456
- if (deviceId !== this.id) throw new Error(`AmcrestCamera: motion deviceId mismatch, expected ${this.id}, got ${deviceId}`);
56457
- return readHubMotionDetected(this.ctx.api.deviceState, this.id);
56458
- } });
56566
+ const provider = hubMotionNativeProvider(this.ctx.api.deviceState, this.id, "AmcrestCamera");
56567
+ this.ctx.registerNativeCap(motionCapability, provider);
56459
56568
  this.ctx.logger.info("amcrest: motion cap registered", { tags: { deviceId: this.id } });
56460
56569
  }
56461
56570
  /** Reusable event-source identity for every emit on this device. */
@@ -56666,7 +56775,7 @@ var AmcrestCamera = class AmcrestCamera extends BaseDevice {
56666
56775
  key: "host",
56667
56776
  label: "Camera IP",
56668
56777
  required: true,
56669
- placeholder: "192.168.x.x"
56778
+ placeholder: "192.168.10.100"
56670
56779
  },
56671
56780
  {
56672
56781
  type: "number",
@@ -70888,13 +70997,13 @@ function enumerateHosts(cidr) {
70888
70997
  for (let d = 1; d <= 254; d += 1) out.push(`${a}.${b}.${c}.${d}`);
70889
70998
  return out;
70890
70999
  }
70891
- /** Best-effort local /24 from the first non-internal IPv4 interface (e.g. `192.168.x.x/24`). */
71000
+ /** Best-effort local /24 from the first non-internal IPv4 interface (e.g. `192.168.10.0/24`). */
70892
71001
  function detectLocalCidr() {
70893
71002
  for (const addrs of Object.values((0, node_os.networkInterfaces)())) for (const addr of addrs ?? []) if (addr.family === "IPv4" && !addr.internal) {
70894
71003
  const [a, b, c] = addr.address.split(".");
70895
71004
  return `${a}.${b}.${c}.0/24`;
70896
71005
  }
70897
- return "192.168.x.x/24";
71006
+ return "192.168.10.0/24";
70898
71007
  }
70899
71008
  /** Flatten a host into a flat row-key slug — must match `generateStableId`'s host fallback so a
70900
71009
  * re-scan of a host-added camera is detected as already onboarded. */
@@ -70963,7 +71072,7 @@ function buildCreationFormSchema() {
70963
71072
  key: "host",
70964
71073
  label: "Camera IP",
70965
71074
  required: true,
70966
- placeholder: "192.168.x.x",
71075
+ placeholder: "192.168.10.100",
70967
71076
  inputType: "text"
70968
71077
  },
70969
71078
  {
@@ -71014,14 +71123,14 @@ function buildDiscoveryParamsFormSchema() {
71014
71123
  }, {
71015
71124
  id: "scan",
71016
71125
  title: "Scan options",
71017
- description: "The credentialed CGI 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.x.x/24), otherwise the scan returns nothing.",
71126
+ description: "The credentialed CGI 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.",
71018
71127
  columns: 1,
71019
71128
  fields: [{
71020
71129
  type: "text",
71021
71130
  key: "networkCidr",
71022
71131
  label: "Network (CIDR) — required if cameras are on a different subnet than the hub",
71023
71132
  required: false,
71024
- placeholder: "192.168.x.x/24"
71133
+ placeholder: "192.168.60.0/24"
71025
71134
  }, {
71026
71135
  type: "number",
71027
71136
  key: "timeoutMs",