camstack 1.2.24 → 1.2.25

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.
@@ -21541,6 +21541,7 @@ var NotificationActionIconSchema = external_exports.enum([
21541
21541
  "acknowledge",
21542
21542
  "dismiss",
21543
21543
  "silence",
21544
+ "snooze",
21544
21545
  "view",
21545
21546
  "play",
21546
21547
  "open",
@@ -21548,9 +21549,13 @@ var NotificationActionIconSchema = external_exports.enum([
21548
21549
  "lock",
21549
21550
  "unlock",
21550
21551
  "arm",
21552
+ "arm-home",
21553
+ "arm-away",
21554
+ "arm-night",
21551
21555
  "disarm",
21552
21556
  "light",
21553
- "alert"
21557
+ "alert",
21558
+ "camera"
21554
21559
  ]);
21555
21560
  var NotificationActionSchema = external_exports.object({
21556
21561
  id: external_exports.string(),
@@ -21739,6 +21744,25 @@ var notificationOutputCapability = {
21739
21744
  }), external_exports.void(), { kind: "mutation" })
21740
21745
  }
21741
21746
  };
21747
+ var MACRO_LABELS = [
21748
+ {
21749
+ id: "person",
21750
+ name: "Person"
21751
+ },
21752
+ {
21753
+ id: "vehicle",
21754
+ name: "Vehicle"
21755
+ },
21756
+ {
21757
+ id: "animal",
21758
+ name: "Animal"
21759
+ },
21760
+ {
21761
+ id: "package",
21762
+ name: "Package"
21763
+ }
21764
+ ];
21765
+ var DETECTION_MACRO_CLASSES = new Set(MACRO_LABELS.map((l) => l.id));
21742
21766
  var COCO_TO_MACRO = {
21743
21767
  mapping: {
21744
21768
  person: "person",
@@ -22437,6 +22461,8 @@ var NcSystemEventKindSchema = external_exports.enum([
22437
22461
  "alarm-triggered",
22438
22462
  "alarm-armed",
22439
22463
  "alarm-disarmed",
22464
+ "alarm-arming",
22465
+ "alarm-arm-refused",
22440
22466
  "camera-online",
22441
22467
  "camera-offline",
22442
22468
  "camera-disabled",
@@ -25728,15 +25754,21 @@ var RunnerCameraConfigSchema = external_exports.object({
25728
25754
  */
25729
25755
  onboardMotionDrivesAnalyzer: external_exports.boolean().default(true),
25730
25756
  /**
25731
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
25732
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec`
25733
- * this is off by default because the recheck re-subscribes a detection session
25734
- * every N seconds while `watching`, a major source of pull-decoder re-dial
25735
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
25757
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
25758
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
25736
25759
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
25737
25760
  * (and only render) when this is enabled.
25738
- */
25739
- occupancyRecheckEnabled: external_exports.boolean().default(false),
25761
+ *
25762
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
25763
+ * recheck re-subscribes a detection session every N seconds while `watching`
25764
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
25765
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
25766
+ * object is counted only while the stationary registry holds it, and the
25767
+ * registry rebuilds from motion, so after a restart a parked car was invisible
25768
+ * to every occupancy rule until something moved in front of it. The churn is
25769
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
25770
+ */
25771
+ occupancyRecheckEnabled: external_exports.boolean().default(true),
25740
25772
  occupancyRecheckSec: external_exports.number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
25741
25773
  occupancyRecheckFrames: external_exports.number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
25742
25774
  /**
@@ -25854,8 +25886,8 @@ var RunnerCameraDeviceUIFields = [
25854
25886
  type: "boolean",
25855
25887
  style: "checkbox",
25856
25888
  label: "Occupancy re-check",
25857
- description: "Periodically re-sample a few frames during the watching phase to confirm the scene is truly empty (catches stationary objects motion-gating would miss). Off by default \u2014 it adds decoder re-dial churn.",
25858
- default: false
25889
+ description: "Periodically re-sample a few frames during the watching phase, so objects that are simply parked are still counted (motion gating alone loses them after a restart). On by default at a wide interval; turn it off on cameras where the extra decoder re-dial is expensive.",
25890
+ default: true
25859
25891
  },
25860
25892
  {
25861
25893
  key: "occupancyRecheckSec",
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runDiscover
4
- } from "./chunk-XWIQP3BN.js";
4
+ } from "./chunk-H274DYSA.js";
5
5
  import "./chunk-LMMQX4CK.js";
6
6
 
7
7
  // src/cli.ts
@@ -38,7 +38,7 @@ async function runServe(args) {
38
38
  ...typeof values.data === "string" ? { data: values.data } : {}
39
39
  };
40
40
  Object.assign(process.env, buildServeEnv(opts));
41
- await import("./launcher-CZIVQSEW.js");
41
+ await import("./launcher-B477E3FR.js");
42
42
  }
43
43
 
44
44
  // src/commands/agent.ts
@@ -83,7 +83,7 @@ async function runAgent(args) {
83
83
  ...typeof values.port === "string" ? { port: values.port } : {}
84
84
  };
85
85
  Object.assign(process.env, buildAgentEnv(opts));
86
- await import("./launcher-CZIVQSEW.js");
86
+ await import("./launcher-B477E3FR.js");
87
87
  }
88
88
 
89
89
  // src/commands/setup.ts
@@ -1130,7 +1130,7 @@ function isUnknown(_value) {
1130
1130
  return true;
1131
1131
  }
1132
1132
  async function resolveServerInteractive(presetNamespace) {
1133
- const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-UVUWK7BL.js");
1133
+ const { discoverNodes, resolveHubFromDiscovered, filterHubNodes, DEFAULT_HUB_HTTPS_PORT } = await import("./discover-FSOATSVM.js");
1134
1134
  if (presetNamespace) {
1135
1135
  const spinner4 = clack.spinner();
1136
1136
  spinner4.start(`Discovering hub on LAN (namespace "${presetNamespace}")`);
@@ -5,7 +5,7 @@ import {
5
5
  filterHubNodes,
6
6
  resolveHubFromDiscovered,
7
7
  runDiscover
8
- } from "./chunk-XWIQP3BN.js";
8
+ } from "./chunk-H274DYSA.js";
9
9
  import "./chunk-LMMQX4CK.js";
10
10
  export {
11
11
  DEFAULT_HUB_HTTPS_PORT,
@@ -23631,9 +23631,9 @@ var require_zod = __commonJS({
23631
23631
  }
23632
23632
  });
23633
23633
 
23634
- // ../system/dist/dist-D4nuaPdd.js
23635
- var require_dist_D4nuaPdd = __commonJS({
23636
- "../system/dist/dist-D4nuaPdd.js"(exports) {
23634
+ // ../system/dist/dist-B4WyhfjH.js
23635
+ var require_dist_B4WyhfjH = __commonJS({
23636
+ "../system/dist/dist-B4WyhfjH.js"(exports) {
23637
23637
  "use strict";
23638
23638
  var zod = require_zod();
23639
23639
  var EventCategory = /* @__PURE__ */ (function(EventCategory2) {
@@ -31975,6 +31975,7 @@ var require_dist_D4nuaPdd = __commonJS({
31975
31975
  "acknowledge",
31976
31976
  "dismiss",
31977
31977
  "silence",
31978
+ "snooze",
31978
31979
  "view",
31979
31980
  "play",
31980
31981
  "open",
@@ -31982,9 +31983,13 @@ var require_dist_D4nuaPdd = __commonJS({
31982
31983
  "lock",
31983
31984
  "unlock",
31984
31985
  "arm",
31986
+ "arm-home",
31987
+ "arm-away",
31988
+ "arm-night",
31985
31989
  "disarm",
31986
31990
  "light",
31987
- "alert"
31991
+ "alert",
31992
+ "camera"
31988
31993
  ]);
31989
31994
  var NotificationActionSchema = zod.z.object({
31990
31995
  id: zod.z.string(),
@@ -32173,6 +32178,24 @@ var require_dist_D4nuaPdd = __commonJS({
32173
32178
  }), zod.z.void(), { kind: "mutation" })
32174
32179
  }
32175
32180
  };
32181
+ new Set([
32182
+ {
32183
+ id: "person",
32184
+ name: "Person"
32185
+ },
32186
+ {
32187
+ id: "vehicle",
32188
+ name: "Vehicle"
32189
+ },
32190
+ {
32191
+ id: "animal",
32192
+ name: "Animal"
32193
+ },
32194
+ {
32195
+ id: "package",
32196
+ name: "Package"
32197
+ }
32198
+ ].map((l) => l.id));
32176
32199
  var COCO_TO_MACRO = {
32177
32200
  mapping: {
32178
32201
  person: "person",
@@ -32871,6 +32894,8 @@ var require_dist_D4nuaPdd = __commonJS({
32871
32894
  "alarm-triggered",
32872
32895
  "alarm-armed",
32873
32896
  "alarm-disarmed",
32897
+ "alarm-arming",
32898
+ "alarm-arm-refused",
32874
32899
  "camera-online",
32875
32900
  "camera-offline",
32876
32901
  "camera-disabled",
@@ -36162,15 +36187,21 @@ var require_dist_D4nuaPdd = __commonJS({
36162
36187
  */
36163
36188
  onboardMotionDrivesAnalyzer: zod.z.boolean().default(true),
36164
36189
  /**
36165
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
36166
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec`
36167
- * this is off by default because the recheck re-subscribes a detection session
36168
- * every N seconds while `watching`, a major source of pull-decoder re-dial
36169
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
36190
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
36191
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
36170
36192
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
36171
36193
  * (and only render) when this is enabled.
36172
- */
36173
- occupancyRecheckEnabled: zod.z.boolean().default(false),
36194
+ *
36195
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
36196
+ * recheck re-subscribes a detection session every N seconds while `watching`
36197
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
36198
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
36199
+ * object is counted only while the stationary registry holds it, and the
36200
+ * registry rebuilds from motion, so after a restart a parked car was invisible
36201
+ * to every occupancy rule until something moved in front of it. The churn is
36202
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
36203
+ */
36204
+ occupancyRecheckEnabled: zod.z.boolean().default(true),
36174
36205
  occupancyRecheckSec: zod.z.number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
36175
36206
  occupancyRecheckFrames: zod.z.number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
36176
36207
  /**
@@ -53379,7 +53410,7 @@ var require_alerts_addon = __commonJS({
53379
53410
  [Symbol.toStringTag]: { value: "Module" }
53380
53411
  });
53381
53412
  require_chunk_Cek0wNdY();
53382
- var require_dist10 = require_dist_D4nuaPdd();
53413
+ var require_dist10 = require_dist_B4WyhfjH();
53383
53414
  function selectExpired(alerts, cutoffMs) {
53384
53415
  return alerts.filter((a) => a.createdAt < cutoffMs).sort((a, b) => a.createdAt - b.createdAt).map((a) => a.id);
53385
53416
  }
@@ -54192,7 +54223,7 @@ var require_console_logging = __commonJS({
54192
54223
  [Symbol.toStringTag]: { value: "Module" }
54193
54224
  });
54194
54225
  require_chunk_Cek0wNdY();
54195
- var require_dist10 = require_dist_D4nuaPdd();
54226
+ var require_dist10 = require_dist_B4WyhfjH();
54196
54227
  var require_formatter = require_formatter_DqAKDlvN();
54197
54228
  var LEVEL_RANK = {
54198
54229
  debug: 0,
@@ -54286,7 +54317,7 @@ var require_core_blocks_addon = __commonJS({
54286
54317
  "use strict";
54287
54318
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
54288
54319
  var require_chunk = require_chunk_Cek0wNdY();
54289
- var require_dist10 = require_dist_D4nuaPdd();
54320
+ var require_dist10 = require_dist_B4WyhfjH();
54290
54321
  var node_crypto = __require("crypto");
54291
54322
  var node_fs_promises = __require("fs/promises");
54292
54323
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -56984,7 +57015,7 @@ var require_device_manager_addon = __commonJS({
56984
57015
  [Symbol.toStringTag]: { value: "Module" }
56985
57016
  });
56986
57017
  require_chunk_Cek0wNdY();
56987
- var require_dist10 = require_dist_D4nuaPdd();
57018
+ var require_dist10 = require_dist_B4WyhfjH();
56988
57019
  var node_crypto = __require("crypto");
56989
57020
  var _camstack_types_node = require_node();
56990
57021
  var JOB_HISTORY = 20;
@@ -60972,7 +61003,7 @@ var require_hub_forwarder = __commonJS({
60972
61003
  [Symbol.toStringTag]: { value: "Module" }
60973
61004
  });
60974
61005
  require_chunk_Cek0wNdY();
60975
- var require_dist10 = require_dist_D4nuaPdd();
61006
+ var require_dist10 = require_dist_B4WyhfjH();
60976
61007
  var require_formatter = require_formatter_DqAKDlvN();
60977
61008
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
60978
61009
  var HubForwarderDestination = class {
@@ -61109,7 +61140,7 @@ var require_liveness_monitor_addon = __commonJS({
61109
61140
  "use strict";
61110
61141
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
61111
61142
  require_chunk_Cek0wNdY();
61112
- var require_dist10 = require_dist_D4nuaPdd();
61143
+ var require_dist10 = require_dist_B4WyhfjH();
61113
61144
  var NO_DEVICES = "liveness:no-devices";
61114
61145
  var ALL_OFFLINE = "liveness:all-devices-offline";
61115
61146
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -61299,7 +61330,7 @@ var require_local_auth_addon = __commonJS({
61299
61330
  [Symbol.toStringTag]: { value: "Module" }
61300
61331
  });
61301
61332
  var require_chunk = require_chunk_Cek0wNdY();
61302
- var require_dist10 = require_dist_D4nuaPdd();
61333
+ var require_dist10 = require_dist_B4WyhfjH();
61303
61334
  var node_crypto = __require("crypto");
61304
61335
  node_crypto = require_chunk.__toESM(node_crypto);
61305
61336
  var crypto$1 = __require("crypto");
@@ -68983,7 +69014,7 @@ var require_loki_logging = __commonJS({
68983
69014
  [Symbol.toStringTag]: { value: "Module" }
68984
69015
  });
68985
69016
  require_chunk_Cek0wNdY();
68986
- var require_dist10 = require_dist_D4nuaPdd();
69017
+ var require_dist10 = require_dist_B4WyhfjH();
68987
69018
  function sanitizeLabelName(raw) {
68988
69019
  const replaced = raw.replaceAll(/[^a-zA-Z0-9_]/g, "_");
68989
69020
  return /^[a-zA-Z_]/.test(replaced) ? replaced : `_${replaced}`;
@@ -69548,7 +69579,7 @@ var require_native_metrics_addon = __commonJS({
69548
69579
  [Symbol.toStringTag]: { value: "Module" }
69549
69580
  });
69550
69581
  var require_chunk = require_chunk_Cek0wNdY();
69551
- var require_dist10 = require_dist_D4nuaPdd();
69582
+ var require_dist10 = require_dist_B4WyhfjH();
69552
69583
  var node_child_process = __require("child_process");
69553
69584
  var node_util = __require("util");
69554
69585
  var node_os = __require("os");
@@ -70490,7 +70521,7 @@ var require_filesystem_storage_addon = __commonJS({
70490
70521
  [Symbol.toStringTag]: { value: "Module" }
70491
70522
  });
70492
70523
  var require_chunk = require_chunk_Cek0wNdY();
70493
- var require_dist10 = require_dist_D4nuaPdd();
70524
+ var require_dist10 = require_dist_B4WyhfjH();
70494
70525
  var node_crypto = __require("crypto");
70495
70526
  var node_fs_promises = __require("fs/promises");
70496
70527
  var node_path = __require("path");
@@ -71606,7 +71637,7 @@ var require_sqlite_settings_addon = __commonJS({
71606
71637
  [Symbol.toStringTag]: { value: "Module" }
71607
71638
  });
71608
71639
  var require_chunk = require_chunk_Cek0wNdY();
71609
- var require_dist10 = require_dist_D4nuaPdd();
71640
+ var require_dist10 = require_dist_B4WyhfjH();
71610
71641
  var node_crypto = __require("crypto");
71611
71642
  var node_fs = __require("fs");
71612
71643
  var node_module = __require("module");
@@ -73157,7 +73188,7 @@ var require_storage_orchestrator_addon = __commonJS({
73157
73188
  [Symbol.toStringTag]: { value: "Module" }
73158
73189
  });
73159
73190
  var require_chunk = require_chunk_Cek0wNdY();
73160
- var require_dist10 = require_dist_D4nuaPdd();
73191
+ var require_dist10 = require_dist_B4WyhfjH();
73161
73192
  var node_crypto = __require("crypto");
73162
73193
  var node_fs_promises = __require("fs/promises");
73163
73194
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -74998,7 +75029,7 @@ var require_system_config_addon = __commonJS({
74998
75029
  [Symbol.toStringTag]: { value: "Module" }
74999
75030
  });
75000
75031
  require_chunk_Cek0wNdY();
75001
- var require_dist10 = require_dist_D4nuaPdd();
75032
+ var require_dist10 = require_dist_B4WyhfjH();
75002
75033
  var SECTION_TITLES = {
75003
75034
  server: "Server",
75004
75035
  auth: "Authentication"
@@ -93059,7 +93090,7 @@ var require_winston_logging = __commonJS({
93059
93090
  [Symbol.toStringTag]: { value: "Module" }
93060
93091
  });
93061
93092
  var require_chunk = require_chunk_Cek0wNdY();
93062
- var require_dist10 = require_dist_D4nuaPdd();
93093
+ var require_dist10 = require_dist_B4WyhfjH();
93063
93094
  var require_formatter = require_formatter_DqAKDlvN();
93064
93095
  var node_path = __require("path");
93065
93096
  node_path = require_chunk.__toESM(node_path);
@@ -97783,7 +97814,7 @@ var require_addon = __commonJS({
97783
97814
  "pipelineEnabled": true,
97784
97815
  "zones": [],
97785
97816
  "onboardMotionDrivesAnalyzer": true,
97786
- "occupancyRecheckEnabled": false,
97817
+ "occupancyRecheckEnabled": true,
97787
97818
  "occupancyRecheckSec": 30,
97788
97819
  "occupancyRecheckFrames": 4,
97789
97820
  "frameSource": { "kind": "local-broker" }
@@ -115079,7 +115110,7 @@ var require_dist3 = __commonJS({
115079
115110
  "use strict";
115080
115111
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
115081
115112
  var require_chunk = require_chunk_Cek0wNdY();
115082
- var require_dist10 = require_dist_D4nuaPdd();
115113
+ var require_dist10 = require_dist_B4WyhfjH();
115083
115114
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
115084
115115
  require_alerts();
115085
115116
  var require_formatter = require_formatter_DqAKDlvN();
@@ -242499,6 +242530,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
242499
242530
  "acknowledge",
242500
242531
  "dismiss",
242501
242532
  "silence",
242533
+ "snooze",
242502
242534
  "view",
242503
242535
  "play",
242504
242536
  "open",
@@ -242506,9 +242538,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
242506
242538
  "lock",
242507
242539
  "unlock",
242508
242540
  "arm",
242541
+ "arm-home",
242542
+ "arm-away",
242543
+ "arm-night",
242509
242544
  "disarm",
242510
242545
  "light",
242511
- "alert"
242546
+ "alert",
242547
+ "camera"
242512
242548
  ]);
242513
242549
  var NotificationActionSchema = zod.z.object({
242514
242550
  id: zod.z.string(),
@@ -243037,6 +243073,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
243037
243073
  name: "Package"
243038
243074
  }
243039
243075
  ];
243076
+ var DETECTION_MACRO_CLASSES = new Set(MACRO_LABELS.map((l) => l.id));
243077
+ function isDetectionMacroClass(className) {
243078
+ return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
243079
+ }
243040
243080
  var COCO_TO_MACRO = {
243041
243081
  mapping: {
243042
243082
  person: "person",
@@ -243753,6 +243793,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
243753
243793
  "alarm-triggered",
243754
243794
  "alarm-armed",
243755
243795
  "alarm-disarmed",
243796
+ "alarm-arming",
243797
+ "alarm-arm-refused",
243756
243798
  "camera-online",
243757
243799
  "camera-offline",
243758
243800
  "camera-disabled",
@@ -243766,8 +243808,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
243766
243808
  ]);
243767
243809
  var NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
243768
243810
  var NC_ALARM_SYSTEM_EVENT_KINDS = [
243769
- "alarm-triggered",
243811
+ "alarm-arm-refused",
243812
+ "alarm-arming",
243770
243813
  "alarm-armed",
243814
+ "alarm-triggered",
243771
243815
  "alarm-disarmed"
243772
243816
  ];
243773
243817
  var NcSystemEventConditionSchema = zod.z.object({
@@ -244449,13 +244493,17 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
244449
244493
  label: "Server update available"
244450
244494
  },
244451
244495
  {
244452
- value: "alarm-triggered",
244453
- label: "Alarm triggered"
244496
+ value: "alarm-arming",
244497
+ label: "Alarm arming (exit delay)"
244454
244498
  },
244455
244499
  {
244456
244500
  value: "alarm-armed",
244457
244501
  label: "Alarm armed"
244458
244502
  },
244503
+ {
244504
+ value: "alarm-triggered",
244505
+ label: "Alarm triggered"
244506
+ },
244459
244507
  {
244460
244508
  value: "alarm-disarmed",
244461
244509
  label: "Alarm disarmed"
@@ -247455,15 +247503,21 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
247455
247503
  */
247456
247504
  onboardMotionDrivesAnalyzer: zod.z.boolean().default(true),
247457
247505
  /**
247458
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
247459
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec`
247460
- * this is off by default because the recheck re-subscribes a detection session
247461
- * every N seconds while `watching`, a major source of pull-decoder re-dial
247462
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
247506
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
247507
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
247463
247508
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
247464
247509
  * (and only render) when this is enabled.
247465
- */
247466
- occupancyRecheckEnabled: zod.z.boolean().default(false),
247510
+ *
247511
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
247512
+ * recheck re-subscribes a detection session every N seconds while `watching`
247513
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
247514
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
247515
+ * object is counted only while the stationary registry holds it, and the
247516
+ * registry rebuilds from motion, so after a restart a parked car was invisible
247517
+ * to every occupancy rule until something moved in front of it. The churn is
247518
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
247519
+ */
247520
+ occupancyRecheckEnabled: zod.z.boolean().default(true),
247467
247521
  occupancyRecheckSec: zod.z.number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
247468
247522
  occupancyRecheckFrames: zod.z.number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
247469
247523
  /**
@@ -247581,8 +247635,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
247581
247635
  type: "boolean",
247582
247636
  style: "checkbox",
247583
247637
  label: "Occupancy re-check",
247584
- description: "Periodically re-sample a few frames during the watching phase to confirm the scene is truly empty (catches stationary objects motion-gating would miss). Off by default \u2014 it adds decoder re-dial churn.",
247585
- default: false
247638
+ description: "Periodically re-sample a few frames during the watching phase, so objects that are simply parked are still counted (motion gating alone loses them after a restart). On by default at a wide interval; turn it off on cameras where the extra decoder re-dial is expensive.",
247639
+ default: true
247586
247640
  },
247587
247641
  {
247588
247642
  key: "occupancyRecheckSec",
@@ -259777,11 +259831,40 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
259777
259831
  } catch {
259778
259832
  }
259779
259833
  }
259834
+ /**
259835
+ * ONE device's bindings, not the fleet's.
259836
+ *
259837
+ * This runs on every `capability.binding-changed` AND on every
259838
+ * `device.registered`, per mirror. It used to answer that with
259839
+ * `getAllBindings` and `.find()` the one row — measured on the live
259840
+ * hub (524 devices): 903 ms and 373 KB per call, against 21 ms and
259841
+ * 613 bytes for `getBindings({deviceId})`.
259842
+ *
259843
+ * Adopting 20 Home Assistant devices registers 166 devices (parents +
259844
+ * accessories), so an operator with the admin UI open bought 166
259845
+ * full-fleet binding sweeps: ~150 s of hub main-thread time inside a
259846
+ * 219 s adoption, with every other RPC — including the adoption's own —
259847
+ * queued behind them. Same defect, same fix, as `refreshDeviceMetadata`
259848
+ * below (`listAll` → `getDevice`).
259849
+ *
259850
+ * Removal is the destructive direction (every proxy handle a consumer
259851
+ * holds goes inert), and an EMPTY binding list is not a claim that the
259852
+ * device is gone — a device can legitimately bind nothing. So it needs
259853
+ * a second read to agree (D49); `device.unregistered` remains the
259854
+ * primary removal path.
259855
+ */
259780
259856
  async refreshBinding(deviceId) {
259781
259857
  try {
259782
- const fresh = (await this.api.deviceManager.getAllBindings.query({})).find((b) => b.deviceId === deviceId);
259783
- if (fresh) this.bindings.set(deviceId, fresh);
259784
- else this.applyDeviceRemoval(deviceId);
259858
+ const fresh = await this.api.deviceManager.getBindings.query({ deviceId });
259859
+ if (fresh.entries.length > 0) {
259860
+ this.bindings.set(deviceId, fresh);
259861
+ return;
259862
+ }
259863
+ if (await this.api.deviceManager.getDevice.query({ deviceId }) === null) {
259864
+ this.applyDeviceRemoval(deviceId);
259865
+ return;
259866
+ }
259867
+ this.bindings.set(deviceId, fresh);
259785
259868
  } catch {
259786
259869
  }
259787
259870
  }
@@ -267785,6 +267868,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
267785
267868
  }
267786
267869
  var CLASS_CONDITION_IDS = /* @__PURE__ */ new Set(["classes", "classesExclude"]);
267787
267870
  var LABEL_CONDITION_IDS = /* @__PURE__ */ new Set(["sensorKinds"]);
267871
+ var COUNT_CLASS_CONDITION_IDS = /* @__PURE__ */ new Set(["occupancy"]);
267788
267872
  function toOption(entry) {
267789
267873
  return {
267790
267874
  value: entry.kind,
@@ -267851,6 +267935,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
267851
267935
  });
267852
267936
  return groups.length > 0 ? { groups } : null;
267853
267937
  }
267938
+ if (COUNT_CLASS_CONDITION_IDS.has(conditionId)) {
267939
+ const options = taxonomy.videoClasses.filter((e) => e.parentKind === null).map(toOption);
267940
+ return options.length > 0 ? { groups: [{
267941
+ key: "macro",
267942
+ fallbackLabel: "Classes",
267943
+ options
267944
+ }] } : null;
267945
+ }
267854
267946
  if (LABEL_CONDITION_IDS.has(conditionId)) {
267855
267947
  const groups = groupLabels(taxonomy.labels);
267856
267948
  return groups.length > 0 ? { groups } : null;
@@ -269642,6 +269734,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
269642
269734
  exports.DETAIL_CROP_PADDING_KEY = DETAIL_CROP_PADDING_KEY;
269643
269735
  exports.DETAIL_CROP_SECTION_ID = DETAIL_CROP_SECTION_ID;
269644
269736
  exports.DETAIL_CROP_SQUARE_KEY = DETAIL_CROP_SQUARE_KEY;
269737
+ exports.DETECTION_MACRO_CLASSES = DETECTION_MACRO_CLASSES;
269645
269738
  exports.DETECTION_PIPELINE_CAP_NAME = DETECTION_PIPELINE_CAP_NAME;
269646
269739
  exports.DEVICE_BACKEND_TO_FORMAT = DEVICE_BACKEND_TO_FORMAT;
269647
269740
  exports.DEVICE_CAP_NAMES = DEVICE_CAP_NAMES;
@@ -270467,6 +270560,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
270467
270560
  exports.isBaseConditionKey = isBaseConditionKey;
270468
270561
  exports.isCollectionArrayMethod = isCollectionArrayMethod;
270469
270562
  exports.isDeployableToAgent = isDeployableToAgent;
270563
+ exports.isDetectionMacroClass = isDetectionMacroClass;
270470
270564
  exports.isDeviceConfigCap = require_sleep.isDeviceConfigCap;
270471
270565
  exports.isDeviceScopedCap = require_sleep.isDeviceScopedCap;
270472
270566
  exports.isEvent = require_sleep.isEvent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "1.2.24",
3
+ "version": "1.2.25",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",