@camstack/addon-post-analysis 1.2.319 → 1.2.321

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 (15) hide show
  1. package/dist/{clip-model-registry-BjYpZuuI.js → clip-model-registry-BCx3qCL5.js} +341 -45
  2. package/dist/{clip-model-registry-DSqobJfx.mjs → clip-model-registry-Dnm9dfy0.mjs} +312 -46
  3. package/dist/embedding-encoder/index.js +1 -1
  4. package/dist/embedding-encoder/index.mjs +1 -1
  5. package/dist/pipeline-analytics/_stub.js +2 -2
  6. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CKHzDA_B.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CPcZN3mA.mjs} +2 -2
  7. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CGUnHrR_.mjs +26 -0
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CVH5tT7I.mjs +26 -0
  9. package/dist/pipeline-analytics/{hostInit-DDDjJDA6.mjs → hostInit-Dt-jaGuG.mjs} +2 -2
  10. package/dist/pipeline-analytics/index.js +934 -113
  11. package/dist/pipeline-analytics/index.mjs +934 -113
  12. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  13. package/package.json +1 -1
  14. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-Bz37odzX.mjs +0 -26
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B_KS96RA.mjs +0 -26
@@ -21843,14 +21843,52 @@ var NcAlarmNonBlockingEditSchema = object({
21843
21843
  nonBlocking: boolean()
21844
21844
  });
21845
21845
  /**
21846
+ * Controllable openings: opt IN to blocking (default is do not block). Contacts
21847
+ * keep using {@link NcAlarmNonBlockingEditSchema}.
21848
+ */
21849
+ var NcAlarmBlockingEditSchema = object({
21850
+ mode: AlarmArmModeSchema,
21851
+ deviceId: number().int().positive(),
21852
+ /** true = on the list (blocks when open); false = off the list (default). */
21853
+ blocking: boolean()
21854
+ });
21855
+ /**
21856
+ * Controllable openings: opt OUT of secure-on-arm (default is secure ON —
21857
+ * close / lock / turn off when this mode arms).
21858
+ */
21859
+ var NcAlarmSecureOnArmEditSchema = object({
21860
+ mode: AlarmArmModeSchema,
21861
+ deviceId: number().int().positive(),
21862
+ /** true = secure on arm (default); false = do not actuate on arm. */
21863
+ secureOnArm: boolean()
21864
+ });
21865
+ /**
21866
+ * One arm mode's device-id list (non-blocking, blocking, or secure-on-arm-off).
21867
+ */
21868
+ var NcAlarmModeDeviceListSchema = object({
21869
+ mode: AlarmArmModeSchema,
21870
+ deviceIds: array(number().int()).max(200)
21871
+ });
21872
+ /**
21873
+ * One arm mode's non-blocking list: devices that may be open when that mode
21874
+ * arms, without the arm being refused.
21875
+ */
21876
+ var NcAlarmNonBlockingSchema = NcAlarmModeDeviceListSchema;
21877
+ /** Controllable devices that DO block when open (opt-in; default empty). */
21878
+ var NcAlarmBlockingSchema = NcAlarmModeDeviceListSchema;
21879
+ /**
21880
+ * Controllable devices that do NOT secure on arm (opt-out; default empty =
21881
+ * every controllable secures).
21882
+ */
21883
+ var NcAlarmSecureOnArmOffSchema = NcAlarmModeDeviceListSchema;
21884
+ /**
21846
21885
  * The per-mode lists, at most one per mode. Two lists for one mode have no
21847
21886
  * meaning a reader could agree on (union? the last one?), so they are refused
21848
21887
  * at the boundary rather than interpreted.
21849
21888
  */
21850
- var NcAlarmNonBlockingListSchema = array(object({
21851
- mode: AlarmArmModeSchema,
21852
- deviceIds: array(number().int()).max(200)
21853
- })).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
21889
+ var NcAlarmNonBlockingListSchema = array(NcAlarmNonBlockingSchema).max(8).refine(uniqueModes, { message: "nonBlocking: at most one list per arm mode" });
21890
+ var NcAlarmBlockingListSchema = array(NcAlarmBlockingSchema).max(8).refine(uniqueModes, { message: "blocking: at most one list per arm mode" });
21891
+ var NcAlarmSecureOnArmOffListSchema = array(NcAlarmSecureOnArmOffSchema).max(8).refine(uniqueModes, { message: "secureOnArmOff: at most one list per arm mode" });
21854
21892
  /**
21855
21893
  * The three durations — global, and per mode since D697 — the panel's state
21856
21894
  * machine runs on, who hears about its transitions, and which openings each mode tolerates.
@@ -21893,9 +21931,23 @@ var NcAlarmSettingsSchema = object({
21893
21931
  * arm (a window left ajar for the cat under `home`, say). At most one entry
21894
21932
  * per mode is meaningful. Defaulted to `[]` so a blob stored before D630
21895
21933
  * parses as "every opening blocks" — the behaviour it was written under.
21934
+ * Contacts use this list; controllable openings use {@link blocking} instead
21935
+ * (default: do not block).
21896
21936
  */
21897
21937
  nonBlocking: NcAlarmNonBlockingListSchema.default([]),
21898
21938
  /**
21939
+ * Per arm mode, controllable openings that DO refuse the arm when open.
21940
+ * Default `[]` = none block (secure-on-arm closes them instead). Absent on a
21941
+ * blob older than this field — read as empty.
21942
+ */
21943
+ blocking: NcAlarmBlockingListSchema.default([]),
21944
+ /**
21945
+ * Per arm mode, controllable openings that must NOT be actuated on arm.
21946
+ * Default `[]` = every controllable secures (close / lock / off). Absent on
21947
+ * an older blob — read as empty (secure ON).
21948
+ */
21949
+ secureOnArmOff: NcAlarmSecureOnArmOffListSchema.default([]),
21950
+ /**
21899
21951
  * Per arm mode, the delays that mode OVERRIDES (D697). A mode with no entry, or an entry
21900
21952
  * without a field, uses the global value above — so `[]`, the default, is exactly the
21901
21953
  * behaviour of a blob stored before this field existed. Read through `resolveModeDelays`.
@@ -21905,7 +21957,7 @@ var NcAlarmSettingsSchema = object({
21905
21957
  /**
21906
21958
  * Every field optional — a tab edits one control at a time.
21907
21959
  *
21908
- * `nonBlocking` and `modeDelays` are re-declared WITHOUT their defaults:
21960
+ * List fields and `modeDelays` are re-declared WITHOUT their defaults:
21909
21961
  * `.partial()` does not remove an inner `.default()`, so a patch that never
21910
21962
  * named the field would parse to `[]` and wipe every mode's list on an
21911
21963
  * unrelated edit (the same trap {@link NcRulePatchSchema} documents). Here
@@ -21913,11 +21965,15 @@ var NcAlarmSettingsSchema = object({
21913
21965
  */
21914
21966
  var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({
21915
21967
  nonBlocking: NcAlarmNonBlockingListSchema,
21968
+ blocking: NcAlarmBlockingListSchema,
21969
+ secureOnArmOff: NcAlarmSecureOnArmOffListSchema,
21916
21970
  /** Per-mode, per-field edits — NOT the whole list (see NcAlarmModeDelaysEditSchema). */
21917
21971
  modeDelays: NcAlarmModeDelaysEditListSchema,
21918
21972
  /** ONE membership change; the panel applies it to the list it holds at its turn. */
21919
- nonBlockingEdit: NcAlarmNonBlockingEditSchema
21920
- }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" });
21973
+ nonBlockingEdit: NcAlarmNonBlockingEditSchema,
21974
+ blockingEdit: NcAlarmBlockingEditSchema,
21975
+ secureOnArmEdit: NcAlarmSecureOnArmEditSchema
21976
+ }).partial().refine((p) => p.nonBlocking === void 0 || p.nonBlockingEdit === void 0, { message: "nonBlocking and nonBlockingEdit are exclusive — send the whole lists or one edit" }).refine((p) => p.blocking === void 0 || p.blockingEdit === void 0, { message: "blocking and blockingEdit are exclusive — send the whole lists or one edit" }).refine((p) => p.secureOnArmOff === void 0 || p.secureOnArmEdit === void 0, { message: "secureOnArmOff and secureOnArmEdit are exclusive — send the whole lists or one edit" });
21921
21977
  /**
21922
21978
  * What one arm mode actually arms, DERIVED from the enabled rules gated on it.
21923
21979
  * Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
@@ -22032,7 +22088,13 @@ var NcAlarmSensorStateSchema = object({
22032
22088
  /** Why `unknown`: the read failed, or the slice holds no word its cap defines (a placeholder included). */
22033
22089
  unknownWhy: _enum(["read-failed", "unknown-word"]).nullable(),
22034
22090
  /** The device manager's liveness as the centre mirrors it; null = not known. */
22035
- online: boolean().nullable()
22091
+ online: boolean().nullable(),
22092
+ /**
22093
+ * True when the opening is a cover / lock / switch — the table shows the
22094
+ * Secure-on-arm switch and uses the controllable defaults. False for contacts
22095
+ * and for non-openings. Defaulted so an older hub answer still parses.
22096
+ */
22097
+ controllable: boolean().default(false)
22036
22098
  });
22037
22099
  var NcAlarmSensorsInputSchema = object({ deviceIds: array(number().int().positive()).max(500) });
22038
22100
  var NcAlarmSensorsSchema = object({
@@ -31474,6 +31536,65 @@ DeviceType.Button, method(object({ deviceId: number().int().nonnegative() }), _v
31474
31536
  auth: "protected"
31475
31537
  });
31476
31538
  /**
31539
+ * `calendar-source` — collection cap exposing per-provider calendars and
31540
+ * their events. Every addon that contributes calendars (Home Assistant,
31541
+ * a future Google Calendar addon, …) registers a provider here. The hub
31542
+ * singleton `calendars` walks the collection and merges the answers.
31543
+ *
31544
+ * Mirrors `addon-pages-source`: the public listing is a different shape
31545
+ * (source-stamped ids) and lives on the aggregator.
31546
+ */
31547
+ var CalendarSourceKindSchema = _enum(["ha", "addon"]);
31548
+ var CalendarInfoSchema = object({
31549
+ /** Provider-local id (e.g. HA `calendar.family`). The aggregator prefixes. */
31550
+ id: string().min(1),
31551
+ name: string().min(1),
31552
+ source: CalendarSourceKindSchema,
31553
+ enabled: boolean()
31554
+ });
31555
+ var CalendarEventSchema = object({
31556
+ id: string().min(1),
31557
+ calendarId: string().min(1),
31558
+ title: string(),
31559
+ startMs: number().int(),
31560
+ endMs: number().int(),
31561
+ allDay: boolean().optional(),
31562
+ description: string().optional(),
31563
+ location: string().optional()
31564
+ });
31565
+ var CalendarListEventsInputSchema = object({
31566
+ calendarIds: array(string().min(1)).max(64).optional(),
31567
+ startMs: number().int(),
31568
+ endMs: number().int()
31569
+ });
31570
+ method(_void(), array(CalendarInfoSchema).readonly(), { auth: "admin" }), method(CalendarListEventsInputSchema, array(CalendarEventSchema).readonly(), { auth: "admin" });
31571
+ /**
31572
+ * `calendars` — system-scoped singleton. Public surface admin-ui and the
31573
+ * notification-center calendar-events producer consume.
31574
+ *
31575
+ * Walks every `calendar-source` provider, stamps each calendar with a
31576
+ * cluster-stable id (`<sourceAddonId>:<localId>`) and the providing
31577
+ * addon's id, and fans `listEvents` out the same way. Home Assistant
31578
+ * import is a built-in refresh of the HA source rather than a second
31579
+ * registry — `importFromHomeAssistant` asks that source to re-discover.
31580
+ */
31581
+ var CalendarEntrySchema = CalendarInfoSchema.extend({
31582
+ /** Cluster-stable id: `<providerAddonId>:<localId>`. */
31583
+ id: string().min(1),
31584
+ /** The addon that registered the calendar-source provider. */
31585
+ providerAddonId: string().min(1).optional()
31586
+ });
31587
+ var CalendarImportResultSchema = object({
31588
+ imported: number().int().nonnegative(),
31589
+ /** Absent when no HA calendar-source provider was reachable. */
31590
+ providerAddonId: string().min(1).optional(),
31591
+ error: string().optional()
31592
+ });
31593
+ method(_void(), array(CalendarEntrySchema).readonly(), { auth: "admin" }), method(CalendarListEventsInputSchema, array(CalendarEventSchema).readonly(), { auth: "admin" }), method(_void(), CalendarImportResultSchema, {
31594
+ auth: "admin",
31595
+ kind: "mutation"
31596
+ });
31597
+ /**
31477
31598
  * camera-credentials — device-scoped cap exposing the camera's network
31478
31599
  * + auth surface in a vendor-neutral shape.
31479
31600
  *
@@ -36129,38 +36250,52 @@ DeviceType.Camera, method(object({
36129
36250
  deviceId: number(),
36130
36251
  status: OsdStatusSchema
36131
36252
  });
36253
+ _enum([
36254
+ "value",
36255
+ "unit",
36256
+ "full"
36257
+ ]);
36258
+ _enum([
36259
+ "value+unit",
36260
+ "value",
36261
+ "unit"
36262
+ ]);
36132
36263
  /**
36133
- * `osd-manager` — the ORCHESTRATOR over the device-scope `osd` cap.
36134
- *
36135
- * The `osd` cap is the firmware contract: it probes a camera's overlay
36136
- * SLOTS and writes literal text into one. It has no idea WHERE that text
36137
- * comes from, and it must not — a driver that grew a "show the temperature
36138
- * here" feature would grow it once per vendor.
36139
- *
36140
- * This cap owns the other half: a per-(camera, slot) BINDING that says
36141
- * which value feeds the slot, how it is formatted, and under which
36142
- * conditions it is shown at all. One addon renders every binding on every
36143
- * camera, so a new source costs zero driver code.
36144
- *
36145
- * Three deliberate choices, each with a rejected alternative:
36146
- *
36147
- * 1. A source is `(capName, valuePath)` over the kernel's device
36148
- * runtime-state mirror — NOT a closed enum of source kinds. Every
36149
- * cap-keyed slice a device publishes is bindable the day the cap
36150
- * ships. The rejected alternative (one enum member per source, with
36151
- * a resolver branch each) is what makes "add the humidity too" a
36152
- * code change.
36153
- * 2. The display gate reuses `NcConditionsSchema` verbatim — the
36154
- * notification centre's condition vocabulary — rather than a parallel
36155
- * model. An operator who has learned one condition editor has learned
36156
- * both.
36157
- * 3. Because the renderer's facts are device STATE and not a detection
36158
- * record, only a SUBSET of that vocabulary can be answered here.
36159
- * `setSlotBinding` REJECTS the rest at write time (see
36160
- * `getConditionSupport`). It does not accept-then-fail-closed: a
36161
- * condition that can never be true renders a permanently blank
36162
- * overlay, and a blank overlay looks exactly like a broken camera.
36264
+ * How the operator reached the leaf device. `deviceId` is ALWAYS the
36265
+ * OSD-supported leaf that is read; `containerId` is set when the pick
36266
+ * went through a container (gateway) first.
36163
36267
  */
36268
+ var OsdTemplatePickSchema = object({
36269
+ containerId: number().int().optional(),
36270
+ deviceId: number().int()
36271
+ });
36272
+ /**
36273
+ * One named binding inside a multi-device template: device path + unit
36274
+ * selection + precision. The parser expands `{alias}`, `{alias.value}`,
36275
+ * `{alias.unit}`, or `{alias.full}` from the same binding — there is no
36276
+ * per-binding format lock.
36277
+ */
36278
+ var OsdTemplateBindingSchema = object({
36279
+ /** Token stem in the parser, e.g. `t` → `{t}` / `{t.value}`. Unique within the template. */
36280
+ alias: string().min(1).max(32).regex(/^[A-Za-z][A-Za-z0-9_-]*$/, "alias must start with a letter"),
36281
+ pick: OsdTemplatePickSchema,
36282
+ capName: string().min(1).max(64),
36283
+ valuePath: string().min(1).max(64),
36284
+ /** Convert TO this unit at render (`tryConvertUnit`). Absent = source unit. */
36285
+ displayUnit: string().max(16).optional(),
36286
+ maxDecimals: number().int().min(0).max(4).default(1)
36287
+ });
36288
+ /**
36289
+ * A named template: bindings (alias → leaf field) + a parser that
36290
+ * references `{alias}` / `{alias.value}` / `{alias.unit}` / `{alias.full}`.
36291
+ */
36292
+ var OsdNamedTemplateSchema = object({
36293
+ id: string().min(1).max(64),
36294
+ name: string().min(1).max(64),
36295
+ bindings: array(OsdTemplateBindingSchema).max(32).default([]),
36296
+ /** Parser with alias placeholders, e.g. `{t.value} {t.unit} / {h}`. */
36297
+ parser: string().max(256).default("")
36298
+ });
36164
36299
  /** Where a slot's value comes from. */
36165
36300
  var OsdSourceSchema = discriminatedUnion("kind", [
36166
36301
  object({
@@ -36179,11 +36314,17 @@ var OsdSourceSchema = discriminatedUnion("kind", [
36179
36314
  deviceId: number().int().optional(),
36180
36315
  capName: string().min(1).max(64),
36181
36316
  /** Dot path inside the slice, e.g. `detected`, `value`, `mode`. */
36182
- valuePath: string().min(1).max(64)
36317
+ valuePath: string().min(1).max(64),
36318
+ /** Convert TO this unit at render. Absent = no conversion. */
36319
+ displayUnit: string().max(16).optional()
36183
36320
  }),
36184
36321
  object({
36185
36322
  kind: literal("latest-recognition"),
36186
36323
  recognition: _enum(["person", "plate"])
36324
+ }),
36325
+ object({
36326
+ kind: literal("template"),
36327
+ templateId: string().min(1).max(64)
36187
36328
  })
36188
36329
  ]);
36189
36330
  var OsdSlotBindingSchema = object({
@@ -36199,7 +36340,10 @@ var OsdSlotBindingSchema = object({
36199
36340
  * documented workaround for firmwares that reject `.` in overlay text.
36200
36341
  */
36201
36342
  maxDecimals: number().int().min(0).max(4).default(1),
36202
- /** Appended via `${unit}`. The state mirror does not carry units. */
36343
+ /**
36344
+ * Appended via `${unit}` when no unit conversion produces a label.
36345
+ * Prefer `source.displayUnit` for converted device-state values.
36346
+ */
36203
36347
  unitLabel: string().max(8).optional(),
36204
36348
  /** Raw value → display text, e.g. `{"true":"MOTION","false":""}`. */
36205
36349
  valueMap: record(string(), string()).optional(),
@@ -36308,6 +36452,12 @@ method(object({ deviceId: number().int() }), object({
36308
36452
  }), method(object({ deviceId: number().int() }), object({ results: array(OsdRenderResultSchema) }), {
36309
36453
  kind: "mutation",
36310
36454
  auth: "admin"
36455
+ }), method(object({}), object({ templates: array(OsdNamedTemplateSchema) }), { auth: "admin" }), method(object({ template: OsdNamedTemplateSchema }), object({ template: OsdNamedTemplateSchema }), {
36456
+ kind: "mutation",
36457
+ auth: "admin"
36458
+ }), method(object({ templateId: string().min(1).max(64) }), object({ success: literal(true) }), {
36459
+ kind: "mutation",
36460
+ auth: "admin"
36311
36461
  });
36312
36462
  /**
36313
36463
  * Feeder connectivity / power status — mirrors the HA petkit device-status
@@ -44762,6 +44912,36 @@ Object.freeze({
44762
44912
  addonId: null,
44763
44913
  access: "create"
44764
44914
  },
44915
+ "calendars.importFromHomeAssistant": {
44916
+ capName: "calendars",
44917
+ capScope: "system",
44918
+ addonId: null,
44919
+ access: "create"
44920
+ },
44921
+ "calendars.listCalendars": {
44922
+ capName: "calendars",
44923
+ capScope: "system",
44924
+ addonId: null,
44925
+ access: "view"
44926
+ },
44927
+ "calendars.listEvents": {
44928
+ capName: "calendars",
44929
+ capScope: "system",
44930
+ addonId: null,
44931
+ access: "view"
44932
+ },
44933
+ "calendarSource.listCalendars": {
44934
+ capName: "calendar-source",
44935
+ capScope: "system",
44936
+ addonId: null,
44937
+ access: "view"
44938
+ },
44939
+ "calendarSource.listEvents": {
44940
+ capName: "calendar-source",
44941
+ capScope: "system",
44942
+ addonId: null,
44943
+ access: "view"
44944
+ },
44765
44945
  "cameraCredentials.getCredentials": {
44766
44946
  capName: "camera-credentials",
44767
44947
  capScope: "device",
@@ -47756,6 +47936,12 @@ Object.freeze({
47756
47936
  addonId: null,
47757
47937
  access: "create"
47758
47938
  },
47939
+ "osdManager.deleteTemplate": {
47940
+ capName: "osd-manager",
47941
+ capScope: "system",
47942
+ addonId: null,
47943
+ access: "delete"
47944
+ },
47759
47945
  "osdManager.getConditionSupport": {
47760
47946
  capName: "osd-manager",
47761
47947
  capScope: "system",
@@ -47774,6 +47960,12 @@ Object.freeze({
47774
47960
  addonId: null,
47775
47961
  access: "view"
47776
47962
  },
47963
+ "osdManager.listTemplates": {
47964
+ capName: "osd-manager",
47965
+ capScope: "system",
47966
+ addonId: null,
47967
+ access: "view"
47968
+ },
47777
47969
  "osdManager.previewSlot": {
47778
47970
  capName: "osd-manager",
47779
47971
  capScope: "system",
@@ -47792,6 +47984,12 @@ Object.freeze({
47792
47984
  addonId: null,
47793
47985
  access: "create"
47794
47986
  },
47987
+ "osdManager.upsertTemplate": {
47988
+ capName: "osd-manager",
47989
+ capScope: "system",
47990
+ addonId: null,
47991
+ access: "create"
47992
+ },
47795
47993
  "petFeeder.callPet": {
47796
47994
  capName: "pet-feeder",
47797
47995
  capScope: "device",
@@ -54372,6 +54570,18 @@ var ARMED_WORD_PREFIX = "armed_";
54372
54570
  var ALARM_PANEL_CAP = "alarm-panel";
54373
54571
  var ALARM_PANEL_TRIGGER_METHOD = "trigger";
54374
54572
  /**
54573
+ * Opening caps that can be ACTUATED to a secure state on arm (close / lock /
54574
+ * off). Contacts are openings but not controllable — they only block.
54575
+ */
54576
+ var NC_ALARM_CONTROLLABLE_CAPS = [
54577
+ "cover",
54578
+ "lock-control",
54579
+ "switch"
54580
+ ];
54581
+ function isAlarmControllableCap(cap) {
54582
+ return NC_ALARM_CONTROLLABLE_CAPS.includes(cap);
54583
+ }
54584
+ /**
54375
54585
  * A rule belongs to the alarm when its `deviceState` gate names ANY device's
54376
54586
  * `armed_*` word, or an ENABLED `onTrigger` sequence pulls the alarm panel's
54377
54587
  * trigger (D630).
@@ -54380,6 +54590,32 @@ function ruleBelongsToAlarm(rule) {
54380
54590
  if ((rule.conditions.deviceState?.states ?? []).some((s) => s.trim().toLowerCase().startsWith(ARMED_WORD_PREFIX))) return true;
54381
54591
  return (rule.onTrigger ?? []).some((sequence) => sequence.enabled && sequence.actions.some((step) => step.kind === "cap" && step.cap === ALARM_PANEL_CAP && step.method === ALARM_PANEL_TRIGGER_METHOD));
54382
54592
  }
54593
+ /** Whether this controllable should be closed/locked/turned off on arm. */
54594
+ function alarmDeviceSecureOnArm(input) {
54595
+ if (!input.controllable) return false;
54596
+ return !input.secureOnArmOff.includes(input.deviceId);
54597
+ }
54598
+ /** The one secure command for a controllable opening cap, or null for contacts. */
54599
+ function secureActuationFor(cap) {
54600
+ if (!isAlarmControllableCap(cap)) return null;
54601
+ switch (cap) {
54602
+ case "cover": return {
54603
+ cap,
54604
+ method: "close",
54605
+ args: {}
54606
+ };
54607
+ case "lock-control": return {
54608
+ cap,
54609
+ method: "lock",
54610
+ args: {}
54611
+ };
54612
+ case "switch": return {
54613
+ cap,
54614
+ method: "setState",
54615
+ args: { on: false }
54616
+ };
54617
+ }
54618
+ }
54383
54619
  var NC_ALARM_DELAY_FIELDS = [
54384
54620
  "exitDelaySec",
54385
54621
  "entryDelaySec",
@@ -54439,10 +54675,11 @@ function applyModeDelaysEdits(current, edits) {
54439
54675
  return out.filter((e) => e.exitDelaySec !== void 0 || e.entryDelaySec !== void 0 || e.triggeredDurationSec !== void 0);
54440
54676
  }
54441
54677
  /**
54442
- * Put ONE device on, or take it off, ONE mode's list. A no-op answers `changed: false`; an add
54443
- * to a full list is REFUSED by reason (never silently dropped); an emptied list drops its entry.
54678
+ * Put ONE device on, or take it off, ONE mode's device-id list. A no-op answers
54679
+ * `changed: false`; an add to a full list is REFUSED by reason (never silently
54680
+ * dropped); an emptied list drops its entry.
54444
54681
  */
54445
- function applyNonBlockingEdit(lists, edit) {
54682
+ function applyModeDeviceListEdit(lists, edit) {
54446
54683
  const copy = lists.map((l) => ({
54447
54684
  mode: l.mode,
54448
54685
  deviceIds: [...l.deviceIds]
@@ -54450,12 +54687,12 @@ function applyNonBlockingEdit(lists, edit) {
54450
54687
  const at = copy.findIndex((l) => l.mode === edit.mode);
54451
54688
  const held = at >= 0 ? copy[at]?.deviceIds ?? [] : [];
54452
54689
  const present = held.includes(edit.deviceId);
54453
- if (edit.nonBlocking === present) return {
54690
+ if (edit.present === present) return {
54454
54691
  kind: "applied",
54455
54692
  lists: copy,
54456
54693
  changed: false
54457
54694
  };
54458
- if (edit.nonBlocking) {
54695
+ if (edit.present) {
54459
54696
  if (held.length >= 200) return {
54460
54697
  kind: "refused",
54461
54698
  reason: "list-full",
@@ -54482,6 +54719,35 @@ function applyNonBlockingEdit(lists, edit) {
54482
54719
  };
54483
54720
  }
54484
54721
  /**
54722
+ * Put ONE device on, or take it off, ONE mode's non-blocking list.
54723
+ */
54724
+ function applyNonBlockingEdit(lists, edit) {
54725
+ return applyModeDeviceListEdit(lists, {
54726
+ mode: edit.mode,
54727
+ deviceId: edit.deviceId,
54728
+ present: edit.nonBlocking
54729
+ });
54730
+ }
54731
+ /** Controllable opt-in to blocking. */
54732
+ function applyBlockingEdit(lists, edit) {
54733
+ return applyModeDeviceListEdit(lists, {
54734
+ mode: edit.mode,
54735
+ deviceId: edit.deviceId,
54736
+ present: edit.blocking
54737
+ });
54738
+ }
54739
+ /**
54740
+ * Controllable secure-on-arm: `secureOnArm: true` removes from the opt-out list;
54741
+ * `false` adds to it.
54742
+ */
54743
+ function applySecureOnArmEdit(lists, edit) {
54744
+ return applyModeDeviceListEdit(lists, {
54745
+ mode: edit.mode,
54746
+ deviceId: edit.deviceId,
54747
+ present: !edit.secureOnArm
54748
+ });
54749
+ }
54750
+ /**
54485
54751
  * WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
54486
54752
  * persists (D640 "a sensor rule fires on the raise, the clear, or both").
54487
54753
  *
@@ -57684,12 +57950,24 @@ Object.defineProperty(exports, "addonWidgetsSourceCapability", {
57684
57950
  return addonWidgetsSourceCapability;
57685
57951
  }
57686
57952
  });
57953
+ Object.defineProperty(exports, "alarmDeviceSecureOnArm", {
57954
+ enumerable: true,
57955
+ get: function() {
57956
+ return alarmDeviceSecureOnArm;
57957
+ }
57958
+ });
57687
57959
  Object.defineProperty(exports, "alarmPanelCapability", {
57688
57960
  enumerable: true,
57689
57961
  get: function() {
57690
57962
  return alarmPanelCapability;
57691
57963
  }
57692
57964
  });
57965
+ Object.defineProperty(exports, "applyBlockingEdit", {
57966
+ enumerable: true,
57967
+ get: function() {
57968
+ return applyBlockingEdit;
57969
+ }
57970
+ });
57693
57971
  Object.defineProperty(exports, "applyModeDelaysEdits", {
57694
57972
  enumerable: true,
57695
57973
  get: function() {
@@ -57702,6 +57980,12 @@ Object.defineProperty(exports, "applyNonBlockingEdit", {
57702
57980
  return applyNonBlockingEdit;
57703
57981
  }
57704
57982
  });
57983
+ Object.defineProperty(exports, "applySecureOnArmEdit", {
57984
+ enumerable: true,
57985
+ get: function() {
57986
+ return applySecureOnArmEdit;
57987
+ }
57988
+ });
57705
57989
  Object.defineProperty(exports, "array", {
57706
57990
  enumerable: true,
57707
57991
  get: function() {
@@ -57882,6 +58166,12 @@ Object.defineProperty(exports, "hydrateSchema", {
57882
58166
  return hydrateSchema;
57883
58167
  }
57884
58168
  });
58169
+ Object.defineProperty(exports, "isAlarmControllableCap", {
58170
+ enumerable: true,
58171
+ get: function() {
58172
+ return isAlarmControllableCap;
58173
+ }
58174
+ });
57885
58175
  Object.defineProperty(exports, "isAudioClassName", {
57886
58176
  enumerable: true,
57887
58177
  get: function() {
@@ -58134,6 +58424,12 @@ Object.defineProperty(exports, "sceneMonitorCapability", {
58134
58424
  return sceneMonitorCapability;
58135
58425
  }
58136
58426
  });
58427
+ Object.defineProperty(exports, "secureActuationFor", {
58428
+ enumerable: true,
58429
+ get: function() {
58430
+ return secureActuationFor;
58431
+ }
58432
+ });
58137
58433
  Object.defineProperty(exports, "sensorRaisedOf", {
58138
58434
  enumerable: true,
58139
58435
  get: function() {