@camstack/types 1.2.248 → 1.2.250

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-9gM_dS-5.js");
2
+ const require_sleep = require("./sleep-CvwUEq16.js");
3
3
  const require_event_category = require("./event-category-BVfsrBYA.js");
4
4
  const require_canonical_hash = require("./canonical-hash-CSE4ioRi.js");
5
5
  const require_enums = require("./enums.js");
@@ -16654,6 +16654,7 @@ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
16654
16654
  sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
16655
16655
  sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
16656
16656
  sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
16657
+ sub("cover", "control", "control", "#0ea5e9", "door", "Cover");
16657
16658
  sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
16658
16659
  sub("siren", "control", "control", "#dc2626", "siren", "Siren");
16659
16660
  sub("button", "control", "control", "#10b981", "button", "Button");
@@ -16998,6 +16999,10 @@ var NcSystemEventKindSchema = zod.z.enum([
16998
16999
  "alarm-disarmed",
16999
17000
  "alarm-arming",
17000
17001
  "alarm-arm-refused",
17002
+ "alarm-pending",
17003
+ "alarm-rearmed",
17004
+ "alarm-sensor-bypass",
17005
+ "alarm-check-failed",
17001
17006
  "addon-updated",
17002
17007
  "server-updated",
17003
17008
  "export-completed",
@@ -17034,9 +17039,13 @@ var NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NcSystemEventKindSchema.options.filter((k
17034
17039
  */
17035
17040
  var NC_ALARM_SYSTEM_EVENT_KINDS = [
17036
17041
  "alarm-arm-refused",
17042
+ "alarm-check-failed",
17037
17043
  "alarm-arming",
17038
17044
  "alarm-armed",
17045
+ "alarm-pending",
17039
17046
  "alarm-triggered",
17047
+ "alarm-rearmed",
17048
+ "alarm-sensor-bypass",
17040
17049
  "alarm-disarmed"
17041
17050
  ];
17042
17051
  /**
@@ -17660,6 +17669,19 @@ var NcMediaFrameSchema = zod.z.enum([
17660
17669
  "full",
17661
17670
  "boxed"
17662
17671
  ]);
17672
+ /**
17673
+ * How a SENSOR notification shows the cameras that link the sensor.
17674
+ *
17675
+ * `mosaic` — one image composed of every linked camera's photograph, taken at
17676
+ * the trigger. `each` — one image per camera where the target takes several
17677
+ * attachments; a target that takes one keeps the first camera's (the degrade
17678
+ * engine drops the rest by order). Either way ONE notification per event.
17679
+ * Absent = `mosaic`, applied by the Notification Center and deliberately NOT
17680
+ * a Zod default (see NcRulePatchSchema: a default materialises on every
17681
+ * partial patch). Meaningless for a rule whose subject is not a sensor, and a
17682
+ * sensor linked to one camera ships that camera's plain snapshot either way.
17683
+ */
17684
+ var NcLinkedCameraModeSchema = zod.z.enum(["mosaic", "each"]);
17663
17685
  var NcMediaPolicySchema = zod.z.object({
17664
17686
  attach: zod.z.enum([
17665
17687
  "best",
@@ -17705,7 +17727,9 @@ var NcMediaPolicySchema = zod.z.object({
17705
17727
  * A profile that is not assigned falls back to the cheapest, and the render
17706
17728
  * reports which one actually ran.
17707
17729
  */
17708
- profile: require_sleep.CamProfileSchema.optional()
17730
+ profile: require_sleep.CamProfileSchema.optional(),
17731
+ /** See {@link NcLinkedCameraModeSchema}. */
17732
+ linkedCameras: NcLinkedCameraModeSchema.optional()
17709
17733
  });
17710
17734
  /**
17711
17735
  * Cooldown GRANULARITY over the subject's class — how much a fired
@@ -17826,7 +17850,7 @@ var NcRuleInputSchema = zod.z.object({
17826
17850
  cooldownSec: 60,
17827
17851
  scope: "rule-device"
17828
17852
  }),
17829
- /** `{{var}}` templating over camera/class/label/zones/confidence/time. */
17853
+ /** `{{var}}` title/body. The variables are declared in `NC_TEMPLATE_VARS` (@camstack/types) and depend on the rule kind — see `templateVarsFor`. */
17830
17854
  template: zod.z.object({
17831
17855
  title: zod.z.string().max(500).optional(),
17832
17856
  body: zod.z.string().max(2e3).optional()
@@ -18006,6 +18030,33 @@ var NcRulePatchSchema = NcRuleInputSchema.partial().extend({
18006
18030
  throttle: NcThrottleSchema.optional(),
18007
18031
  priority: zod.z.number().int().min(1).max(5).optional()
18008
18032
  });
18033
+ /**
18034
+ * Optional rule fields an editor may EMPTY on an update.
18035
+ *
18036
+ * `updateRule` merges `{ ...existing, ...patch }`, so an omitted key keeps its
18037
+ * stored value — which is what a partial patch must do, and exactly why a
18038
+ * field emptied in an editor used to survive the save. An editor names here
18039
+ * the keys it AUTHORS and found empty; the store removes them before the
18040
+ * merge. A `schedule` cannot be sent empty at all (`windows.min(1)`), so this
18041
+ * is the only way to remove one.
18042
+ */
18043
+ var NC_RULE_CLEARABLE_KEYS = [
18044
+ "template",
18045
+ "schedule",
18046
+ "targetUsers",
18047
+ "snoozeOptions",
18048
+ "snoozeAllowGlobal",
18049
+ "waitForEnhancement",
18050
+ "actions",
18051
+ "confirm",
18052
+ "groupIdleSec"
18053
+ ];
18054
+ var NcRuleClearableKeySchema = zod.z.enum(NC_RULE_CLEARABLE_KEYS);
18055
+ var NcRuleUpdateInputSchema = zod.z.object({
18056
+ ruleId: zod.z.string(),
18057
+ patch: NcRulePatchSchema,
18058
+ clear: zod.z.array(NcRuleClearableKeySchema).optional()
18059
+ });
18009
18060
  /** A persisted rule. */
18010
18061
  var NcRuleSchema = NcRuleInputSchema.extend({
18011
18062
  id: zod.z.string(),
@@ -18037,6 +18088,87 @@ var NcTestResultSchema = zod.z.object({
18037
18088
  className: zod.z.string().optional(),
18038
18089
  label: zod.z.string().optional()
18039
18090
  });
18091
+ /** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
18092
+ * is the same union; the template-vars spec pins the two together. */
18093
+ var NcRuleKindSchema = zod.z.enum([
18094
+ "detection",
18095
+ "sensor",
18096
+ "occupancy",
18097
+ "sound",
18098
+ "system"
18099
+ ]);
18100
+ /** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
18101
+ var NcTemplateFamilySchema = zod.z.enum([
18102
+ "rule",
18103
+ "timelapse",
18104
+ "summary"
18105
+ ]);
18106
+ /** Which text field of that producer. `previewText` is the timelapse frame
18107
+ * caption, `captionText` the digest mosaic caption — both see fewer vars. */
18108
+ var NcTemplateFieldSchema = zod.z.enum([
18109
+ "title",
18110
+ "body",
18111
+ "previewText",
18112
+ "captionText"
18113
+ ]);
18114
+ var NcTemplateVarGroupSchema = zod.z.enum([
18115
+ "subject",
18116
+ "place",
18117
+ "time",
18118
+ "rule",
18119
+ "occupancy",
18120
+ "sound",
18121
+ "sensor",
18122
+ "system",
18123
+ "digest",
18124
+ "ai"
18125
+ ]);
18126
+ /**
18127
+ * ONE `{{var}}` a notification template may name.
18128
+ *
18129
+ * `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
18130
+ * it has a value; absent = no restriction on that axis. `systemEventKinds` is
18131
+ * read only for a `system` rule. `dynamic` declares a FAMILY of names
18132
+ * (`count_<class>`): the descriptor's `name` is the example member.
18133
+ */
18134
+ var NcTemplateVarDescriptorSchema = zod.z.object({
18135
+ name: zod.z.string().regex(/^\w+$/),
18136
+ label: zod.z.string(),
18137
+ description: zod.z.string().optional(),
18138
+ example: zod.z.string(),
18139
+ group: NcTemplateVarGroupSchema,
18140
+ families: zod.z.array(NcTemplateFamilySchema).min(1),
18141
+ fields: zod.z.array(NcTemplateFieldSchema).optional(),
18142
+ kinds: zod.z.array(NcRuleKindSchema).optional(),
18143
+ deliveries: zod.z.array(NcDeliverySchema).optional(),
18144
+ systemEventKinds: zod.z.array(NcSystemEventKindSchema).optional(),
18145
+ dynamic: zod.z.object({
18146
+ prefix: zod.z.literal("count_"),
18147
+ from: zod.z.literal("classes")
18148
+ }).optional()
18149
+ });
18150
+ /** What an editor knows about the field being edited. */
18151
+ var NcTemplateVarContextSchema = zod.z.object({
18152
+ family: NcTemplateFamilySchema,
18153
+ field: NcTemplateFieldSchema,
18154
+ kind: NcRuleKindSchema.optional(),
18155
+ delivery: NcDeliverySchema.optional(),
18156
+ systemEventKinds: zod.z.array(NcSystemEventKindSchema).optional()
18157
+ });
18158
+ /** Input to `previewTemplate` — the editor's own context, plus the draft text. */
18159
+ var NcTemplatePreviewInputSchema = zod.z.object({
18160
+ context: NcTemplateVarContextSchema,
18161
+ template: zod.z.object({
18162
+ title: zod.z.string().max(500).optional(),
18163
+ body: zod.z.string().max(2e3).optional()
18164
+ })
18165
+ });
18166
+ var NcTemplatePreviewSchema = zod.z.object({
18167
+ title: zod.z.string().nullable(),
18168
+ body: zod.z.string().nullable(),
18169
+ /** Names the template uses that this context never fills — they render empty. */
18170
+ unknown: zod.z.array(zod.z.string())
18171
+ });
18040
18172
  var NcConditionDescriptorSchema = zod.z.object({
18041
18173
  /** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
18042
18174
  id: zod.z.string(),
@@ -18200,6 +18332,14 @@ var NC_CONDITION_CATALOG = [
18200
18332
  value: "backup-completed",
18201
18333
  label: "Backup completed"
18202
18334
  },
18335
+ {
18336
+ value: "alarm-arm-refused",
18337
+ label: "Alarm arm refused"
18338
+ },
18339
+ {
18340
+ value: "alarm-check-failed",
18341
+ label: "Alarm check failed (armed anyway)"
18342
+ },
18203
18343
  {
18204
18344
  value: "alarm-arming",
18205
18345
  label: "Alarm arming (exit delay)"
@@ -18208,10 +18348,22 @@ var NC_CONDITION_CATALOG = [
18208
18348
  value: "alarm-armed",
18209
18349
  label: "Alarm armed"
18210
18350
  },
18351
+ {
18352
+ value: "alarm-pending",
18353
+ label: "Alarm entry delay started"
18354
+ },
18211
18355
  {
18212
18356
  value: "alarm-triggered",
18213
18357
  label: "Alarm triggered"
18214
18358
  },
18359
+ {
18360
+ value: "alarm-rearmed",
18361
+ label: "Alarm re-armed after siren"
18362
+ },
18363
+ {
18364
+ value: "alarm-sensor-bypass",
18365
+ label: "Alarm sensor bypass"
18366
+ },
18215
18367
  {
18216
18368
  value: "alarm-disarmed",
18217
18369
  label: "Alarm disarmed"
@@ -18765,8 +18917,22 @@ var NcSnoozeSuppressedSchema = zod.z.object({
18765
18917
  lastAt: zod.z.number()
18766
18918
  });
18767
18919
  /**
18768
- * The three durations the panel's state machine runs on, plus who hears about
18769
- * an arm.
18920
+ * One arm mode's non-blocking list: devices that may be open when that mode
18921
+ * arms, without the arm being refused.
18922
+ */
18923
+ var NcAlarmNonBlockingSchema = zod.z.object({
18924
+ mode: AlarmArmModeSchema,
18925
+ deviceIds: zod.z.array(zod.z.number().int()).max(200)
18926
+ });
18927
+ /**
18928
+ * The per-mode lists, at most one per mode. Two lists for one mode have no
18929
+ * meaning a reader could agree on (union? the last one?), so they are refused
18930
+ * at the boundary rather than interpreted.
18931
+ */
18932
+ var NcAlarmNonBlockingListSchema = zod.z.array(NcAlarmNonBlockingSchema).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
18933
+ /**
18934
+ * The three durations the panel's state machine runs on, who hears about its
18935
+ * transitions, and which openings each mode tolerates.
18770
18936
  *
18771
18937
  * They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
18772
18938
  * `alarm-panel` is `deviceNative` and its other provider mirrors somebody
@@ -18785,21 +18951,39 @@ var NcAlarmSettingsSchema = zod.z.object({
18785
18951
  * existed, and therefore what an untouched install keeps doing.
18786
18952
  */
18787
18953
  triggeredDurationSec: zod.z.number().int().min(0).max(3600),
18788
- /** Send a notification when a mode takes effect. */
18789
- announceArm: zod.z.boolean(),
18790
18954
  /**
18791
- * Where that notification goes. Target ids from `notification-output`.
18955
+ * Deprecated by D630 — every transition is announced. Parsed, never read;
18956
+ * remove after one release. Optional so an older admin's patch (which still
18957
+ * sends it) parses rather than failing the whole save.
18958
+ */
18959
+ announceArm: zod.z.boolean().optional(),
18960
+ /**
18961
+ * Target ids from `notification-output`: every alarm transition goes here.
18792
18962
  *
18793
- * Explicit rather than "everyone": an arm announcement is a household
18963
+ * Explicit rather than "everyone": an alarm announcement is a household
18794
18964
  * message, and broadcasting it to every configured endpoint (including a
18795
18965
  * webhook wired to something else) is not a default anybody would choose.
18796
- * Empty with `announceArm: true` sends nothing, and the server logs that —
18797
- * silence must be attributable.
18966
+ * Empty sends nothing, and the server logs that — silence must be
18967
+ * attributable.
18968
+ */
18969
+ announceTargets: zod.z.array(zod.z.string().min(1)).max(16),
18970
+ /**
18971
+ * Per arm mode, the covered devices whose being OPEN does not refuse the
18972
+ * arm (a window left ajar for the cat under `home`, say). At most one entry
18973
+ * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
18974
+ * parses as "every opening blocks" — the behaviour it was written under.
18798
18975
  */
18799
- announceTargets: zod.z.array(zod.z.string().min(1)).max(16)
18976
+ nonBlocking: NcAlarmNonBlockingListSchema.default([])
18800
18977
  });
18801
- /** Every field optional — a tab edits one control at a time. */
18802
- var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.partial();
18978
+ /**
18979
+ * Every field optional — a tab edits one control at a time.
18980
+ *
18981
+ * `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
18982
+ * remove an inner `.default()`, so a patch that never named the field would
18983
+ * parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
18984
+ * (the same trap {@link NcRulePatchSchema} documents).
18985
+ */
18986
+ var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
18803
18987
  /**
18804
18988
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
18805
18989
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -18844,6 +19028,36 @@ var NcAlarmModeCoverageSchema = zod.z.object({
18844
19028
  */
18845
19029
  skippedDevices: zod.z.array(NcAlarmSkippedDeviceSchema).default([])
18846
19030
  });
19031
+ /** One covered device that reads open, and whether that refuses the arm. */
19032
+ var NcAlarmOpeningSchema = zod.z.object({
19033
+ deviceId: zod.z.number().int(),
19034
+ name: zod.z.string(),
19035
+ /** The device's state word as read (`open`, `unlocked`, …). */
19036
+ state: zod.z.string(),
19037
+ /** False when the mode's non-blocking list names this device. */
19038
+ blocking: zod.z.boolean()
19039
+ });
19040
+ /** A device the panel is currently ignoring, and which side of it it is on. */
19041
+ var NcAlarmExclusionViewSchema = zod.z.object({
19042
+ deviceId: zod.z.number().int(),
19043
+ name: zod.z.string(),
19044
+ phase: zod.z.enum(["open", "closed"])
19045
+ });
19046
+ /** One mode's openings: devices read open, plus those whose state is unknown. */
19047
+ var NcAlarmModeOpeningsSchema = zod.z.object({
19048
+ mode: AlarmArmModeSchema,
19049
+ devices: zod.z.array(NcAlarmOpeningSchema),
19050
+ /** Covered devices whose state could not be read (D49): neither open nor closed. */
19051
+ unknown: zod.z.array(zod.z.number().int())
19052
+ });
19053
+ var NcAlarmLiveSchema = zod.z.object({
19054
+ state: AlarmStateSchema,
19055
+ /** The mode being armed into (exit delay) or held; null when disarmed. */
19056
+ targetMode: AlarmArmModeSchema.nullable(),
19057
+ availableModes: zod.z.array(AlarmArmModeSchema),
19058
+ openings: zod.z.array(NcAlarmModeOpeningsSchema),
19059
+ exclusions: zod.z.array(NcAlarmExclusionViewSchema)
19060
+ });
18847
19061
  var NcAlarmConfigSchema = zod.z.object({
18848
19062
  /**
18849
19063
  * The panel's device id, or null when this install has no panel (the ensure
@@ -18853,7 +19067,13 @@ var NcAlarmConfigSchema = zod.z.object({
18853
19067
  */
18854
19068
  deviceId: zod.z.number().int().nullable(),
18855
19069
  settings: NcAlarmSettingsSchema,
18856
- coverage: zod.z.array(NcAlarmModeCoverageSchema)
19070
+ coverage: zod.z.array(NcAlarmModeCoverageSchema),
19071
+ /**
19072
+ * The panel as it is NOW — state, what each mode would find open, what is
19073
+ * excluded. Optional: a panel-less hub answers without it, and a client
19074
+ * must not read its absence as "nothing is open".
19075
+ */
19076
+ live: NcAlarmLiveSchema.optional()
18857
19077
  });
18858
19078
  /**
18859
19079
  * The most seconds of footage any notification rule may ask a camera to retain.
@@ -18927,10 +19147,7 @@ var notificationRulesCapability = {
18927
19147
  auth: "admin",
18928
19148
  caller: "required"
18929
19149
  }),
18930
- updateRule: require_sleep.method(zod.z.object({
18931
- ruleId: zod.z.string(),
18932
- patch: NcRulePatchSchema
18933
- }), zod.z.object({ rule: NcRuleSchema }), {
19150
+ updateRule: require_sleep.method(NcRuleUpdateInputSchema, zod.z.object({ rule: NcRuleSchema }), {
18934
19151
  kind: "mutation",
18935
19152
  auth: "admin",
18936
19153
  caller: "required"
@@ -19018,6 +19235,18 @@ var notificationRulesCapability = {
19018
19235
  catalog: zod.z.array(NcConditionDescriptorSchema),
19019
19236
  taxonomy: NcTaxonomySchema.optional()
19020
19237
  })),
19238
+ /** The declared `{{var}}` vocabulary; editors filter it with `templateVarsFor`. */
19239
+ getTemplateCatalog: require_sleep.method(zod.z.object({}), zod.z.object({ vars: zod.z.array(NcTemplateVarDescriptorSchema) })),
19240
+ /**
19241
+ * Render a template with example values, through the hub's text catalog.
19242
+ * Pure: sends nothing. A MUTATION only for transport — a 2.5k-char draft in
19243
+ * a batched GET URL risks length limits — so access stays `view`, the same
19244
+ * as `getTemplateCatalog`.
19245
+ */
19246
+ previewTemplate: require_sleep.method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
19247
+ kind: "mutation",
19248
+ access: "view"
19249
+ }),
19021
19250
  /**
19022
19251
  * Queryable delivery history — a read-only view over the durable outbox
19023
19252
  * (fired rule, subject summary, target, status, timestamps, error on a
@@ -19072,7 +19301,10 @@ var notificationRulesCapability = {
19072
19301
  caller: "required"
19073
19302
  }),
19074
19303
  /**
19075
- * The alarm panel's durations, plus what each mode ACTUALLY arms.
19304
+ * The alarm panel's settings, what each mode ACTUALLY arms, and — when a
19305
+ * panel exists — its `live` state: current state and target mode, what
19306
+ * each mode would find open (blocking or not, and what is unknown), and
19307
+ * the devices currently excluded.
19076
19308
  *
19077
19309
  * Coverage is returned by the same call as the settings on purpose: they
19078
19310
  * are read together or not at all. An editor that showed the delays
@@ -19082,7 +19314,9 @@ var notificationRulesCapability = {
19082
19314
  */
19083
19315
  getAlarmConfig: require_sleep.method(zod.z.object({}), NcAlarmConfigSchema, { auth: "admin" }),
19084
19316
  /**
19085
- * Patch the durations. Returns the WHOLE config, coverage included, so a
19317
+ * Patch the settings (durations, recipients, per-mode non-blocking lists;
19318
+ * `announceArm` is accepted and ignored — D630). Returns the WHOLE config,
19319
+ * coverage and `live` included, so a
19086
19320
  * client never has to guess what the server settled on — the panel clamps
19087
19321
  * and normalises, and a form that re-rendered from its own input would
19088
19322
  * show a value the alarm is not using.
@@ -25315,6 +25549,7 @@ var CAP_TO_KIND = {
25315
25549
  "enum-sensor": "enum-sensor",
25316
25550
  "event-emitter": "device-event",
25317
25551
  "lock-control": "lock",
25552
+ cover: "cover",
25318
25553
  switch: "switch",
25319
25554
  button: "button",
25320
25555
  doorbell: "doorbell"
@@ -42463,9 +42698,8 @@ var DEVICE_STATE_READERS = [
42463
42698
  field: "state"
42464
42699
  },
42465
42700
  {
42466
- cap: "lock",
42467
- field: "locked",
42468
- booleanWords: ["locked", "unlocked"]
42701
+ cap: "lock-control",
42702
+ field: "state"
42469
42703
  },
42470
42704
  {
42471
42705
  cap: "contact",
@@ -52167,6 +52401,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
52167
52401
  addonId: null,
52168
52402
  access: "view"
52169
52403
  },
52404
+ "notificationRules.getTemplateCatalog": {
52405
+ capName: "notification-rules",
52406
+ capScope: "system",
52407
+ addonId: null,
52408
+ access: "view"
52409
+ },
52170
52410
  "notificationRules.listDeviceMutes": {
52171
52411
  capName: "notification-rules",
52172
52412
  capScope: "system",
@@ -52185,6 +52425,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
52185
52425
  addonId: null,
52186
52426
  access: "view"
52187
52427
  },
52428
+ "notificationRules.previewTemplate": {
52429
+ capName: "notification-rules",
52430
+ capScope: "system",
52431
+ addonId: null,
52432
+ access: "view"
52433
+ },
52188
52434
  "notificationRules.resolveArtifactUrl": {
52189
52435
  capName: "notification-rules",
52190
52436
  capScope: "system",
@@ -60416,6 +60662,768 @@ function isScheduleActive(schedule, atMs) {
60416
60662
  return schedule.invert === true ? !inside : inside;
60417
60663
  }
60418
60664
  //#endregion
60665
+ //#region src/notification/template-vars.ts
60666
+ var RULE_BODY_ONLY = ["body"];
60667
+ var RULE_TEXT = ["title", "body"];
60668
+ var BATTERY = ["device-battery-low", "device-battery-normal"];
60669
+ var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
60670
+ var UPDATES = [
60671
+ "addon-update-available",
60672
+ "server-update-available",
60673
+ "wrapper-update-available"
60674
+ ];
60675
+ var PACKAGE_UPDATES = ["addon-update-available", "server-update-available"];
60676
+ var ALARM = [
60677
+ "alarm-triggered",
60678
+ "alarm-armed",
60679
+ "alarm-disarmed",
60680
+ "alarm-arming",
60681
+ "alarm-arm-refused",
60682
+ "alarm-pending",
60683
+ "alarm-rearmed",
60684
+ "alarm-sensor-bypass",
60685
+ "alarm-check-failed"
60686
+ ];
60687
+ var COMBINED_ALARM = ["alarm-triggered"];
60688
+ var DEVICE_EVENTS = [
60689
+ "device-online",
60690
+ "device-offline",
60691
+ "device-disabled",
60692
+ "device-enabled",
60693
+ ...BATTERY,
60694
+ ...CONSUMABLE,
60695
+ "stream-online",
60696
+ "stream-offline",
60697
+ "detection-blind",
60698
+ ...ALARM
60699
+ ];
60700
+ var NC_TEMPLATE_VARS = [
60701
+ {
60702
+ name: "camera",
60703
+ label: "Camera",
60704
+ example: "Ingresso",
60705
+ group: "subject",
60706
+ families: ["rule", "timelapse"],
60707
+ systemEventKinds: [...DEVICE_EVENTS, "export-completed"]
60708
+ },
60709
+ {
60710
+ name: "class",
60711
+ label: "Detected class",
60712
+ description: "The counted class on an occupancy row (e.g. \"person\"); the detected class elsewhere.",
60713
+ example: "person",
60714
+ group: "subject",
60715
+ families: ["rule"],
60716
+ fields: [...RULE_TEXT],
60717
+ kinds: [
60718
+ "detection",
60719
+ "sensor",
60720
+ "sound",
60721
+ "occupancy",
60722
+ "system"
60723
+ ],
60724
+ systemEventKinds: [...COMBINED_ALARM]
60725
+ },
60726
+ {
60727
+ name: "label",
60728
+ label: "Name or plate",
60729
+ description: "Recognised person or read plate; empty otherwise.",
60730
+ example: "Mario",
60731
+ group: "subject",
60732
+ families: ["rule"],
60733
+ fields: [...RULE_TEXT],
60734
+ kinds: ["detection", "system"],
60735
+ systemEventKinds: [...COMBINED_ALARM]
60736
+ },
60737
+ {
60738
+ name: "subject",
60739
+ label: "Subject sentence",
60740
+ description: "Composed in the notification language, e.g. \"person (Mario)\".",
60741
+ example: "persona (Mario)",
60742
+ group: "subject",
60743
+ families: ["rule"],
60744
+ fields: [...RULE_TEXT],
60745
+ kinds: [
60746
+ "detection",
60747
+ "sensor",
60748
+ "sound",
60749
+ "occupancy"
60750
+ ]
60751
+ },
60752
+ {
60753
+ name: "confidence",
60754
+ label: "Confidence",
60755
+ example: "87%",
60756
+ group: "subject",
60757
+ families: ["rule"],
60758
+ fields: [...RULE_TEXT],
60759
+ kinds: ["detection", "system"],
60760
+ systemEventKinds: [...COMBINED_ALARM]
60761
+ },
60762
+ {
60763
+ name: "count",
60764
+ label: "Count",
60765
+ description: "Occupancy count, subjects in a grouped notification, or the event count of a system notice.",
60766
+ example: "3",
60767
+ group: "subject",
60768
+ families: ["rule"],
60769
+ fields: [...RULE_TEXT],
60770
+ kinds: [
60771
+ "detection",
60772
+ "occupancy",
60773
+ "system"
60774
+ ],
60775
+ systemEventKinds: [
60776
+ "addon-crash-loop",
60777
+ ...PACKAGE_UPDATES,
60778
+ "addon-updated"
60779
+ ]
60780
+ },
60781
+ {
60782
+ name: "detectionSummary",
60783
+ label: "Detection summary",
60784
+ description: "Class composition, e.g. \"2 persons and 1 car\".",
60785
+ example: "2 persone e 1 auto",
60786
+ group: "subject",
60787
+ families: [
60788
+ "rule",
60789
+ "timelapse",
60790
+ "summary"
60791
+ ],
60792
+ fields: [...RULE_TEXT],
60793
+ kinds: ["detection"]
60794
+ },
60795
+ {
60796
+ name: "zones",
60797
+ label: "Zones",
60798
+ example: "Cancello, Vialetto",
60799
+ group: "place",
60800
+ families: ["rule"],
60801
+ fields: [...RULE_TEXT],
60802
+ kinds: ["detection", "system"],
60803
+ systemEventKinds: [...COMBINED_ALARM]
60804
+ },
60805
+ {
60806
+ name: "zone",
60807
+ label: "Zone",
60808
+ example: "Cancello",
60809
+ group: "place",
60810
+ families: ["rule"],
60811
+ fields: [...RULE_TEXT],
60812
+ kinds: [
60813
+ "detection",
60814
+ "occupancy",
60815
+ "system"
60816
+ ],
60817
+ systemEventKinds: [...COMBINED_ALARM]
60818
+ },
60819
+ {
60820
+ name: "inZones",
60821
+ label: "\"in <zones>\" fragment",
60822
+ description: "Leading space included; empty when no zone.",
60823
+ example: " in Cancello",
60824
+ group: "place",
60825
+ families: ["rule"],
60826
+ fields: [...RULE_TEXT],
60827
+ kinds: ["detection", "system"],
60828
+ systemEventKinds: [...COMBINED_ALARM]
60829
+ },
60830
+ {
60831
+ name: "scope",
60832
+ label: "Occupancy scope",
60833
+ example: "Cancello",
60834
+ group: "place",
60835
+ families: ["rule"],
60836
+ fields: [...RULE_TEXT],
60837
+ kinds: ["occupancy"]
60838
+ },
60839
+ {
60840
+ name: "time",
60841
+ label: "Time",
60842
+ description: "Hours and minutes, in the notification language.",
60843
+ example: "20:05",
60844
+ group: "time",
60845
+ families: [
60846
+ "rule",
60847
+ "timelapse",
60848
+ "summary"
60849
+ ],
60850
+ fields: [...RULE_TEXT]
60851
+ },
60852
+ {
60853
+ name: "rule",
60854
+ label: "Rule name",
60855
+ example: "Ingresso notte",
60856
+ group: "rule",
60857
+ families: [
60858
+ "rule",
60859
+ "timelapse",
60860
+ "summary"
60861
+ ]
60862
+ },
60863
+ {
60864
+ name: "from",
60865
+ label: "Window start",
60866
+ example: "07:00",
60867
+ group: "time",
60868
+ families: ["timelapse", "summary"]
60869
+ },
60870
+ {
60871
+ name: "to",
60872
+ label: "Window end",
60873
+ example: "08:00",
60874
+ group: "time",
60875
+ families: ["timelapse", "summary"]
60876
+ },
60877
+ {
60878
+ name: "capacity",
60879
+ label: "Capacity",
60880
+ example: "2",
60881
+ group: "occupancy",
60882
+ families: ["rule"],
60883
+ fields: [...RULE_TEXT],
60884
+ kinds: ["occupancy"]
60885
+ },
60886
+ {
60887
+ name: "op",
60888
+ label: "Occupied / free",
60889
+ example: "occupata",
60890
+ group: "occupancy",
60891
+ families: ["rule"],
60892
+ fields: [...RULE_TEXT],
60893
+ kinds: ["occupancy"]
60894
+ },
60895
+ {
60896
+ name: "sounds",
60897
+ label: "Sounds heard",
60898
+ example: "abbaio, clacson",
60899
+ group: "sound",
60900
+ families: ["rule"],
60901
+ fields: [...RULE_TEXT],
60902
+ kinds: ["sound"]
60903
+ },
60904
+ {
60905
+ name: "hitPercent",
60906
+ label: "Share of the window",
60907
+ description: "Level mode only: percentage of samples above the threshold.",
60908
+ example: "42",
60909
+ group: "sound",
60910
+ families: ["rule"],
60911
+ fields: [...RULE_TEXT],
60912
+ kinds: ["sound"]
60913
+ },
60914
+ {
60915
+ name: "db",
60916
+ label: "Peak level (dBFS)",
60917
+ example: "-18",
60918
+ group: "sound",
60919
+ families: ["rule"],
60920
+ fields: [...RULE_TEXT],
60921
+ kinds: ["sound"]
60922
+ },
60923
+ {
60924
+ name: "eventType",
60925
+ label: "Event type",
60926
+ description: "Raw device event token, e.g. \"press\" or \"open\"; empty when the device sends none.",
60927
+ example: "press",
60928
+ group: "sensor",
60929
+ families: ["rule"],
60930
+ fields: [...RULE_TEXT],
60931
+ kinds: ["sensor"]
60932
+ },
60933
+ {
60934
+ name: "ai",
60935
+ label: "AI sentence",
60936
+ description: "Where the AI description goes; appended on its own line when absent.",
60937
+ example: "Una persona lascia un pacco.",
60938
+ group: "ai",
60939
+ families: ["rule", "summary"],
60940
+ fields: [...RULE_BODY_ONLY],
60941
+ kinds: ["detection", "occupancy"]
60942
+ },
60943
+ {
60944
+ name: "eventKind",
60945
+ label: "Event kind",
60946
+ example: "device-offline",
60947
+ group: "system",
60948
+ families: ["rule"],
60949
+ fields: [...RULE_TEXT],
60950
+ kinds: ["system"]
60951
+ },
60952
+ {
60953
+ name: "deviceName",
60954
+ label: "Device",
60955
+ example: "Ingresso",
60956
+ group: "system",
60957
+ families: ["rule"],
60958
+ fields: [...RULE_TEXT],
60959
+ kinds: ["system"],
60960
+ systemEventKinds: [...DEVICE_EVENTS]
60961
+ },
60962
+ {
60963
+ name: "deviceId",
60964
+ label: "Device id",
60965
+ example: "617",
60966
+ group: "system",
60967
+ families: ["rule"],
60968
+ fields: [...RULE_TEXT],
60969
+ kinds: ["system"],
60970
+ systemEventKinds: [...DEVICE_EVENTS, "export-completed"]
60971
+ },
60972
+ {
60973
+ name: "percentage",
60974
+ label: "Percentage",
60975
+ example: "15",
60976
+ group: "system",
60977
+ families: ["rule"],
60978
+ fields: [...RULE_TEXT],
60979
+ kinds: ["system"],
60980
+ systemEventKinds: [...BATTERY, ...CONSUMABLE]
60981
+ },
60982
+ {
60983
+ name: "item",
60984
+ label: "Consumable",
60985
+ example: "Filtro",
60986
+ group: "system",
60987
+ families: ["rule"],
60988
+ fields: [...RULE_TEXT],
60989
+ kinds: ["system"],
60990
+ systemEventKinds: [...CONSUMABLE]
60991
+ },
60992
+ {
60993
+ name: "profile",
60994
+ label: "Stream profile",
60995
+ example: "high",
60996
+ group: "system",
60997
+ families: ["rule"],
60998
+ fields: [...RULE_TEXT],
60999
+ kinds: ["system"],
61000
+ systemEventKinds: ["stream-online", "stream-offline"]
61001
+ },
61002
+ {
61003
+ name: "node",
61004
+ label: "Node",
61005
+ example: "hub",
61006
+ group: "system",
61007
+ families: ["rule"],
61008
+ fields: [...RULE_TEXT],
61009
+ kinds: ["system"],
61010
+ systemEventKinds: [
61011
+ "node-online",
61012
+ "node-offline",
61013
+ "node-inference-unavailable",
61014
+ "addon-crash-loop",
61015
+ ...UPDATES,
61016
+ "addon-updated",
61017
+ "server-updated"
61018
+ ]
61019
+ },
61020
+ {
61021
+ name: "nodeId",
61022
+ label: "Node id",
61023
+ example: "hub",
61024
+ group: "system",
61025
+ families: ["rule"],
61026
+ fields: [...RULE_TEXT],
61027
+ kinds: ["system"],
61028
+ systemEventKinds: [
61029
+ "node-online",
61030
+ "node-offline",
61031
+ "node-inference-unavailable",
61032
+ "addon-crash-loop",
61033
+ ...UPDATES,
61034
+ "server-updated"
61035
+ ]
61036
+ },
61037
+ {
61038
+ name: "devices",
61039
+ label: "Devices",
61040
+ description: "node-inference-unavailable: the affected cameras, comma-joined. Alarm arm / refusal: HOW MANY devices are armed (or open).",
61041
+ example: "Ingresso, Garage",
61042
+ group: "system",
61043
+ families: ["rule"],
61044
+ fields: [...RULE_TEXT],
61045
+ kinds: ["system"],
61046
+ systemEventKinds: [
61047
+ "node-inference-unavailable",
61048
+ "alarm-armed",
61049
+ "alarm-arm-refused"
61050
+ ]
61051
+ },
61052
+ {
61053
+ name: "addons",
61054
+ label: "Addons",
61055
+ example: "stream-broker",
61056
+ group: "system",
61057
+ families: ["rule"],
61058
+ fields: [...RULE_TEXT],
61059
+ kinds: ["system"],
61060
+ systemEventKinds: ["addon-crash-loop"]
61061
+ },
61062
+ {
61063
+ name: "minutes",
61064
+ label: "Minutes",
61065
+ example: "12",
61066
+ group: "system",
61067
+ families: ["rule"],
61068
+ fields: [...RULE_TEXT],
61069
+ kinds: ["system"],
61070
+ systemEventKinds: ["detection-blind"]
61071
+ },
61072
+ {
61073
+ name: "package",
61074
+ label: "Package",
61075
+ example: "@camstack/addon-recorder",
61076
+ group: "system",
61077
+ families: ["rule"],
61078
+ fields: [...RULE_TEXT],
61079
+ kinds: ["system"],
61080
+ systemEventKinds: [
61081
+ ...UPDATES,
61082
+ "addon-updated",
61083
+ "server-updated"
61084
+ ]
61085
+ },
61086
+ {
61087
+ name: "packageName",
61088
+ label: "Package",
61089
+ example: "@camstack/addon-recorder",
61090
+ group: "system",
61091
+ families: ["rule"],
61092
+ fields: [...RULE_TEXT],
61093
+ kinds: ["system"],
61094
+ systemEventKinds: [...PACKAGE_UPDATES, "server-updated"]
61095
+ },
61096
+ {
61097
+ name: "nodes",
61098
+ label: "Nodes",
61099
+ description: "Comma-joined node ids, when an update spans more than one — the single-node case names it through {{nodeId}} instead. `incomingFromUpdateAvailable` (event-intake.ts) fills `nodes` from ONE shared textParams object for both addon- and server-update-available, exactly like packageName/currentVersion/latestVersion/count — only the shipped TEXT (system.server-update-available.body.onNodes) reads it today, but the var is real on both kinds.",
61100
+ example: "hub, little-unraid",
61101
+ group: "system",
61102
+ families: ["rule"],
61103
+ fields: [...RULE_TEXT],
61104
+ kinds: ["system"],
61105
+ systemEventKinds: [...PACKAGE_UPDATES]
61106
+ },
61107
+ {
61108
+ name: "currentVersion",
61109
+ label: "Current version",
61110
+ example: "1.2.377",
61111
+ group: "system",
61112
+ families: ["rule"],
61113
+ fields: [...RULE_TEXT],
61114
+ kinds: ["system"],
61115
+ systemEventKinds: [...UPDATES]
61116
+ },
61117
+ {
61118
+ name: "latestVersion",
61119
+ label: "Latest version",
61120
+ example: "1.2.378",
61121
+ group: "system",
61122
+ families: ["rule"],
61123
+ fields: [...RULE_TEXT],
61124
+ kinds: ["system"],
61125
+ systemEventKinds: [...UPDATES]
61126
+ },
61127
+ {
61128
+ name: "list",
61129
+ label: "Updated packages",
61130
+ description: "One line per package. `incomingFromUpdateAvailable` fills it for addon-/server-update-available too (the SAME shared textParams object as `nodes`) — `system.addon-update-available.body` reads it; `incomingFromAddonUpdates` fills it again, independently, for the completed `addon-updated` event.",
61131
+ example: "recorder, notifiers",
61132
+ group: "system",
61133
+ families: ["rule"],
61134
+ fields: [...RULE_TEXT],
61135
+ kinds: ["system"],
61136
+ systemEventKinds: [...PACKAGE_UPDATES, "addon-updated"]
61137
+ },
61138
+ {
61139
+ name: "fromVersion",
61140
+ label: "From version",
61141
+ example: "1.2.377",
61142
+ group: "system",
61143
+ families: ["rule"],
61144
+ fields: [...RULE_TEXT],
61145
+ kinds: ["system"],
61146
+ systemEventKinds: ["server-updated"]
61147
+ },
61148
+ {
61149
+ name: "toVersion",
61150
+ label: "To version",
61151
+ example: "1.2.378",
61152
+ group: "system",
61153
+ families: ["rule"],
61154
+ fields: [...RULE_TEXT],
61155
+ kinds: ["system"],
61156
+ systemEventKinds: ["server-updated"]
61157
+ },
61158
+ {
61159
+ name: "destinationsOk",
61160
+ label: "Destinations ok",
61161
+ example: "2",
61162
+ group: "system",
61163
+ families: ["rule"],
61164
+ fields: [...RULE_TEXT],
61165
+ kinds: ["system"],
61166
+ systemEventKinds: ["backup-completed"]
61167
+ },
61168
+ {
61169
+ name: "destinationsTotal",
61170
+ label: "Destinations",
61171
+ example: "2",
61172
+ group: "system",
61173
+ families: ["rule"],
61174
+ fields: [...RULE_TEXT],
61175
+ kinds: ["system"],
61176
+ systemEventKinds: ["backup-completed"]
61177
+ },
61178
+ {
61179
+ name: "destinationsFailed",
61180
+ label: "Destinations failed",
61181
+ example: "0",
61182
+ group: "system",
61183
+ families: ["rule"],
61184
+ fields: [...RULE_TEXT],
61185
+ kinds: ["system"],
61186
+ systemEventKinds: ["backup-completed"]
61187
+ },
61188
+ {
61189
+ name: "sizeMB",
61190
+ label: "Size (MB)",
61191
+ example: "412",
61192
+ group: "system",
61193
+ families: ["rule"],
61194
+ fields: [...RULE_TEXT],
61195
+ kinds: ["system"],
61196
+ systemEventKinds: ["backup-completed"]
61197
+ },
61198
+ {
61199
+ name: "backupId",
61200
+ label: "Backup id",
61201
+ example: "bk-20260924",
61202
+ group: "system",
61203
+ families: ["rule"],
61204
+ fields: [...RULE_TEXT],
61205
+ kinds: ["system"],
61206
+ systemEventKinds: ["backup-completed"]
61207
+ },
61208
+ {
61209
+ name: "title",
61210
+ label: "Export title",
61211
+ example: "Ingresso 08:00",
61212
+ group: "system",
61213
+ families: ["rule"],
61214
+ fields: [...RULE_TEXT],
61215
+ kinds: ["system"],
61216
+ systemEventKinds: ["export-completed"]
61217
+ },
61218
+ {
61219
+ name: "exportId",
61220
+ label: "Export id",
61221
+ example: "exp-20260924",
61222
+ group: "system",
61223
+ families: ["rule"],
61224
+ fields: [...RULE_TEXT],
61225
+ kinds: ["system"],
61226
+ systemEventKinds: ["export-completed"]
61227
+ },
61228
+ {
61229
+ name: "bytes",
61230
+ label: "Export size (bytes)",
61231
+ example: "10485760",
61232
+ group: "system",
61233
+ families: ["rule"],
61234
+ fields: [...RULE_TEXT],
61235
+ kinds: ["system"],
61236
+ systemEventKinds: ["export-completed"]
61237
+ },
61238
+ {
61239
+ name: "mode",
61240
+ label: "Alarm mode",
61241
+ example: "away",
61242
+ group: "system",
61243
+ families: ["rule"],
61244
+ fields: [...RULE_TEXT],
61245
+ kinds: ["system"],
61246
+ systemEventKinds: [
61247
+ "alarm-armed",
61248
+ "alarm-arming",
61249
+ "alarm-arm-refused",
61250
+ "alarm-pending",
61251
+ "alarm-rearmed",
61252
+ "alarm-check-failed"
61253
+ ]
61254
+ },
61255
+ {
61256
+ name: "seconds",
61257
+ label: "Seconds",
61258
+ description: "Arming: the exit delay left. Pending: the entry delay left to disarm.",
61259
+ example: "30",
61260
+ group: "system",
61261
+ families: ["rule"],
61262
+ fields: [...RULE_TEXT],
61263
+ kinds: ["system"],
61264
+ systemEventKinds: ["alarm-arming", "alarm-pending"]
61265
+ },
61266
+ {
61267
+ name: "rules",
61268
+ label: "Covering rules",
61269
+ description: "How many enabled rules the armed mode gates.",
61270
+ example: "2",
61271
+ group: "system",
61272
+ families: ["rule"],
61273
+ fields: [...RULE_TEXT],
61274
+ kinds: ["system"],
61275
+ systemEventKinds: ["alarm-armed"]
61276
+ },
61277
+ {
61278
+ name: "names",
61279
+ label: "Device names",
61280
+ description: "Armed: the devices armed. Arming: the devices this mode cannot arm. Refusal: the open sensors.",
61281
+ example: "Ingresso, Garage",
61282
+ group: "system",
61283
+ families: ["rule"],
61284
+ fields: [...RULE_TEXT],
61285
+ kinds: ["system"],
61286
+ systemEventKinds: [
61287
+ "alarm-armed",
61288
+ "alarm-arming",
61289
+ "alarm-arm-refused"
61290
+ ]
61291
+ },
61292
+ {
61293
+ name: "skippedNames",
61294
+ label: "Skipped devices",
61295
+ description: "Devices the mode names but cannot arm (muted, detection off, offline).",
61296
+ example: "Garage",
61297
+ group: "system",
61298
+ families: ["rule"],
61299
+ fields: [...RULE_TEXT],
61300
+ kinds: ["system"],
61301
+ systemEventKinds: ["alarm-armed"]
61302
+ },
61303
+ {
61304
+ name: "bypassedNames",
61305
+ label: "Bypassed sensors",
61306
+ description: "Sensors the operator armed around because they were open.",
61307
+ example: "Finestra veranda",
61308
+ group: "system",
61309
+ families: ["rule"],
61310
+ fields: [...RULE_TEXT],
61311
+ kinds: ["system"],
61312
+ systemEventKinds: ["alarm-armed", "alarm-arming"]
61313
+ },
61314
+ {
61315
+ name: "coverage",
61316
+ label: "Coverage %",
61317
+ example: "96",
61318
+ group: "digest",
61319
+ families: ["timelapse"],
61320
+ fields: [...RULE_TEXT]
61321
+ },
61322
+ {
61323
+ name: "events",
61324
+ label: "Events",
61325
+ example: "4",
61326
+ group: "digest",
61327
+ families: ["timelapse", "summary"],
61328
+ fields: [...RULE_TEXT]
61329
+ },
61330
+ {
61331
+ name: "detections",
61332
+ label: "Detections",
61333
+ example: "7",
61334
+ group: "digest",
61335
+ families: ["timelapse", "summary"],
61336
+ fields: [...RULE_TEXT]
61337
+ },
61338
+ {
61339
+ name: "count_person",
61340
+ label: "Count of one class",
61341
+ description: "count_<class>, e.g. count_person, count_car.",
61342
+ example: "3",
61343
+ group: "digest",
61344
+ families: ["timelapse", "summary"],
61345
+ fields: [...RULE_TEXT],
61346
+ dynamic: {
61347
+ prefix: "count_",
61348
+ from: "classes"
61349
+ }
61350
+ },
61351
+ {
61352
+ name: "cameras",
61353
+ label: "Cameras",
61354
+ example: "3",
61355
+ group: "digest",
61356
+ families: ["summary"],
61357
+ fields: [...RULE_TEXT]
61358
+ },
61359
+ {
61360
+ name: "matched",
61361
+ label: "Matched events",
61362
+ example: "12",
61363
+ group: "digest",
61364
+ families: ["summary"],
61365
+ fields: [...RULE_TEXT]
61366
+ },
61367
+ {
61368
+ name: "shown",
61369
+ label: "Events shown",
61370
+ example: "9",
61371
+ group: "digest",
61372
+ families: ["summary"],
61373
+ fields: [...RULE_TEXT]
61374
+ },
61375
+ {
61376
+ name: "total",
61377
+ label: "Events total",
61378
+ example: "12",
61379
+ group: "digest",
61380
+ families: ["summary"],
61381
+ fields: [...RULE_TEXT]
61382
+ }
61383
+ ];
61384
+ /** The caption fields see a fixed, smaller vocabulary (`timelapsePreviewText`,
61385
+ * `summaryCaptionText`). Declared here rather than as `fields` on every
61386
+ * descriptor so the whole rule stays in one line of code. */
61387
+ var CAPTION_VARS = {
61388
+ previewText: [
61389
+ "camera",
61390
+ "rule",
61391
+ "from",
61392
+ "to"
61393
+ ],
61394
+ captionText: [
61395
+ "rule",
61396
+ "from",
61397
+ "to"
61398
+ ]
61399
+ };
61400
+ function onAxis(declared, asked) {
61401
+ return declared === void 0 || asked === void 0 || declared.includes(asked);
61402
+ }
61403
+ function onSystemAxis(d, ctx) {
61404
+ if (ctx.kind !== "system" || d.systemEventKinds === void 0) return true;
61405
+ const asked = ctx.systemEventKinds ?? [];
61406
+ return asked.length === 0 || asked.some((k) => d.systemEventKinds?.includes(k) === true);
61407
+ }
61408
+ /**
61409
+ * The variables that have a value in `ctx`, in catalog order. Pure.
61410
+ *
61411
+ * `ctx` is trusted, typed input — untrusted input (a served/RPC boundary) is
61412
+ * validated once against {@link NcTemplateVarContextSchema} at the cap/bridge
61413
+ * boundary, not re-parsed on every call here (Task 4).
61414
+ */
61415
+ function templateVarsFor(ctx) {
61416
+ if (ctx.field === "previewText" || ctx.field === "captionText") {
61417
+ const allowed = CAPTION_VARS[ctx.field];
61418
+ return NC_TEMPLATE_VARS.filter((d) => d.families.includes(ctx.family) && allowed.includes(d.name) && d.dynamic === void 0);
61419
+ }
61420
+ return NC_TEMPLATE_VARS.filter((d) => d.families.includes(ctx.family) && onAxis(d.fields, ctx.field) && onAxis(d.kinds, ctx.kind) && onAxis(d.deliveries, ctx.delivery) && onSystemAxis(d, ctx));
61421
+ }
61422
+ /** True when `name` is supplied in `ctx`, dynamic families (`count_<class>`) included. */
61423
+ function isKnownTemplateVar(name, ctx) {
61424
+ return templateVarsFor(ctx).some((d) => d.dynamic !== void 0 ? name.startsWith(d.dynamic.prefix) && name.length > d.dynamic.prefix.length : d.name === name);
61425
+ }
61426
+ //#endregion
60419
61427
  //#region src/notification/system-event-filters.ts
60420
61428
  /** Every filter, for the callers that must cover all four (guards, tests). */
60421
61429
  var NC_SYSTEM_EVENT_FILTER_KEYS = [
@@ -64022,6 +65030,7 @@ exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
64022
65030
  exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
64023
65031
  exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
64024
65032
  exports.NC_OCCUPANCY_DEFAULTS = NC_OCCUPANCY_DEFAULTS;
65033
+ exports.NC_RULE_CLEARABLE_KEYS = NC_RULE_CLEARABLE_KEYS;
64025
65034
  exports.NC_RULE_EDITOR_SECTION_ORDER = NC_RULE_EDITOR_SECTION_ORDER;
64026
65035
  exports.NC_RULE_KIND_SPECS = NC_RULE_KIND_SPECS;
64027
65036
  exports.NC_RULE_SECTIONS = NC_RULE_SECTIONS;
@@ -64029,6 +65038,7 @@ exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
64029
65038
  exports.NC_SYSTEM_DELIVERY = NC_SYSTEM_DELIVERY;
64030
65039
  exports.NC_SYSTEM_EVENT_FILTER_KEYS = NC_SYSTEM_EVENT_FILTER_KEYS;
64031
65040
  exports.NC_TAXONOMY = NC_TAXONOMY;
65041
+ exports.NC_TEMPLATE_VARS = NC_TEMPLATE_VARS;
64032
65042
  exports.NETWORK_LINK_TYPES = NETWORK_LINK_TYPES;
64033
65043
  exports.NETWORK_LINK_UNKNOWN = NETWORK_LINK_UNKNOWN;
64034
65044
  exports.NativeCropBboxSchema = NativeCropBboxSchema;
@@ -64074,6 +65084,7 @@ exports.NcHistoryFilterSchema = NcHistoryFilterSchema;
64074
65084
  exports.NcHistoryRecordKindSchema = NcHistoryRecordKindSchema;
64075
65085
  exports.NcHistoryStatusSchema = NcHistoryStatusSchema;
64076
65086
  exports.NcHistorySubjectSchema = NcHistorySubjectSchema;
65087
+ exports.NcLinkedCameraModeSchema = NcLinkedCameraModeSchema;
64077
65088
  exports.NcMediaFrameSchema = NcMediaFrameSchema;
64078
65089
  exports.NcMediaPolicySchema = NcMediaPolicySchema;
64079
65090
  exports.NcOccupancyConditionSchema = NcOccupancyConditionSchema;
@@ -64081,11 +65092,14 @@ exports.NcPlateMatcherSchema = NcPlateMatcherSchema;
64081
65092
  exports.NcRuleActionSchema = NcRuleActionSchema;
64082
65093
  exports.NcRuleActionSequenceSchema = NcRuleActionSequenceSchema;
64083
65094
  exports.NcRuleActionsSchema = NcRuleActionsSchema;
65095
+ exports.NcRuleClearableKeySchema = NcRuleClearableKeySchema;
64084
65096
  exports.NcRuleInputSchema = NcRuleInputSchema;
65097
+ exports.NcRuleKindSchema = NcRuleKindSchema;
64085
65098
  exports.NcRuleNotificationButtonSchema = NcRuleNotificationButtonSchema;
64086
65099
  exports.NcRulePatchSchema = NcRulePatchSchema;
64087
65100
  exports.NcRuleSchema = NcRuleSchema;
64088
65101
  exports.NcRuleTargetSchema = NcRuleTargetSchema;
65102
+ exports.NcRuleUpdateInputSchema = NcRuleUpdateInputSchema;
64089
65103
  exports.NcSceneConditionSchema = NcSceneConditionSchema;
64090
65104
  exports.NcScheduleSchema = NcScheduleSchema;
64091
65105
  exports.NcScheduleWindowSchema = NcScheduleWindowSchema;
@@ -64097,6 +65111,13 @@ exports.NcSystemEventConditionSchema = NcSystemEventConditionSchema;
64097
65111
  exports.NcSystemEventKindSchema = NcSystemEventKindSchema;
64098
65112
  exports.NcTaxonomyEntrySchema = NcTaxonomyEntrySchema;
64099
65113
  exports.NcTaxonomySchema = NcTaxonomySchema;
65114
+ exports.NcTemplateFamilySchema = NcTemplateFamilySchema;
65115
+ exports.NcTemplateFieldSchema = NcTemplateFieldSchema;
65116
+ exports.NcTemplatePreviewInputSchema = NcTemplatePreviewInputSchema;
65117
+ exports.NcTemplatePreviewSchema = NcTemplatePreviewSchema;
65118
+ exports.NcTemplateVarContextSchema = NcTemplateVarContextSchema;
65119
+ exports.NcTemplateVarDescriptorSchema = NcTemplateVarDescriptorSchema;
65120
+ exports.NcTemplateVarGroupSchema = NcTemplateVarGroupSchema;
64100
65121
  exports.NcTestResultSchema = NcTestResultSchema;
64101
65122
  exports.NcThrottleGranularitySchema = NcThrottleGranularitySchema;
64102
65123
  exports.NcThrottleSchema = NcThrottleSchema;
@@ -64797,6 +65818,7 @@ exports.isEvent = require_sleep.isEvent;
64797
65818
  exports.isEventOwnerType = isEventOwnerType;
64798
65819
  exports.isFirstLevelMacroClass = isFirstLevelMacroClass;
64799
65820
  exports.isIsolatedBuiltin = isIsolatedBuiltin;
65821
+ exports.isKnownTemplateVar = isKnownTemplateVar;
64800
65822
  exports.isLocationEnabled = isLocationEnabled;
64801
65823
  exports.isMediaOwnerType = isMediaOwnerType;
64802
65824
  exports.isNode = isNode;
@@ -65036,6 +66058,7 @@ exports.taskLogEntrySchema = taskLogEntrySchema;
65036
66058
  exports.taskPhaseSchema = taskPhaseSchema;
65037
66059
  exports.taskTargetSchema = taskTargetSchema;
65038
66060
  exports.temperatureSensorCapability = temperatureSensorCapability;
66061
+ exports.templateVarsFor = templateVarsFor;
65039
66062
  exports.terminalSessionCapability = terminalSessionCapability;
65040
66063
  exports.textToHtml = textToHtml;
65041
66064
  exports.timeOfDayToMinutes = timeOfDayToMinutes;