@camstack/types 1.2.24 → 1.2.26

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.
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Which capability says what a device's STATE is, and how to read it.
3
+ *
4
+ * `deviceManager.loadRuntimeState` returns every cap's slice for a device,
5
+ * keyed by the kebab-case cap name:
6
+ *
7
+ * { 'device-status': { online: true }, switch: { on: true, … } }
8
+ *
9
+ * "The state of this device" is not a thing the platform models — a switch has
10
+ * `on`, a contact has `entryOpen`, an alarm panel has `state`. So the mapping
11
+ * is a TABLE, declared here, rather than a guess made at read time. An
12
+ * unmapped cap yields no state, and no state never matches a gate: adding a cap
13
+ * cannot silently change what an existing rule does.
14
+ *
15
+ * ORDER MATTERS. A device usually carries several of these slices (a switch is
16
+ * also `device-status`), and the first match wins — so the list runs from most
17
+ * specific to least. `device-status` is deliberately absent: "online" is not a
18
+ * state a rule should gate on, and having it here would make every device match
19
+ * the first entry and mask its real one.
20
+ */
21
+ /** How one capability's slice collapses to a single comparable string. */
22
+ interface StateReader {
23
+ /** kebab-case cap name, as `loadRuntimeState` keys it. */
24
+ readonly cap: string;
25
+ /** Field inside that slice. */
26
+ readonly field: string;
27
+ /**
28
+ * Vocabulary a rule author writes. A boolean field becomes these two words,
29
+ * in this order — `[whenTrue, whenFalse]`. Absent for a field that is already
30
+ * a string (the alarm's `state`, the cover's `state`).
31
+ */
32
+ readonly booleanWords?: readonly [string, string];
33
+ }
34
+ /**
35
+ * Most specific first. Extending this list is how a new device kind becomes
36
+ * gateable; nothing else needs to change.
37
+ */
38
+ export declare const DEVICE_STATE_READERS: readonly StateReader[];
39
+ /**
40
+ * Collapse a device's full runtime state to the one string a rule compares
41
+ * against, or `undefined` when nothing in the table applies.
42
+ *
43
+ * `undefined` is the safe answer everywhere: the gate treats it as "does not
44
+ * match", so a device whose kind we cannot read simply never arms a rule.
45
+ */
46
+ export declare function readDeviceStateFrom(runtimeState: Readonly<Record<string, unknown>>): string | undefined;
47
+ /**
48
+ * The states a rule editor can offer for a device, given its runtime state.
49
+ * Same table, same order — so the picker can never offer a value the gate
50
+ * would not recognise.
51
+ */
52
+ export declare function stateVocabularyFor(runtimeState: Readonly<Record<string, unknown>>, alarmStates?: readonly string[]): readonly string[];
53
+ export {};
@@ -4326,6 +4326,27 @@ export type AppRouter = TrpcCoreRouter<{
4326
4326
  output: z.infer<typeof notificationRulesCapability.methods.getHistory.output>;
4327
4327
  meta: object;
4328
4328
  }>;
4329
+ listSnoozes: TRPCQueryProcedure<{
4330
+ input: {
4331
+ [x: string]: unknown;
4332
+ } & z.input<typeof notificationRulesCapability.methods.listSnoozes.input>;
4333
+ output: z.infer<typeof notificationRulesCapability.methods.listSnoozes.output>;
4334
+ meta: object;
4335
+ }>;
4336
+ createSnooze: TRPCMutationProcedure<{
4337
+ input: {
4338
+ [x: string]: unknown;
4339
+ } & z.input<typeof notificationRulesCapability.methods.createSnooze.input>;
4340
+ output: z.infer<typeof notificationRulesCapability.methods.createSnooze.output>;
4341
+ meta: object;
4342
+ }>;
4343
+ cancelSnooze: TRPCMutationProcedure<{
4344
+ input: {
4345
+ [x: string]: unknown;
4346
+ } & z.input<typeof notificationRulesCapability.methods.cancelSnooze.input>;
4347
+ output: z.infer<typeof notificationRulesCapability.methods.cancelSnooze.output>;
4348
+ meta: object;
4349
+ }>;
4329
4350
  }>>;
4330
4351
  notifier: TRPCBuiltRouter<{
4331
4352
  ctx: TrpcContext;
@@ -49,6 +49,7 @@ import type { motionDetectionCapability } from '../capabilities/motion-detection
49
49
  import type { motionTriggerCapability } from '../capabilities/motion-trigger.cap.js';
50
50
  import type { motionZonesCapability } from '../capabilities/motion-zones.cap.js';
51
51
  import type { nativeObjectDetectionCapability } from '../capabilities/native-object-detection.cap.js';
52
+ import type { notificationRulesCapability } from '../capabilities/notification-rules.cap.js';
52
53
  import type { notifierCapability } from '../capabilities/notifier.cap.js';
53
54
  import type { numericSensorCapability } from '../capabilities/numeric-sensor.cap.js';
54
55
  import type { osdCapability } from '../capabilities/osd.cap.js';
@@ -230,6 +231,7 @@ export interface DeviceProxy {
230
231
  readonly motionTrigger?: InferDeviceProxyCap<typeof motionTriggerCapability>;
231
232
  readonly motionZones?: InferDeviceProxyCap<typeof motionZonesCapability>;
232
233
  readonly nativeObjectDetection?: InferDeviceProxyCap<typeof nativeObjectDetectionCapability>;
234
+ readonly notificationRules?: InferDeviceProxyCap<typeof notificationRulesCapability>;
233
235
  readonly notifier?: InferDeviceProxyCap<typeof notifierCapability>;
234
236
  readonly numericSensor?: InferDeviceProxyCap<typeof numericSensorCapability>;
235
237
  readonly osd?: InferDeviceProxyCap<typeof osdCapability>;
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 841 method paths across 118 capabilities.
9
+ * Coverage: 844 method paths across 118 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {
@@ -26,7 +26,6 @@ import type { networkAccessCapability } from '../capabilities/network-access.cap
26
26
  import type { networkQualityCapability } from '../capabilities/network-quality.cap.js';
27
27
  import type { nodesCapability } from '../capabilities/nodes.cap.js';
28
28
  import type { notificationOutputCapability } from '../capabilities/notification-output.cap.js';
29
- import type { notificationRulesCapability } from '../capabilities/notification-rules.cap.js';
30
29
  import type { pipelineExecutorCapability } from '../capabilities/pipeline-executor.cap.js';
31
30
  import type { pipelineOrchestratorCapability } from '../capabilities/pipeline-orchestrator.cap.js';
32
31
  import type { pipelineRunnerCapability } from '../capabilities/pipeline-runner.cap.js';
@@ -77,7 +76,6 @@ export interface SystemProxy {
77
76
  readonly networkQuality: Pick<InferProvider<typeof networkQualityCapability>, 'getAllStats'>;
78
77
  readonly nodes: Pick<InferProvider<typeof nodesCapability>, 'topology' | 'deployAddon' | 'undeployAddon' | 'restartAddon' | 'restartProcess' | 'restartNode' | 'shutdownNode' | 'renameNode' | 'clusterAddonStatus' | 'getCapUsageGraph' | 'getNodeAddons' | 'setProcessLogLevel' | 'executeQuery'>;
79
78
  readonly notificationOutput: Pick<InferProvider<typeof notificationOutputCapability>, 'listTargetKinds' | 'listTargets' | 'discoverTargets' | 'send' | 'testTarget' | 'upsertTarget' | 'deleteTarget' | 'setTargetEnabled'>;
80
- readonly notificationRules: Pick<InferProvider<typeof notificationRulesCapability>, 'listRules' | 'getRule' | 'createRule' | 'updateRule' | 'deleteRule' | 'setRuleEnabled' | 'testRule' | 'getConditionCatalog' | 'getHistory'>;
81
79
  readonly pipelineExecutor: Pick<InferProvider<typeof pipelineExecutorCapability>, 'getAvailableEngines' | 'getSelectedEngine' | 'getDefaultSteps' | 'getEngineProvisioning' | 'getVideoPipelineSteps' | 'setVideoPipelineSteps' | 'clearDeviceOverrides' | 'getSchema' | 'getGlobalSteps' | 'getGlobalPipelineConfig' | 'getOrchestratorConfigSchema' | 'validatePipeline' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate' | 'getCapabilities' | 'getAddonModels' | 'downloadModel' | 'deleteModel' | 'cacheFrameInPool' | 'inferCached' | 'uncacheFrame' | 'getEffectiveTuning' | 'listLoadedEngines' | 'spinEngine' | 'killEngine' | 'listReferenceImages' | 'getReferenceImage' | 'getReferenceAudioFiles' | 'getReferenceAudio' | 'getAudioCapabilities' | 'runAudioTest' | 'getDetectionConfigSchema'>;
82
80
  readonly pipelineOrchestrator: Pick<InferProvider<typeof pipelineOrchestratorCapability>, 'rebalance' | 'getPipelineAssignments' | 'getAgentLoad' | 'getGlobalMetrics' | 'getCapabilityBindings' | 'setCapabilityBinding' | 'getIngestOwner' | 'getAudioNodeLoad' | 'getAgentSettings' | 'listAgentSettings' | 'removeAgentSettings' | 'setAgentMaxCameras' | 'setAgentDetectWeight' | 'setAgentCapabilities' | 'setAgentReachableHost' | 'setAgentInferenceDevices' | 'getNodeInferenceDevices' | 'resetNodePipelineDefaults' | 'getCameraStatuses' | 'listTemplates' | 'saveTemplate' | 'updateTemplate' | 'deleteTemplate'>;
83
81
  readonly pipelineRunner: Pick<InferProvider<typeof pipelineRunnerCapability>, 'attachCamera' | 'reportMotion' | 'getLocalLoad' | 'getLocalMetrics' | 'getAllCameraMetrics' | 'getLocalCameras' | 'getNativeCrop'>;
package/dist/index.d.ts CHANGED
@@ -121,6 +121,7 @@ export { COCO_80_LABELS, COCO_TO_MACRO, MACRO_LABELS, } from './catalogs/coco-cl
121
121
  export { AUDIO_MACRO_LABELS, YAMNET_TO_MACRO, APPLE_SA_TO_MACRO, mapAudioLabelToMacro, getAudioMacroClassIds, } from './catalogs/audio-classmap.js';
122
122
  export { EVENT_TAXONOMY, TAXONOMY_COLORS, DEFAULT_EVENT_COLOR, getTaxonomyEntry, colorForKind, subKindsOf, type EventTaxonomyEntry, type EventTaxonomyCategory, type EventTaxonomyLevel, } from './catalogs/event-taxonomy.js';
123
123
  export { NcTaxonomyEntrySchema, NcTaxonomySchema, buildNcTaxonomy, NC_TAXONOMY, type NcTaxonomyEntry, type NcTaxonomy, } from './catalogs/nc-taxonomy.js';
124
+ export { DEVICE_STATE_READERS, readDeviceStateFrom, stateVocabularyFor, } from './catalogs/device-state-vocabulary.js';
124
125
  export type { CameraMotionConfig, CameraNativeDetectionConfig, CameraDetectionCapabilities, } from './types/camera-detection.js';
125
126
  export { type DeviceTypeInfo, DEVICE_TYPE_INFO } from './types/device-type.js';
126
127
  export type { DeviceBinding, DeviceBindingEntry } from './device/device-binding.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-CI7DYokZ.js");
2
+ const require_sleep = require("./sleep-BvSjpsfC.js");
3
3
  const require_event_category = require("./event-category-BE4PDZ_3.js");
4
4
  const require_enums = require("./enums.js");
5
5
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -2973,6 +2973,78 @@ function buildNcTaxonomy() {
2973
2973
  /** The frozen NC taxonomy, derived once from the taxonomy dictionary. */
2974
2974
  var NC_TAXONOMY = Object.freeze(buildNcTaxonomy());
2975
2975
  //#endregion
2976
+ //#region src/catalogs/device-state-vocabulary.ts
2977
+ /**
2978
+ * Most specific first. Extending this list is how a new device kind becomes
2979
+ * gateable; nothing else needs to change.
2980
+ */
2981
+ var DEVICE_STATE_READERS = [
2982
+ {
2983
+ cap: "alarm-panel",
2984
+ field: "state"
2985
+ },
2986
+ {
2987
+ cap: "cover",
2988
+ field: "state"
2989
+ },
2990
+ {
2991
+ cap: "presence",
2992
+ field: "state"
2993
+ },
2994
+ {
2995
+ cap: "lock",
2996
+ field: "locked",
2997
+ booleanWords: ["locked", "unlocked"]
2998
+ },
2999
+ {
3000
+ cap: "contact",
3001
+ field: "entryOpen",
3002
+ booleanWords: ["open", "closed"]
3003
+ },
3004
+ {
3005
+ cap: "switch",
3006
+ field: "on",
3007
+ booleanWords: ["on", "off"]
3008
+ },
3009
+ {
3010
+ cap: "binary",
3011
+ field: "on",
3012
+ booleanWords: ["on", "off"]
3013
+ }
3014
+ ];
3015
+ /**
3016
+ * Collapse a device's full runtime state to the one string a rule compares
3017
+ * against, or `undefined` when nothing in the table applies.
3018
+ *
3019
+ * `undefined` is the safe answer everywhere: the gate treats it as "does not
3020
+ * match", so a device whose kind we cannot read simply never arms a rule.
3021
+ */
3022
+ function readDeviceStateFrom(runtimeState) {
3023
+ for (const reader of DEVICE_STATE_READERS) {
3024
+ const slice = runtimeState[reader.cap];
3025
+ if (slice === null || typeof slice !== "object") continue;
3026
+ const value = slice[reader.field];
3027
+ if (typeof value === "string" && value.length > 0) return value;
3028
+ if (typeof value === "boolean" && reader.booleanWords !== void 0) return value ? reader.booleanWords[0] : reader.booleanWords[1];
3029
+ }
3030
+ }
3031
+ /**
3032
+ * The states a rule editor can offer for a device, given its runtime state.
3033
+ * Same table, same order — so the picker can never offer a value the gate
3034
+ * would not recognise.
3035
+ */
3036
+ function stateVocabularyFor(runtimeState, alarmStates = []) {
3037
+ for (const reader of DEVICE_STATE_READERS) {
3038
+ const slice = runtimeState[reader.cap];
3039
+ if (slice === null || typeof slice !== "object") continue;
3040
+ if (reader.booleanWords !== void 0) return [...reader.booleanWords];
3041
+ if (reader.cap === "alarm-panel") return [...alarmStates];
3042
+ const current = slice[reader.field];
3043
+ return typeof current === "string" ? [current] : [];
3044
+ }
3045
+ return [];
3046
+ }
3047
+ //#endregion
2976
3048
  //#region src/types/device-type.ts
2977
3049
  var DEVICE_TYPE_INFO = { ["camera"]: {
2978
3050
  type: "camera",
@@ -4784,7 +4856,24 @@ var NcZoneConditionSchema = zod.z.object({
4784
4856
  * The P1 condition set — a flat AND of groups; absent group = pass;
4785
4857
  * membership lists are OR within the list (spec §2.3).
4786
4858
  */
4859
+ /**
4860
+ * "This rule applies only while `deviceId` is in one of `states`."
4861
+ *
4862
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
4863
+ * `on`/`off` for a switch — not a normalised set, because normalising would
4864
+ * make the condition lie about devices whose states have no equivalent.
4865
+ *
4866
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
4867
+ * condition that fired on "I could not read it" would be worse than no gate.
4868
+ */
4869
+ var NcDeviceStateConditionSchema = zod.z.object({
4870
+ deviceId: zod.z.number().int(),
4871
+ /** Any of these matches. */
4872
+ states: zod.z.array(zod.z.string().min(1)).min(1)
4873
+ });
4787
4874
  var NcConditionsSchema = zod.z.object({
4875
+ /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
4876
+ deviceState: NcDeviceStateConditionSchema.optional(),
4788
4877
  /** Device scope — absent = all devices. */
4789
4878
  devices: zod.z.array(zod.z.number()).optional(),
4790
4879
  /** Detector class names (any overlap with the record's class set). */
@@ -4985,6 +5074,14 @@ var NcMediaPolicySchema = zod.z.object({
4985
5074
  clipPreRollSec: zod.z.number().int().min(0).max(30).optional(),
4986
5075
  clipPostRollSec: zod.z.number().int().min(0).max(30).optional(),
4987
5076
  /**
5077
+ * Playback rate of the attached gif / clip. Absent = 2x.
5078
+ *
5079
+ * A notification clip is GLANCED at on a lock screen, not watched: at real
5080
+ * time an eight-second passage is eight seconds of the recipient's attention
5081
+ * and twice the bytes. 1 is real time for the operator who wants it.
5082
+ */
5083
+ clipSpeed: zod.z.number().min(1).max(8).optional(),
5084
+ /**
4988
5085
  * Which stream profile the footage is cut from. Absent = the CHEAPEST
4989
5086
  * assigned profile: a notification is watched on a phone, so the 4K
4990
5087
  * rendition would burn CPU to produce a file the client downscales anyway.
@@ -5056,7 +5153,21 @@ var NcRuleInputSchema = zod.z.object({
5056
5153
  * behaviour, visible to all, read-only in the viewer). Present = personal
5057
5154
  * rule owned by this userId. Server-stamped; never trusted from a client.
5058
5155
  */
5059
- ownerUserId: zod.z.string().optional()
5156
+ ownerUserId: zod.z.string().optional(),
5157
+ /**
5158
+ * May a non-admin snooze this rule for EVERYONE, not just themselves?
5159
+ *
5160
+ * A snooze is personal by default — it silences the person who set it. This
5161
+ * opts THIS rule into the "the gardener is here all afternoon" case, where
5162
+ * silencing the camera for the whole household is legitimate. It silences
5163
+ * other people, so it is off unless a rule deliberately allows it.
5164
+ *
5165
+ * `.optional()`, deliberately NOT `.default()`: a Zod default does not run on
5166
+ * the addon cap path (three production failures in one day), so absent is
5167
+ * read as `false` by {@link canSetGlobal} in the engine. Admins are not bound
5168
+ * by this flag — see the scope rules on that function.
5169
+ */
5170
+ snoozeAllowGlobal: zod.z.boolean().optional()
5060
5171
  });
5061
5172
  /**
5062
5173
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -5127,7 +5238,8 @@ var NcConditionDescriptorSchema = zod.z.object({
5127
5238
  "packagePhase",
5128
5239
  "crossingSelect",
5129
5240
  "polygonDraw",
5130
- "occupancy"
5241
+ "occupancy",
5242
+ "deviceState"
5131
5243
  ]),
5132
5244
  operator: zod.z.enum([
5133
5245
  "in",
@@ -5141,7 +5253,28 @@ var NcConditionDescriptorSchema = zod.z.object({
5141
5253
  /** Which delivery kinds the condition applies to. */
5142
5254
  appliesTo: zod.z.array(NcDeliverySchema),
5143
5255
  phase: zod.z.string(),
5144
- description: zod.z.string().optional()
5256
+ description: zod.z.string().optional(),
5257
+ /**
5258
+ * The CHOICES for a single-choice widget (`sourceSelect`, `crossingSelect`,
5259
+ * `packagePhase`, …), served with the descriptor.
5260
+ *
5261
+ * Before this the descriptor said which widget to render and not what to put
5262
+ * in it, so every option list lived in three places: this file's enums, the
5263
+ * admin's `NC_*_OPTIONS` and the viewer's `NC_*_VALUES`. That triple mirror
5264
+ * is the drift that emptied the viewer's rule editor on 2026-08-04 — the app
5265
+ * mirrors the cap by hand, so it is only ever as current as its last build.
5266
+ *
5267
+ * With the options on the wire, a condition of an EXISTING `valueType` costs
5268
+ * zero client changes. Clients keep a local fallback for an older hub that
5269
+ * does not send them; absent here is "use your own list", not "no choices".
5270
+ */
5271
+ options: zod.z.array(zod.z.object({
5272
+ /** Written to the rule verbatim. `''` means the ABSENT state. */
5273
+ value: zod.z.string(),
5274
+ label: zod.z.string(),
5275
+ /** What THIS choice matches — shown one at a time, under the control. */
5276
+ hint: zod.z.string().optional()
5277
+ })).readonly().optional()
5145
5278
  });
5146
5279
  /**
5147
5280
  * The P1 condition surface as data — served by `getConditionCatalog` so
@@ -5235,6 +5368,28 @@ var NC_CONDITION_CATALOG = [
5235
5368
  group: "zones",
5236
5369
  label: "Zone crossing",
5237
5370
  valueType: "crossingSelect",
5371
+ options: [
5372
+ {
5373
+ value: "",
5374
+ label: "Not set — entering",
5375
+ hint: "Entries, and every detection that is not a crossing at all. Exits are excluded. This is what a rule without a crossing condition already does, and choosing it writes nothing to the rule."
5376
+ },
5377
+ {
5378
+ value: "enter",
5379
+ label: "Entered a zone",
5380
+ hint: "The same matches as \"not set\", stated explicitly: entries and non-crossing detections pass, exits do not."
5381
+ },
5382
+ {
5383
+ value: "exit",
5384
+ label: "Left a zone",
5385
+ hint: "ONLY an exit crossing. A detection that is not a crossing fails, so this rule cannot fire on ordinary movement — pair it with the zones the subject must have left. Also matches a tripwire crossed against its drawn direction."
5386
+ },
5387
+ {
5388
+ value: "any",
5389
+ label: "Entered or left",
5390
+ hint: "No direction filter: entries, exits and non-crossing detections alike. This is the only choice that adds exits to what the rule already matched."
5391
+ }
5392
+ ],
5238
5393
  operator: "in",
5239
5394
  appliesTo: ["immediate"],
5240
5395
  phase: "P1",
@@ -5315,6 +5470,24 @@ var NC_CONDITION_CATALOG = [
5315
5470
  group: "scope",
5316
5471
  label: "Detection source",
5317
5472
  valueType: "sourceSelect",
5473
+ options: [
5474
+ {
5475
+ value: "any",
5476
+ label: "Any source"
5477
+ },
5478
+ {
5479
+ value: "pipeline",
5480
+ label: "Pipeline"
5481
+ },
5482
+ {
5483
+ value: "onboard",
5484
+ label: "Onboard (camera)"
5485
+ },
5486
+ {
5487
+ value: "sensor",
5488
+ label: "Sensor"
5489
+ }
5490
+ ],
5318
5491
  operator: "in",
5319
5492
  appliesTo: [
5320
5493
  "immediate",
@@ -5325,6 +5498,21 @@ var NC_CONDITION_CATALOG = [
5325
5498
  phase: "P1",
5326
5499
  description: "pipeline / onboard / sensor; a record with no stamped source counts as pipeline."
5327
5500
  },
5501
+ {
5502
+ id: "deviceState",
5503
+ group: "scope",
5504
+ label: "Device state",
5505
+ valueType: "deviceState",
5506
+ operator: "in",
5507
+ appliesTo: [
5508
+ "immediate",
5509
+ "track-end",
5510
+ "device-event",
5511
+ "package-event"
5512
+ ],
5513
+ phase: "P2",
5514
+ description: "Only fire while another device is in one of the chosen states — the alarm armed, a switch on, a contact closed. A state that cannot be read does NOT fire."
5515
+ },
5328
5516
  {
5329
5517
  id: "sensorKinds",
5330
5518
  group: "device",
@@ -5350,6 +5538,20 @@ var NC_CONDITION_CATALOG = [
5350
5538
  group: "package",
5351
5539
  label: "Package phase",
5352
5540
  valueType: "packagePhase",
5541
+ options: [
5542
+ {
5543
+ value: "delivered",
5544
+ label: "Delivered"
5545
+ },
5546
+ {
5547
+ value: "picked-up",
5548
+ label: "Picked up"
5549
+ },
5550
+ {
5551
+ value: "both",
5552
+ label: "Both"
5553
+ }
5554
+ ],
5353
5555
  operator: "in",
5354
5556
  appliesTo: ["package-event"],
5355
5557
  phase: "P1",
@@ -5481,6 +5683,78 @@ var NcHistoryFilterSchema = zod.z.object({
5481
5683
  until: zod.z.number().optional(),
5482
5684
  limit: zod.z.number().int().min(1).max(500).default(100)
5483
5685
  });
5686
+ /**
5687
+ * What a snooze covers. Broader scopes win when several overlap, so one window
5688
+ * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
5689
+ * summarising the same silence.
5690
+ */
5691
+ var NcSnoozeScopeSchema = zod.z.enum([
5692
+ "rule",
5693
+ "device",
5694
+ "all"
5695
+ ]);
5696
+ /** Hard ceiling on a window (24h). A snooze that could not expire would be an
5697
+ * outage the operator asked for once and forgot. */
5698
+ var NC_SNOOZE_MAX_MINUTES = 1440;
5699
+ /**
5700
+ * Client-authored snooze. The server stamps `userId`, `startedAt` and
5701
+ * `expiresAt` — a DURATION is sent rather than an instant so a client with a
5702
+ * skewed clock cannot author a window that is already over, or never ends.
5703
+ */
5704
+ var NcSnoozeInputSchema = zod.z.object({
5705
+ scope: NcSnoozeScopeSchema,
5706
+ /** Required when `scope: 'rule'` — a scoped snooze with no id matches
5707
+ * NOTHING rather than degrading to "everything". */
5708
+ ruleId: zod.z.string().optional(),
5709
+ /** Required when `scope: 'device'`. */
5710
+ deviceId: zod.z.number().int().optional(),
5711
+ durationMinutes: zod.z.number().int().min(1).max(NC_SNOOZE_MAX_MINUTES),
5712
+ /**
5713
+ * Silence this for EVERY recipient, not just the caller. Permission is
5714
+ * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
5715
+ * broader scopes). Absent = personal.
5716
+ */
5717
+ global: zod.z.boolean().optional(),
5718
+ /**
5719
+ * Deliver a summary of what was suppressed when the window ends. Absent =
5720
+ * ON: someone silencing a nuisance camera wants it off, someone silencing a
5721
+ * SECURITY camera wants to know what they missed, and choosing "off" for
5722
+ * everybody is how a snooze becomes an outage. Resolved to a concrete
5723
+ * boolean by the server at create time — never left to a Zod default, which
5724
+ * does not run on the addon cap path.
5725
+ */
5726
+ summary: zod.z.boolean().optional()
5727
+ });
5728
+ /** A persisted snooze window. */
5729
+ var NcSnoozeSchema = zod.z.object({
5730
+ id: zod.z.string(),
5731
+ /** Who set it. Also who it silences, unless `global`. */
5732
+ userId: zod.z.string(),
5733
+ scope: NcSnoozeScopeSchema,
5734
+ ruleId: zod.z.string().optional(),
5735
+ deviceId: zod.z.number().int().optional(),
5736
+ startedAt: zod.z.number(),
5737
+ /** Exclusive: at exactly this instant the snooze is over. Expiry is a
5738
+ * COMPARISON, not a job — no sweeper can leave the operator silenced. */
5739
+ expiresAt: zod.z.number(),
5740
+ global: zod.z.boolean(),
5741
+ summary: zod.z.boolean(),
5742
+ /** When the end-of-window digest went out. Absent = not sent (yet, or the
5743
+ * window has not closed, or `summary` is false). */
5744
+ digestSentAt: zod.z.number().optional()
5745
+ });
5746
+ /** One line of an end-of-window digest: what one rule hid on one camera. */
5747
+ var NcSnoozeSuppressedSchema = zod.z.object({
5748
+ snoozeId: zod.z.string(),
5749
+ targetId: zod.z.string(),
5750
+ ruleId: zod.z.string(),
5751
+ ruleName: zod.z.string(),
5752
+ deviceId: zod.z.number().int(),
5753
+ /** How many notifications this snooze hid for that pair. */
5754
+ count: zod.z.number().int(),
5755
+ firstAt: zod.z.number(),
5756
+ lastAt: zod.z.number()
5757
+ });
5484
5758
  var notificationRulesCapability = {
5485
5759
  name: "notification-rules",
5486
5760
  scope: "system",
@@ -5547,7 +5821,36 @@ var notificationRulesCapability = {
5547
5821
  * P1 (no user dimension); the P2 viewer History screen adds per-caller
5548
5822
  * scoping on the same method.
5549
5823
  */
5550
- getHistory: require_sleep.method(zod.z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), zod.z.object({ entries: zod.z.array(NcHistoryEntrySchema) }), { auth: "admin" })
5824
+ getHistory: require_sleep.method(zod.z.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), zod.z.object({ entries: zod.z.array(NcHistoryEntrySchema) }), { auth: "admin" }),
5825
+ /**
5826
+ * Snooze windows currently in effect, plus any whose digest has not yet
5827
+ * gone out. `caller: 'required'`: a user sees their OWN windows and the
5828
+ * global ones that silence them, never another person's private silence.
5829
+ * An admin sees every window — a household-wide silence is exactly the
5830
+ * thing an operator has to be able to find and cancel.
5831
+ */
5832
+ listSnoozes: require_sleep.method(zod.z.object({}), zod.z.object({ snoozes: zod.z.array(NcSnoozeSchema) }), { caller: "required" }),
5833
+ /**
5834
+ * Open a window. `userId` is stamped from the caller; a GLOBAL window is
5835
+ * permission-checked server-side (the rule's `snoozeAllowGlobal` for a
5836
+ * rule-scoped one, admin for a camera or the whole feed) — a client asking
5837
+ * for one it may not have is rejected, never silently downgraded, because
5838
+ * a snooze the operator believes is in effect and is not is the same
5839
+ * failure as one they cannot end.
5840
+ */
5841
+ createSnooze: require_sleep.method(zod.z.object({ snooze: NcSnoozeInputSchema }), zod.z.object({ snooze: NcSnoozeSchema }), {
5842
+ kind: "mutation",
5843
+ caller: "required"
5844
+ }),
5845
+ /**
5846
+ * End a window NOW. The digest still goes out (what was hidden while it
5847
+ * was open is still what the operator missed); only the silence stops.
5848
+ * A user may cancel their own; an admin may cancel any.
5849
+ */
5850
+ cancelSnooze: require_sleep.method(zod.z.object({ snoozeId: zod.z.string() }), zod.z.object({ success: zod.z.literal(true) }), {
5851
+ kind: "mutation",
5852
+ caller: "required"
5853
+ })
5551
5854
  }
5552
5855
  };
5553
5856
  //#endregion
@@ -6656,7 +6959,15 @@ var streamBrokerCapability = {
6656
6959
  format: zod.z.enum(["gif", "mp4"]).default("gif"),
6657
6960
  maxWidth: zod.z.number().int().min(120).max(1920).default(480),
6658
6961
  /** GIF only — MP4 keeps the source cadence. */
6659
- fps: zod.z.number().int().min(1).max(15).default(5)
6962
+ fps: zod.z.number().int().min(1).max(15).default(5),
6963
+ /**
6964
+ * Playback rate. A notification clip is GLANCED at on a lock screen,
6965
+ * not watched, so 2x is the default: the recipient sees the whole
6966
+ * passage in half the time and the GIF is half the bytes. `1` is real
6967
+ * time. Applies to MP4 as well — the operator set a speed, not a GIF
6968
+ * speed.
6969
+ */
6970
+ speed: zod.z.number().min(1).max(8).default(2)
6660
6971
  }), zod.z.object({
6661
6972
  base64: zod.z.string(),
6662
6973
  mime: zod.z.string(),
@@ -14796,17 +15107,6 @@ function createSystemProxy(api) {
14796
15107
  deleteTarget: (input) => dispatch("notificationOutput", "deleteTarget", "mutation", input),
14797
15108
  setTargetEnabled: (input) => dispatch("notificationOutput", "setTargetEnabled", "mutation", input)
14798
15109
  },
14799
- notificationRules: {
14800
- listRules: (input) => dispatch("notificationRules", "listRules", "query", input),
14801
- getRule: (input) => dispatch("notificationRules", "getRule", "query", input),
14802
- createRule: (input) => dispatch("notificationRules", "createRule", "mutation", input),
14803
- updateRule: (input) => dispatch("notificationRules", "updateRule", "mutation", input),
14804
- deleteRule: (input) => dispatch("notificationRules", "deleteRule", "mutation", input),
14805
- setRuleEnabled: (input) => dispatch("notificationRules", "setRuleEnabled", "mutation", input),
14806
- testRule: (input) => dispatch("notificationRules", "testRule", "mutation", input),
14807
- getConditionCatalog: (input) => dispatch("notificationRules", "getConditionCatalog", "query", input),
14808
- getHistory: (input) => dispatch("notificationRules", "getHistory", "query", input)
14809
- },
14810
15110
  pipelineExecutor: {
14811
15111
  getAvailableEngines: (input) => dispatch("pipelineExecutor", "getAvailableEngines", "query", input),
14812
15112
  getSelectedEngine: (input) => dispatch("pipelineExecutor", "getSelectedEngine", "query", input),
@@ -30706,12 +31006,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
30706
31006
  addonId: null,
30707
31007
  access: "create"
30708
31008
  },
31009
+ "notificationRules.cancelSnooze": {
31010
+ capName: "notification-rules",
31011
+ capScope: "system",
31012
+ addonId: null,
31013
+ access: "create"
31014
+ },
30709
31015
  "notificationRules.createRule": {
30710
31016
  capName: "notification-rules",
30711
31017
  capScope: "system",
30712
31018
  addonId: null,
30713
31019
  access: "create"
30714
31020
  },
31021
+ "notificationRules.createSnooze": {
31022
+ capName: "notification-rules",
31023
+ capScope: "system",
31024
+ addonId: null,
31025
+ access: "create"
31026
+ },
30715
31027
  "notificationRules.deleteRule": {
30716
31028
  capName: "notification-rules",
30717
31029
  capScope: "system",
@@ -30742,6 +31054,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30742
31054
  addonId: null,
30743
31055
  access: "view"
30744
31056
  },
31057
+ "notificationRules.listSnoozes": {
31058
+ capName: "notification-rules",
31059
+ capScope: "system",
31060
+ addonId: null,
31061
+ access: "view"
31062
+ },
30745
31063
  "notificationRules.setRuleEnabled": {
30746
31064
  capName: "notification-rules",
30747
31065
  capScope: "system",
@@ -34072,6 +34390,7 @@ exports.DEVICE_BACKEND_TO_FORMAT = DEVICE_BACKEND_TO_FORMAT;
34072
34390
  exports.DEVICE_CAP_NAMES = DEVICE_CAP_NAMES;
34073
34391
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
34074
34392
  exports.DEVICE_SETTINGS_CONTRIBUTION_METHODS = require_sleep.DEVICE_SETTINGS_CONTRIBUTION_METHODS;
34393
+ exports.DEVICE_STATE_READERS = DEVICE_STATE_READERS;
34075
34394
  exports.DEVICE_STATUS_METHOD = require_sleep.DEVICE_STATUS_METHOD;
34076
34395
  exports.DEVICE_TYPE_CONTROL_KIND = DEVICE_TYPE_CONTROL_KIND;
34077
34396
  exports.DEVICE_TYPE_INFO = DEVICE_TYPE_INFO;
@@ -34270,6 +34589,7 @@ exports.NC_CONDITION_CATALOG = NC_CONDITION_CATALOG;
34270
34589
  exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
34271
34590
  exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
34272
34591
  exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
34592
+ exports.NC_SNOOZE_MAX_MINUTES = NC_SNOOZE_MAX_MINUTES;
34273
34593
  exports.NC_TAXONOMY = NC_TAXONOMY;
34274
34594
  exports.NativeCropBboxSchema = NativeCropBboxSchema;
34275
34595
  exports.NativeCropRefSchema = NativeCropRefSchema;
@@ -34282,6 +34602,7 @@ exports.NcConditionDescriptorSchema = NcConditionDescriptorSchema;
34282
34602
  exports.NcConditionsSchema = NcConditionsSchema;
34283
34603
  exports.NcCrossingSchema = NcCrossingSchema;
34284
34604
  exports.NcDeliverySchema = NcDeliverySchema;
34605
+ exports.NcDeviceStateConditionSchema = NcDeviceStateConditionSchema;
34285
34606
  exports.NcHistoryEntrySchema = NcHistoryEntrySchema;
34286
34607
  exports.NcHistoryFilterSchema = NcHistoryFilterSchema;
34287
34608
  exports.NcHistoryRecordKindSchema = NcHistoryRecordKindSchema;
@@ -34297,6 +34618,10 @@ exports.NcRuleSchema = NcRuleSchema;
34297
34618
  exports.NcRuleTargetSchema = NcRuleTargetSchema;
34298
34619
  exports.NcScheduleSchema = NcScheduleSchema;
34299
34620
  exports.NcScheduleWindowSchema = NcScheduleWindowSchema;
34621
+ exports.NcSnoozeInputSchema = NcSnoozeInputSchema;
34622
+ exports.NcSnoozeSchema = NcSnoozeSchema;
34623
+ exports.NcSnoozeScopeSchema = NcSnoozeScopeSchema;
34624
+ exports.NcSnoozeSuppressedSchema = NcSnoozeSuppressedSchema;
34300
34625
  exports.NcTaxonomyEntrySchema = NcTaxonomyEntrySchema;
34301
34626
  exports.NcTaxonomySchema = NcTaxonomySchema;
34302
34627
  exports.NcTestResultSchema = NcTestResultSchema;
@@ -34783,6 +35108,7 @@ exports.procedureAuthKey = procedureAuthKey;
34783
35108
  exports.ptzAutotrackCapability = ptzAutotrackCapability;
34784
35109
  exports.ptzCapability = ptzCapability;
34785
35110
  exports.pythonScriptForBackend = pythonScriptForBackend;
35111
+ exports.readDeviceStateFrom = readDeviceStateFrom;
34786
35112
  exports.readNodePin = require_sleep.readNodePin;
34787
35113
  exports.readinessKey = require_sleep.readinessKey;
34788
35114
  exports.rebootCapability = rebootCapability;
@@ -34822,6 +35148,7 @@ exports.smtpProviderCapability = smtpProviderCapability;
34822
35148
  exports.snapshotCapability = snapshotCapability;
34823
35149
  exports.ssoBridgeCapability = ssoBridgeCapability;
34824
35150
  exports.startReachabilityPoll = startReachabilityPoll;
35151
+ exports.stateVocabularyFor = stateVocabularyFor;
34825
35152
  exports.storageCapability = storageCapability;
34826
35153
  exports.storageEvictableCapability = storageEvictableCapability;
34827
35154
  exports.storageProviderCapability = storageProviderCapability;