@camstack/addon-osd-manager 0.1.7 → 0.1.9

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
@@ -11393,7 +11393,14 @@ var cameraStreamsCapability = {
11393
11393
  low: string().optional()
11394
11394
  }),
11395
11395
  lastChangedAt: number()
11396
- })
11396
+ }),
11397
+ /**
11398
+ * Runtime-state durability: **session** — a restored `slotStatuses: streaming` for a camera that has been dark for two hours is a lie the UI renders as truth.
11399
+ *
11400
+ * See `RuntimeStateDurability`. Enforced by
11401
+ * `scripts/check-runtime-state-durability.ts`.
11402
+ */
11403
+ durability: "session"
11397
11404
  };
11398
11405
  /** Where a block runs. The operator chooses — a block driving a device on an
11399
11406
  * agent is the reason placement is not fixed to the hub. */
@@ -12160,6 +12167,13 @@ var deviceDiscoveryCapability = {
12160
12167
  kind: "poll"
12161
12168
  },
12162
12169
  runtimeState: DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number().int().nonnegative() }),
12170
+ /**
12171
+ * Runtime-state durability: **session** — 5.7 KB of scan output on the largest device, fully re-derivable by re-scanning.
12172
+ *
12173
+ * See `RuntimeStateDurability`. Enforced by
12174
+ * `scripts/check-runtime-state-durability.ts`.
12175
+ */
12176
+ durability: "session",
12163
12177
  methods: {
12164
12178
  /**
12165
12179
  * Snapshot of the current `discovered` list. Returns the
@@ -14708,11 +14722,33 @@ var NotificationFormatSchema = _enum([
14708
14722
  * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
14709
14723
  * renderer's icon set; "acknowledge" survives an adapter that draws it
14710
14724
  * differently.
14725
+ *
14726
+ * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
14727
+ *
14728
+ * These names are for US. **No adapter may forward one verbatim.** Each maps
14729
+ * the whole set onto its own renderer's vocabulary through a
14730
+ * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
14731
+ * fallback, so adding a member here fails every adapter's build until someone
14732
+ * decides its glyph, which is the only place that decision can be made
14733
+ * honestly.
14734
+ *
14735
+ * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
14736
+ * `disarm` straight through; iOS feeds that string to
14737
+ * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
14738
+ * and every snooze and alarm button arrived BLANK. A pass-through is not a
14739
+ * mapping, and "the field is documented" is not "the value renders".
14740
+ *
14741
+ * Adding a member is TRAIN-BOUND. The enum lives in the published
14742
+ * `@camstack/server` closure and the cap seam validates against the HUB's copy,
14743
+ * so an addon that emits a token the running hub does not know does not lose an
14744
+ * icon — its whole `send` fails Zod validation and the notification never
14745
+ * arrives. Never emit a new token from an addon before the train carrying it.
14711
14746
  */
14712
14747
  var NotificationActionIconSchema = _enum([
14713
14748
  "acknowledge",
14714
14749
  "dismiss",
14715
14750
  "silence",
14751
+ "snooze",
14716
14752
  "view",
14717
14753
  "play",
14718
14754
  "open",
@@ -14720,9 +14756,13 @@ var NotificationActionIconSchema = _enum([
14720
14756
  "lock",
14721
14757
  "unlock",
14722
14758
  "arm",
14759
+ "arm-home",
14760
+ "arm-away",
14761
+ "arm-night",
14723
14762
  "disarm",
14724
14763
  "light",
14725
- "alert"
14764
+ "alert",
14765
+ "camera"
14726
14766
  ]);
14727
14767
  /** A single tap-through action button. */
14728
14768
  var NotificationActionSchema = object({
@@ -14738,7 +14778,23 @@ var NotificationActionSchema = object({
14738
14778
  * else — see `notification-center/action-token.ts` for what that does and
14739
14779
  * does not buy.
14740
14780
  */
14741
- destructive: boolean().optional()
14781
+ destructive: boolean().optional(),
14782
+ /**
14783
+ * How the tap should REACH the url.
14784
+ *
14785
+ * `navigate` (absent, and every button authored before this field) opens it:
14786
+ * the phone leaves the notification and shows whatever the callback returns.
14787
+ * That is right for a button whose answer the operator wants to read.
14788
+ *
14789
+ * `background` fires it as a POST and stays put. It exists for the buttons
14790
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
14791
+ * an answer to the notification, and being thrown into a browser tab to
14792
+ * confirm it costs more attention than the notification did. A backend that
14793
+ * cannot do a background call renders it as an ordinary link (the adapters
14794
+ * fall back rather than dropping the button), so this is a preference, never
14795
+ * a requirement.
14796
+ */
14797
+ mode: _enum(["navigate", "background"]).optional()
14742
14798
  });
14743
14799
  /**
14744
14800
  * The canonical notification. `body` is the only hard field (Apprise model).
@@ -14920,6 +14976,24 @@ var notificationOutputCapability = {
14920
14976
  }), _void(), { kind: "mutation" })
14921
14977
  }
14922
14978
  };
14979
+ new Set([
14980
+ {
14981
+ id: "person",
14982
+ name: "Person"
14983
+ },
14984
+ {
14985
+ id: "vehicle",
14986
+ name: "Vehicle"
14987
+ },
14988
+ {
14989
+ id: "animal",
14990
+ name: "Animal"
14991
+ },
14992
+ {
14993
+ id: "package",
14994
+ name: "Package"
14995
+ }
14996
+ ].map((l) => l.id));
14923
14997
  var COCO_TO_MACRO = {
14924
14998
  mapping: {
14925
14999
  person: "person",
@@ -15604,7 +15678,17 @@ var alarmPanelCapability = {
15604
15678
  * full slice; renders an arm button per `availableModes` entry and
15605
15679
  * a PIN field iff `requiresCode === true`.
15606
15680
  */
15607
- runtimeState: AlarmPanelStatusSchema
15681
+ runtimeState: AlarmPanelStatusSchema,
15682
+ /**
15683
+ * Runtime-state durability: **restored** — armed state is the one thing a panel must not lose across a restart.
15684
+ *
15685
+ * See `RuntimeStateDurability`. Enforced by
15686
+ * `scripts/check-runtime-state-durability.ts`.
15687
+ */
15688
+ durability: "restored",
15689
+ /** Clock fields: written, but excluded from the compare that decides
15690
+ * whether persisting is worth a SQLite commit. */
15691
+ volatileStateFields: ["lastChangedAt"]
15608
15692
  };
15609
15693
  /**
15610
15694
  * Shared geometry vocabulary for on-frame shape caps — privacy-mask,
@@ -15745,6 +15829,8 @@ var NcSystemEventKindSchema = _enum([
15745
15829
  "alarm-triggered",
15746
15830
  "alarm-armed",
15747
15831
  "alarm-disarmed",
15832
+ "alarm-arming",
15833
+ "alarm-arm-refused",
15748
15834
  "camera-online",
15749
15835
  "camera-offline",
15750
15836
  "camera-disabled",
@@ -15781,6 +15867,9 @@ var NcSystemEventConditionSchema = object({
15781
15867
  nodeIds: array(string().min(1)).min(1).optional(),
15782
15868
  packageNames: array(string().min(1)).min(1).optional()
15783
15869
  });
15870
+ /** Hard ceiling on a window (24h). A snooze that could not expire would be an
15871
+ * outage the operator asked for once and forgot. */
15872
+ var NC_SNOOZE_MAX_MINUTES = 1440;
15784
15873
  /** Weekly schedule — OR of windows; absence on the rule = always active. */
15785
15874
  var NcScheduleSchema = object({
15786
15875
  windows: array(object({
@@ -16157,15 +16246,15 @@ var NcConditionsSchema = object({
16157
16246
  * (an `immediate` rule naming an `audio-*` class, one notification per
16158
16247
  * classified sample) stays exactly as it was for rules that already use it.
16159
16248
  *
16160
- * NOT in {@link NC_CONDITION_CATALOG} yet, and that is the sequencing rule
16161
- * rather than an oversight: the viewer mirrors the descriptor enums BY HAND
16162
- * (`camstack/src/data/notification-center.ts`, guarded by
16163
- * `scripts/check-viewer-condition-mirror.ts`) and its rule editor STRIPS the
16249
+ * In {@link NC_CONDITION_CATALOG} since P2, and the ORDER it got there is the
16250
+ * rule rather than an accident: the viewer mirrors the descriptor enums BY
16251
+ * HAND (`camstack/src/data/notification-center.ts`, guarded by
16252
+ * `scripts/check-viewer-condition-mirror.ts`) and its rule editor strips the
16164
16253
  * condition fields it does not know when a rule is saved from the phone.
16165
16254
  * Publishing an editor for a condition the app cannot round-trip is how an
16166
- * operator loses a rule's conditions by opening it — so the descriptor, the
16167
- * admin widget and the viewer mirror land together (P2 + P3), and only then
16168
- * does an audio rule become authorable.
16255
+ * operator loses a rule's conditions by opening it — so the viewer mirror
16256
+ * (P3, shipped) went FIRST, and the descriptor an editor renders from
16257
+ * follows here.
16169
16258
  */
16170
16259
  audio: NcAudioConditionSchema.optional()
16171
16260
  });
@@ -16401,6 +16490,30 @@ var NcRuleInputSchema = object({
16401
16490
  */
16402
16491
  snoozeAllowGlobal: boolean().optional(),
16403
16492
  /**
16493
+ * The snooze durations THIS rule's notification offers as buttons, in
16494
+ * minutes.
16495
+ *
16496
+ * Three states, and all three are distinct — which is exactly why this is
16497
+ * `.optional()` and never `.default()`. A Zod default does not run on the
16498
+ * addon cap path (three production failures in one day), so a schema default
16499
+ * would collapse the first two:
16500
+ *
16501
+ * | value | meaning |
16502
+ * | --- | --- |
16503
+ * | absent | the operator never said ⇒ {@link NC_DEFAULT_SNOOZE_MINUTES} |
16504
+ * | `[]` | **no snooze buttons on this rule** — the explicit override |
16505
+ * | a list | these choices, de-duplicated and sorted, at most four |
16506
+ *
16507
+ * `.max(4)` because the notifier's own action budget is small (ntfy allows
16508
+ * three buttons in total) and a rule that spent it all on snooze choices
16509
+ * would push its own tap-through actions off the notification.
16510
+ *
16511
+ * An empty list is NOT an alarm exemption: a rule the alarm is about, or
16512
+ * that arms the panel, is exempt automatically and cannot be silenced by a
16513
+ * window from anywhere (D133).
16514
+ */
16515
+ snoozeOptions: array(number().int().min(1).max(NC_SNOOZE_MAX_MINUTES)).max(4).optional(),
16516
+ /**
16404
16517
  * Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
16405
16518
  *
16406
16519
  * This is what makes the rule set the alarm's trigger set without the alarm
@@ -16500,6 +16613,7 @@ var NcConditionDescriptorSchema = object({
16500
16613
  "device",
16501
16614
  "package",
16502
16615
  "occupancy",
16616
+ "audio",
16503
16617
  "system"
16504
16618
  ]),
16505
16619
  label: string(),
@@ -16518,6 +16632,7 @@ var NcConditionDescriptorSchema = object({
16518
16632
  "crossingSelect",
16519
16633
  "polygonDraw",
16520
16634
  "occupancy",
16635
+ "audio",
16521
16636
  "deviceState",
16522
16637
  "systemEvent"
16523
16638
  ]),
@@ -16608,13 +16723,17 @@ var NC_CONDITION_CATALOG = [
16608
16723
  label: "Server update available"
16609
16724
  },
16610
16725
  {
16611
- value: "alarm-triggered",
16612
- label: "Alarm triggered"
16726
+ value: "alarm-arming",
16727
+ label: "Alarm arming (exit delay)"
16613
16728
  },
16614
16729
  {
16615
16730
  value: "alarm-armed",
16616
16731
  label: "Alarm armed"
16617
16732
  },
16733
+ {
16734
+ value: "alarm-triggered",
16735
+ label: "Alarm triggered"
16736
+ },
16618
16737
  {
16619
16738
  value: "alarm-disarmed",
16620
16739
  label: "Alarm disarmed"
@@ -16915,6 +17034,16 @@ var NC_CONDITION_CATALOG = [
16915
17034
  phase: "P1",
16916
17035
  description: "ZoneAnalytics occupancy edge (optionally zone/class-scoped): count crosses the threshold and holds for sustainSeconds. Fail-closed on a missing snapshot."
16917
17036
  },
17037
+ {
17038
+ id: "audio",
17039
+ group: "audio",
17040
+ label: "Sound",
17041
+ valueType: "audio",
17042
+ operator: "anyOf",
17043
+ appliesTo: ["immediate"],
17044
+ phase: "P2",
17045
+ description: "Fires when at least hitPercent% of the samples in a samplingSeconds window are hits — a hit clears the dBFS floor AND carries one of the chosen sounds. Both filters are optional and independent, but naming NEITHER never matches: every sample would be a hit, so the engine refuses rather than notifying on silence. dBFS is negative-going (0 = full scale, -96 = silence)."
17046
+ },
16918
17047
  {
16919
17048
  id: "customZones",
16920
17049
  group: "zones",
@@ -17059,7 +17188,20 @@ var NcSnoozeInputSchema = object({
17059
17188
  ruleId: string().optional(),
17060
17189
  /** Required when `scope: 'device'`. */
17061
17190
  deviceId: number().int().optional(),
17062
- durationMinutes: number().int().min(1).max(1440),
17191
+ /**
17192
+ * Narrow the window to these subject classes — "the cat, not the person".
17193
+ *
17194
+ * ORTHOGONAL to `scope`, deliberately, and absent means EVERY class: that is
17195
+ * what every window authored before this field meant, so no persisted row
17196
+ * changes meaning and no client has to learn anything to keep working.
17197
+ *
17198
+ * It is what makes the window's real key `(deviceId, classes[])` and lets it
17199
+ * cross rules (D133): the operator points at a camera and a kind of thing,
17200
+ * not at whichever of their four rules happened to produce the notification
17201
+ * they are dismissing.
17202
+ */
17203
+ classes: array(string().min(1)).min(1).optional(),
17204
+ durationMinutes: number().int().min(1).max(NC_SNOOZE_MAX_MINUTES),
17063
17205
  /**
17064
17206
  * Silence this for EVERY recipient, not just the caller. Permission is
17065
17207
  * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
@@ -17084,6 +17226,10 @@ var NcSnoozeSchema = object({
17084
17226
  scope: NcSnoozeScopeSchema,
17085
17227
  ruleId: string().optional(),
17086
17228
  deviceId: number().int().optional(),
17229
+ /** Subject classes this window covers. ABSENT = every class — see
17230
+ * {@link NcSnoozeInputSchema.shape.classes}. Lives in the JSON blob and has
17231
+ * no SQLite column: nothing queries a window by class. */
17232
+ classes: array(string().min(1)).min(1).optional(),
17087
17233
  startedAt: number(),
17088
17234
  /** Exclusive: at exactly this instant the snooze is over. Expiry is a
17089
17235
  * COMPARISON, not a job — no sweeper can leave the operator silenced. */
@@ -19837,7 +19983,14 @@ var zonesCapability = {
19837
19983
  * handle. Slice shape is `{ zones: Zone[] }` so future extensions
19838
19984
  * (e.g. zone groupings) can sit alongside the polygon list.
19839
19985
  */
19840
- runtimeState: object({ zones: array(ZoneSchema).readonly() })
19986
+ runtimeState: object({ zones: array(ZoneSchema).readonly() }),
19987
+ /**
19988
+ * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
19989
+ *
19990
+ * See `RuntimeStateDurability`. Enforced by
19991
+ * `scripts/check-runtime-state-durability.ts`.
19992
+ */
19993
+ durability: "restored"
19841
19994
  };
19842
19995
  /**
19843
19996
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
@@ -20007,6 +20160,22 @@ var detectionFpsField = {
20007
20160
  default: 10,
20008
20161
  step: 1
20009
20162
  };
20163
+ /**
20164
+ * The occupancy re-check interval. DEFAULT 300 s (2026-08-13 — was 30 s).
20165
+ *
20166
+ * The recheck is now on by default (a parked car is invisible to occupancy
20167
+ * rules until the stationary registry has been rebuilt by motion, which after a
20168
+ * restart may be never on a quiet camera). Each cycle re-subscribes a detection
20169
+ * session — an RTSP re-dial — so the switch is only affordable at a WIDE
20170
+ * interval: 300 s is ~12 re-dials an hour per camera, against 120 at the old
20171
+ * 30 s. A parked car is therefore counted within 5 minutes of a restart.
20172
+ *
20173
+ * Why not wider: `max` is 300 and raising it is TRAIN-BOUND, not addon-bound —
20174
+ * the host validates `attachCamera` against ITS copy of this schema, so a
20175
+ * runner asked for 600 would be rejected by the hub until a `@camstack/server`
20176
+ * carrying the wider bound is installed everywhere. 300 is the widest value
20177
+ * that ships with an addon deploy.
20178
+ */
20010
20179
  var occupancyRecheckSecField = {
20011
20180
  min: 0,
20012
20181
  max: 300,
@@ -20208,15 +20377,21 @@ var RunnerCameraConfigSchema = object({
20208
20377
  */
20209
20378
  onboardMotionDrivesAnalyzer: boolean().default(true),
20210
20379
  /**
20211
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
20212
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec`
20213
- * this is off by default because the recheck re-subscribes a detection session
20214
- * every N seconds while `watching`, a major source of pull-decoder re-dial
20215
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
20380
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
20381
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
20216
20382
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
20217
20383
  * (and only render) when this is enabled.
20218
- */
20219
- occupancyRecheckEnabled: boolean().default(false),
20384
+ *
20385
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
20386
+ * recheck re-subscribes a detection session every N seconds while `watching`
20387
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
20388
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
20389
+ * object is counted only while the stationary registry holds it, and the
20390
+ * registry rebuilds from motion, so after a restart a parked car was invisible
20391
+ * to every occupancy rule until something moved in front of it. The churn is
20392
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
20393
+ */
20394
+ occupancyRecheckEnabled: boolean().default(true),
20220
20395
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
20221
20396
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
20222
20397
  /**
@@ -23779,7 +23954,17 @@ var airQualitySensorCapability = {
23779
23954
  schema: AirQualitySensorStatusSchema,
23780
23955
  kind: "push"
23781
23956
  },
23782
- runtimeState: AirQualitySensorStatusSchema
23957
+ runtimeState: AirQualitySensorStatusSchema,
23958
+ /**
23959
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
23960
+ *
23961
+ * See `RuntimeStateDurability`. Enforced by
23962
+ * `scripts/check-runtime-state-durability.ts`.
23963
+ */
23964
+ durability: "restored",
23965
+ /** Clock fields: written, but excluded from the compare that decides
23966
+ * whether persisting is worth a SQLite commit. */
23967
+ volatileStateFields: ["lastFetchedAt"]
23783
23968
  };
23784
23969
  /**
23785
23970
  * Ambient illuminance reading in lux. Drives Home Assistant `sensor`
@@ -23811,7 +23996,17 @@ var ambientLightSensorCapability = {
23811
23996
  schema: AmbientLightSensorStatusSchema,
23812
23997
  kind: "push"
23813
23998
  },
23814
- runtimeState: AmbientLightSensorStatusSchema
23999
+ runtimeState: AmbientLightSensorStatusSchema,
24000
+ /**
24001
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
24002
+ *
24003
+ * See `RuntimeStateDurability`. Enforced by
24004
+ * `scripts/check-runtime-state-durability.ts`.
24005
+ */
24006
+ durability: "restored",
24007
+ /** Clock fields: written, but excluded from the compare that decides
24008
+ * whether persisting is worth a SQLite commit. */
24009
+ volatileStateFields: ["lastFetchedAt"]
23815
24010
  };
23816
24011
  /**
23817
24012
  * Per-class audio metrics aggregated over a sliding window.
@@ -23929,7 +24124,14 @@ var audioMetricsCapability = {
23929
24124
  }), AudioMetricsHistorySchema)
23930
24125
  },
23931
24126
  /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
23932
- runtimeState: AudioMetricsSnapshotSchema
24127
+ runtimeState: AudioMetricsSnapshotSchema,
24128
+ /**
24129
+ * Runtime-state durability: **session** — 1 Hz per camera at the mirror and ~63 % of the fleet's whole runtime-state write rate. `avgDbfs` is a rolling 60 s mean that genuinely moves every second, so no equality fix reclaims it — and a two-hour-old dB reading rendered as current is worse than no reading.
24130
+ *
24131
+ * See `RuntimeStateDurability`. Enforced by
24132
+ * `scripts/check-runtime-state-durability.ts`.
24133
+ */
24134
+ durability: "session"
23933
24135
  };
23934
24136
  /**
23935
24137
  * Automation-control cap. Models HA `automation.*` entities on
@@ -23991,7 +24193,14 @@ var automationControlCapability = {
23991
24193
  * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
23992
24194
  * (badge) directly.
23993
24195
  */
23994
- runtimeState: AutomationControlStatusSchema
24196
+ runtimeState: AutomationControlStatusSchema,
24197
+ /**
24198
+ * Runtime-state durability: **session** — the authority for an automation being enabled is the automation store; a restored `isRunning` would be a lie. D62: one authority per switch.
24199
+ *
24200
+ * See `RuntimeStateDurability`. Enforced by
24201
+ * `scripts/check-runtime-state-durability.ts`.
24202
+ */
24203
+ durability: "session"
23995
24204
  };
23996
24205
  /**
23997
24206
  * Battery status snapshot. Emitted by providers whose device is
@@ -24097,7 +24306,17 @@ onStatusChanged: { data: object({
24097
24306
  * via `device.runtimeState.getCapState('battery')` regardless of
24098
24307
  * the underlying driver.
24099
24308
  */
24100
- runtimeState: BatteryStatusSchema
24309
+ runtimeState: BatteryStatusSchema,
24310
+ /**
24311
+ * Runtime-state durability: **restored** — a sleeping battery camera may not report for hours; the restored percentage is the only thing the UI and the sleep gate have. Zero churn once `lastUpdated` is excluded — 526 writes, 0 value changes, in 25 minutes.
24312
+ *
24313
+ * See `RuntimeStateDurability`. Enforced by
24314
+ * `scripts/check-runtime-state-durability.ts`.
24315
+ */
24316
+ durability: "restored",
24317
+ /** Clock fields: written, but excluded from the compare that decides
24318
+ * whether persisting is worth a SQLite commit. */
24319
+ volatileStateFields: ["lastUpdated"]
24101
24320
  };
24102
24321
  /**
24103
24322
  * Generic boolean sensor — last-resort fallback when no domain-
@@ -24126,7 +24345,17 @@ var binaryCapability = {
24126
24345
  schema: BinaryStatusSchema,
24127
24346
  kind: "push"
24128
24347
  },
24129
- runtimeState: BinaryStatusSchema
24348
+ runtimeState: BinaryStatusSchema,
24349
+ /**
24350
+ * Runtime-state durability: **restored** — transition-driven sensor state; the restored value gives the boot comparison.
24351
+ *
24352
+ * See `RuntimeStateDurability`. Enforced by
24353
+ * `scripts/check-runtime-state-durability.ts`.
24354
+ */
24355
+ durability: "restored",
24356
+ /** Clock fields: written, but excluded from the compare that decides
24357
+ * whether persisting is worth a SQLite commit. */
24358
+ volatileStateFields: ["lastChangedAt"]
24130
24359
  };
24131
24360
  /**
24132
24361
  * Dimmable-light brightness control. Co-exists with `switch` on the
@@ -24179,7 +24408,14 @@ onBrightnessChanged: { data: object({
24179
24408
  * by the kernel. Read via `device.state.brightness.value` so UI
24180
24409
  * sliders surface the current level without polling the provider.
24181
24410
  */
24182
- runtimeState: BrightnessStatusSchema
24411
+ runtimeState: BrightnessStatusSchema,
24412
+ /**
24413
+ * Runtime-state durability: **session** — live lamp state, re-published by the provider on connect.
24414
+ *
24415
+ * See `RuntimeStateDurability`. Enforced by
24416
+ * `scripts/check-runtime-state-durability.ts`.
24417
+ */
24418
+ durability: "session"
24183
24419
  };
24184
24420
  /**
24185
24421
  * button — device-scoped capability for HA `button.*` / `input_button.*`
@@ -24308,7 +24544,17 @@ var carbonMonoxideCapability = {
24308
24544
  schema: CarbonMonoxideStatusSchema,
24309
24545
  kind: "push"
24310
24546
  },
24311
- runtimeState: CarbonMonoxideStatusSchema
24547
+ runtimeState: CarbonMonoxideStatusSchema,
24548
+ /**
24549
+ * Runtime-state durability: **restored** — as `smoke`.
24550
+ *
24551
+ * See `RuntimeStateDurability`. Enforced by
24552
+ * `scripts/check-runtime-state-durability.ts`.
24553
+ */
24554
+ durability: "restored",
24555
+ /** Clock fields: written, but excluded from the compare that decides
24556
+ * whether persisting is worth a SQLite commit. */
24557
+ volatileStateFields: ["lastChangedAt"]
24312
24558
  };
24313
24559
  /**
24314
24560
  * HVAC / climate control cap. Models the full surface of a HA
@@ -24468,7 +24714,14 @@ var climateControlCapability = {
24468
24714
  * the full slice via `device.state.climate-control.value` and refresh
24469
24715
  * on every push without re-querying the provider.
24470
24716
  */
24471
- runtimeState: ClimateControlStatusSchema
24717
+ runtimeState: ClimateControlStatusSchema,
24718
+ /**
24719
+ * Runtime-state durability: **session** — as `brightness`; `currentTemp` moves continuously and is re-published on connect.
24720
+ *
24721
+ * See `RuntimeStateDurability`. Enforced by
24722
+ * `scripts/check-runtime-state-durability.ts`.
24723
+ */
24724
+ durability: "session"
24472
24725
  };
24473
24726
  /**
24474
24727
  * Color-light cap. Coexists with `switch` (on/off) and `brightness`
@@ -24578,7 +24831,14 @@ onColorChanged: { data: object({
24578
24831
  * kernel. Read via `device.state.color.value` so UI pickers surface
24579
24832
  * the current chromaticity without polling the provider.
24580
24833
  */
24581
- runtimeState: ColorStatusSchema
24834
+ runtimeState: ColorStatusSchema,
24835
+ /**
24836
+ * Runtime-state durability: **session** — as `brightness`.
24837
+ *
24838
+ * See `RuntimeStateDurability`. Enforced by
24839
+ * `scripts/check-runtime-state-durability.ts`.
24840
+ */
24841
+ durability: "session"
24582
24842
  };
24583
24843
  var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
24584
24844
  object({
@@ -24644,7 +24904,17 @@ var connectivityCapability = {
24644
24904
  schema: ConnectivityStatusSchema,
24645
24905
  kind: "push"
24646
24906
  },
24647
- runtimeState: ConnectivityStatusSchema
24907
+ runtimeState: ConnectivityStatusSchema,
24908
+ /**
24909
+ * Runtime-state durability: **restored** — same shape and same argument as `device-status`, for links rather than devices.
24910
+ *
24911
+ * See `RuntimeStateDurability`. Enforced by
24912
+ * `scripts/check-runtime-state-durability.ts`.
24913
+ */
24914
+ durability: "restored",
24915
+ /** Clock fields: written, but excluded from the compare that decides
24916
+ * whether persisting is worth a SQLite commit. */
24917
+ volatileStateFields: ["lastChangedAt"]
24648
24918
  };
24649
24919
  /**
24650
24920
  * Generic device-consumables capability — surfaces a device's
@@ -24726,7 +24996,14 @@ reset: method(object({
24726
24996
  }
24727
24997
  }
24728
24998
  },
24729
- runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() })
24999
+ runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() }),
25000
+ /**
25001
+ * Runtime-state durability: **session** — the authority is the appliance; the provider re-reads the whole item array on connect.
25002
+ *
25003
+ * See `RuntimeStateDurability`. Enforced by
25004
+ * `scripts/check-runtime-state-durability.ts`.
25005
+ */
25006
+ durability: "session"
24730
25007
  };
24731
25008
  /**
24732
25009
  * Door / window / opening / garage / valve contact sensor. Boolean
@@ -24757,7 +25034,17 @@ var contactCapability = {
24757
25034
  schema: ContactStatusSchema,
24758
25035
  kind: "push"
24759
25036
  },
24760
- runtimeState: ContactStatusSchema
25037
+ runtimeState: ContactStatusSchema,
25038
+ /**
25039
+ * Runtime-state durability: **restored** — a door left open across a restart must still read open.
25040
+ *
25041
+ * See `RuntimeStateDurability`. Enforced by
25042
+ * `scripts/check-runtime-state-durability.ts`.
25043
+ */
25044
+ durability: "restored",
25045
+ /** Clock fields: written, but excluded from the compare that decides
25046
+ * whether persisting is worth a SQLite commit. */
25047
+ volatileStateFields: ["lastChangedAt"]
24761
25048
  };
24762
25049
  /**
24763
25050
  * Status slice — flat object (the framework's `runtimeState` contract
@@ -24863,7 +25150,14 @@ var controlCapability = {
24863
25150
  * dropdown / text field / date picker) read the slice's discriminant
24864
25151
  * and value directly without polling the provider.
24865
25152
  */
24866
- runtimeState: ControlStatusSchema
25153
+ runtimeState: ControlStatusSchema,
25154
+ /**
25155
+ * Runtime-state durability: **session** — a generic control mirrors an external entity that re-publishes on connect; the options array is re-derived with it.
25156
+ *
25157
+ * See `RuntimeStateDurability`. Enforced by
25158
+ * `scripts/check-runtime-state-durability.ts`.
25159
+ */
25160
+ durability: "session"
24867
25161
  };
24868
25162
  var CoverStatusSchema = object({
24869
25163
  /** Lifecycle state of the cover. */
@@ -24924,7 +25218,17 @@ var coverCapability = {
24924
25218
  * Runtime-state slice — mirrored by the kernel. UI controls watch
24925
25219
  * the slice for live position changes during a move.
24926
25220
  */
24927
- runtimeState: CoverStatusSchema
25221
+ runtimeState: CoverStatusSchema,
25222
+ /**
25223
+ * Runtime-state durability: **restored** — position survives a restart on the device; the mirror should agree at boot rather than read blank.
25224
+ *
25225
+ * See `RuntimeStateDurability`. Enforced by
25226
+ * `scripts/check-runtime-state-durability.ts`.
25227
+ */
25228
+ durability: "restored",
25229
+ /** Clock fields: written, but excluded from the compare that decides
25230
+ * whether persisting is worth a SQLite commit. */
25231
+ volatileStateFields: ["lastChangedAt"]
24928
25232
  };
24929
25233
  /**
24930
25234
  * Vendor-neutral day/night (IR-cut) control — the per-camera config cap
@@ -25018,7 +25322,17 @@ var dayNightCapability = {
25018
25322
  schema: DayNightStatusSchema,
25019
25323
  kind: "poll"
25020
25324
  },
25021
- runtimeState: DayNightStatusSchema
25325
+ runtimeState: DayNightStatusSchema,
25326
+ /**
25327
+ * Runtime-state durability: **restored** — operator-set IR-cut behaviour; mutation-driven.
25328
+ *
25329
+ * See `RuntimeStateDurability`. Enforced by
25330
+ * `scripts/check-runtime-state-durability.ts`.
25331
+ */
25332
+ durability: "restored",
25333
+ /** Clock fields: written, but excluded from the compare that decides
25334
+ * whether persisting is worth a SQLite commit. */
25335
+ volatileStateFields: ["lastFetchedAt"]
25022
25336
  };
25023
25337
  /**
25024
25338
  * Generic device-level status snapshot. Auto-registered by `BaseDevice`
@@ -25065,7 +25379,17 @@ onStatusChanged: { data: object({
25065
25379
  schema: DeviceStatusSchema,
25066
25380
  kind: "push"
25067
25381
  },
25068
- runtimeState: DeviceStatusSchema
25382
+ runtimeState: DeviceStatusSchema,
25383
+ /**
25384
+ * Runtime-state durability: **restored** — the previous observation is what makes the first reading after a restart a COMPARISON instead of a phantom transition (D130). 32 real flips across 16 devices in 25 min — the busiest slice in the cold half.
25385
+ *
25386
+ * See `RuntimeStateDurability`. Enforced by
25387
+ * `scripts/check-runtime-state-durability.ts`.
25388
+ */
25389
+ durability: "restored",
25390
+ /** Clock fields: written, but excluded from the compare that decides
25391
+ * whether persisting is worth a SQLite commit. */
25392
+ volatileStateFields: ["lastChangedAt"]
25069
25393
  };
25070
25394
  /**
25071
25395
  * Doorbell button cap. Two kinds of providers coexist behind this cap
@@ -25127,7 +25451,14 @@ onPressed: { data: DoorbellPressEventSchema } },
25127
25451
  * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
25128
25452
  * without subscribing.
25129
25453
  */
25130
- runtimeState: DoorbellStatusSchema
25454
+ runtimeState: DoorbellStatusSchema,
25455
+ /**
25456
+ * Runtime-state durability: **restored** — a monotonic accumulator: the slice IS the record. `lastPressedAt` is content here, not a clock, so it is deliberately NOT volatile.
25457
+ *
25458
+ * See `RuntimeStateDurability`. Enforced by
25459
+ * `scripts/check-runtime-state-durability.ts`.
25460
+ */
25461
+ durability: "restored"
25131
25462
  };
25132
25463
  /**
25133
25464
  * Enum-state sensor — a string value picked from a finite option set.
@@ -25167,7 +25498,17 @@ var enumSensorCapability = {
25167
25498
  schema: EnumSensorStatusSchema,
25168
25499
  kind: "push"
25169
25500
  },
25170
- runtimeState: EnumSensorStatusSchema
25501
+ runtimeState: EnumSensorStatusSchema,
25502
+ /**
25503
+ * Runtime-state durability: **restored** — as `numeric-sensor`; 80 devices.
25504
+ *
25505
+ * See `RuntimeStateDurability`. Enforced by
25506
+ * `scripts/check-runtime-state-durability.ts`.
25507
+ */
25508
+ durability: "restored",
25509
+ /** Clock fields: written, but excluded from the compare that decides
25510
+ * whether persisting is worth a SQLite commit. */
25511
+ volatileStateFields: ["lastFetchedAt"]
25171
25512
  };
25172
25513
  /**
25173
25514
  * Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
@@ -25203,7 +25544,14 @@ var eventEmitterCapability = {
25203
25544
  schema: EventEmitterStatusSchema,
25204
25545
  kind: "push"
25205
25546
  },
25206
- runtimeState: EventEmitterStatusSchema
25547
+ runtimeState: EventEmitterStatusSchema,
25548
+ /**
25549
+ * Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
25550
+ *
25551
+ * See `RuntimeStateDurability`. Enforced by
25552
+ * `scripts/check-runtime-state-durability.ts`.
25553
+ */
25554
+ durability: "session"
25207
25555
  };
25208
25556
  var EventItemSchema = object({
25209
25557
  id: string(),
@@ -25520,7 +25868,14 @@ var fanControlCapability = {
25520
25868
  * Runtime-state slice — mirrored by the kernel. UI fan speed
25521
25869
  * sliders read `percentage` for live updates.
25522
25870
  */
25523
- runtimeState: FanControlStatusSchema
25871
+ runtimeState: FanControlStatusSchema,
25872
+ /**
25873
+ * Runtime-state durability: **session** — as `brightness`.
25874
+ *
25875
+ * See `RuntimeStateDurability`. Enforced by
25876
+ * `scripts/check-runtime-state-durability.ts`.
25877
+ */
25878
+ durability: "session"
25524
25879
  };
25525
25880
  /**
25526
25881
  * Per-device feature/identity probe slice. Holds the runtime-resolved
@@ -25596,7 +25951,14 @@ onProbeChanged: { data: object({
25596
25951
  schema: FeatureProbeStatusSchema,
25597
25952
  kind: "push"
25598
25953
  },
25599
- runtimeState: FeatureProbeStatusSchema
25954
+ runtimeState: FeatureProbeStatusSchema,
25955
+ /**
25956
+ * Runtime-state durability: **session** — per-session by definition — `lastProbedAt` means "this worker completed a probe THIS session", which is why the mirror seed already blanks it. Persisting it only creates something to blank.
25957
+ *
25958
+ * See `RuntimeStateDurability`. Enforced by
25959
+ * `scripts/check-runtime-state-durability.ts`.
25960
+ */
25961
+ durability: "session"
25600
25962
  };
25601
25963
  /**
25602
25964
  * Water leak / moisture sensor. Boolean "is liquid currently
@@ -25623,7 +25985,17 @@ var floodCapability = {
25623
25985
  schema: FloodStatusSchema,
25624
25986
  kind: "push"
25625
25987
  },
25626
- runtimeState: FloodStatusSchema
25988
+ runtimeState: FloodStatusSchema,
25989
+ /**
25990
+ * Runtime-state durability: **restored** — as `smoke`.
25991
+ *
25992
+ * See `RuntimeStateDurability`. Enforced by
25993
+ * `scripts/check-runtime-state-durability.ts`.
25994
+ */
25995
+ durability: "restored",
25996
+ /** Clock fields: written, but excluded from the compare that decides
25997
+ * whether persisting is worth a SQLite commit. */
25998
+ volatileStateFields: ["lastChangedAt"]
25627
25999
  };
25628
26000
  /**
25629
26001
  * Combustible-gas (LPG / methane / hydrogen) alarm sensor. Drives
@@ -25646,7 +26018,17 @@ var gasCapability = {
25646
26018
  schema: GasStatusSchema,
25647
26019
  kind: "push"
25648
26020
  },
25649
- runtimeState: GasStatusSchema
26021
+ runtimeState: GasStatusSchema,
26022
+ /**
26023
+ * Runtime-state durability: **restored** — as `smoke`.
26024
+ *
26025
+ * See `RuntimeStateDurability`. Enforced by
26026
+ * `scripts/check-runtime-state-durability.ts`.
26027
+ */
26028
+ durability: "restored",
26029
+ /** Clock fields: written, but excluded from the compare that decides
26030
+ * whether persisting is worth a SQLite commit. */
26031
+ volatileStateFields: ["lastChangedAt"]
25650
26032
  };
25651
26033
  /**
25652
26034
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
@@ -25722,7 +26104,14 @@ var humidifierCapability = {
25722
26104
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
25723
26105
  * slice for live humidity / mode changes.
25724
26106
  */
25725
- runtimeState: HumidifierStatusSchema
26107
+ runtimeState: HumidifierStatusSchema,
26108
+ /**
26109
+ * Runtime-state durability: **session** — as `climate-control`.
26110
+ *
26111
+ * See `RuntimeStateDurability`. Enforced by
26112
+ * `scripts/check-runtime-state-durability.ts`.
26113
+ */
26114
+ durability: "session"
25726
26115
  };
25727
26116
  /**
25728
26117
  * Single-metric humidity reading. Drives Home Assistant `sensor`
@@ -25758,7 +26147,17 @@ var humiditySensorCapability = {
25758
26147
  schema: HumiditySensorStatusSchema,
25759
26148
  kind: "push"
25760
26149
  },
25761
- runtimeState: HumiditySensorStatusSchema
26150
+ runtimeState: HumiditySensorStatusSchema,
26151
+ /**
26152
+ * Runtime-state durability: **restored** — as `numeric-sensor` (67 of 75 writes were the clock alone).
26153
+ *
26154
+ * See `RuntimeStateDurability`. Enforced by
26155
+ * `scripts/check-runtime-state-durability.ts`.
26156
+ */
26157
+ durability: "restored",
26158
+ /** Clock fields: written, but excluded from the compare that decides
26159
+ * whether persisting is worth a SQLite commit. */
26160
+ volatileStateFields: ["lastFetchedAt"]
25762
26161
  };
25763
26162
  /**
25764
26163
  * Image display cap. Models a single still image exposed by an integration —
@@ -25796,7 +26195,14 @@ var imageCapability = {
25796
26195
  * Runtime-state slice — mirrored by the kernel. The UI reads `url`
25797
26196
  * directly and renders the still image.
25798
26197
  */
25799
- runtimeState: ImageStatusSchema
26198
+ runtimeState: ImageStatusSchema,
26199
+ /**
26200
+ * Runtime-state durability: **session** — a snapshot URL is a session-scoped handle; a restored one points at nothing.
26201
+ *
26202
+ * See `RuntimeStateDurability`. Enforced by
26203
+ * `scripts/check-runtime-state-durability.ts`.
26204
+ */
26205
+ durability: "session"
25800
26206
  };
25801
26207
  /**
25802
26208
  * Vendor-neutral image / picture-adjustment cap — the per-camera config
@@ -25945,7 +26351,17 @@ var imageSettingsCapability = {
25945
26351
  schema: ImageSettingsStatusSchema,
25946
26352
  kind: "poll"
25947
26353
  },
25948
- runtimeState: ImageSettingsStatusSchema
26354
+ runtimeState: ImageSettingsStatusSchema,
26355
+ /**
26356
+ * Runtime-state durability: **restored** — operator-set camera imaging; mutation-driven.
26357
+ *
26358
+ * See `RuntimeStateDurability`. Enforced by
26359
+ * `scripts/check-runtime-state-durability.ts`.
26360
+ */
26361
+ durability: "restored",
26362
+ /** Clock fields: written, but excluded from the compare that decides
26363
+ * whether persisting is worth a SQLite commit. */
26364
+ volatileStateFields: ["lastFetchedAt"]
25949
26365
  };
25950
26366
  /**
25951
26367
  * integrations — system-scoped singleton capability for integration
@@ -26354,7 +26770,14 @@ var lawnMowerControlCapability = {
26354
26770
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
26355
26771
  * slice for live activity + battery changes.
26356
26772
  */
26357
- runtimeState: LawnMowerControlStatusSchema
26773
+ runtimeState: LawnMowerControlStatusSchema,
26774
+ /**
26775
+ * Runtime-state durability: **session** — as `vacuum-control`.
26776
+ *
26777
+ * See `RuntimeStateDurability`. Enforced by
26778
+ * `scripts/check-runtime-state-durability.ts`.
26779
+ */
26780
+ durability: "session"
26358
26781
  };
26359
26782
  /**
26360
26783
  * local-network — hub-only singleton.
@@ -26630,7 +27053,17 @@ var lockControlCapability = {
26630
27053
  * read `state` and disable themselves during `locking`/`unlocking`
26631
27054
  * transitions.
26632
27055
  */
26633
- runtimeState: LockControlStatusSchema
27056
+ runtimeState: LockControlStatusSchema,
27057
+ /**
27058
+ * Runtime-state durability: **restored** — a lock left locked must still read locked.
27059
+ *
27060
+ * See `RuntimeStateDurability`. Enforced by
27061
+ * `scripts/check-runtime-state-durability.ts`.
27062
+ */
27063
+ durability: "restored",
27064
+ /** Clock fields: written, but excluded from the compare that decides
27065
+ * whether persisting is worth a SQLite commit. */
27066
+ volatileStateFields: ["lastChangedAt"]
26634
27067
  };
26635
27068
  /**
26636
27069
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
@@ -26792,7 +27225,14 @@ var mediaPlayerCapability = {
26792
27225
  * full slice for live now-playing, volume, and progress updates
26793
27226
  * without polling.
26794
27227
  */
26795
- runtimeState: MediaPlayerStatusSchema
27228
+ runtimeState: MediaPlayerStatusSchema,
27229
+ /**
27230
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
27231
+ *
27232
+ * See `RuntimeStateDurability`. Enforced by
27233
+ * `scripts/check-runtime-state-durability.ts`.
27234
+ */
27235
+ durability: "session"
26796
27236
  };
26797
27237
  /**
26798
27238
  * mesh-network — collection cap for mesh-VPN providers.
@@ -27117,7 +27557,14 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
27117
27557
  * `device.state.motion.value`. Reads never invoke the provider, so
27118
27558
  * UIs and other addons can poll the cached state safely.
27119
27559
  */
27120
- runtimeState: MotionStatusSchema
27560
+ runtimeState: MotionStatusSchema,
27561
+ /**
27562
+ * Runtime-state durability: **session** — self-clearing by construction (`autoClearAfterMs`); a restored `detected: true` is a frozen event, and the next frame re-publishes the real one.
27563
+ *
27564
+ * See `RuntimeStateDurability`. Enforced by
27565
+ * `scripts/check-runtime-state-durability.ts`.
27566
+ */
27567
+ durability: "session"
27121
27568
  };
27122
27569
  /**
27123
27570
  * Motion-trigger toggle for accessory devices.
@@ -27182,7 +27629,14 @@ var motionTriggerCapability = {
27182
27629
  schema: MotionTriggerStatusSchema,
27183
27630
  kind: "command-driven"
27184
27631
  },
27185
- runtimeState: MotionTriggerRuntimeStateSchema
27632
+ runtimeState: MotionTriggerRuntimeStateSchema,
27633
+ /**
27634
+ * Runtime-state durability: **session** — the authority for motion-trigger enablement is the provider's own config; the slice is a mirror of it, re-published on connect.
27635
+ *
27636
+ * See `RuntimeStateDurability`. Enforced by
27637
+ * `scripts/check-runtime-state-durability.ts`.
27638
+ */
27639
+ durability: "session"
27186
27640
  };
27187
27641
  /**
27188
27642
  * Motion-zones share the same MaskShape vocabulary as privacy-mask — the
@@ -27249,7 +27703,17 @@ var motionZonesCapability = {
27249
27703
  schema: MotionZoneStatusSchema,
27250
27704
  kind: "poll"
27251
27705
  },
27252
- runtimeState: MotionZoneStatusSchema
27706
+ runtimeState: MotionZoneStatusSchema,
27707
+ /**
27708
+ * Runtime-state durability: **restored** — the 14 KB polygon list is the single largest slice on the fleet and has not changed since the operator drew it. Highest value per byte in the table.
27709
+ *
27710
+ * See `RuntimeStateDurability`. Enforced by
27711
+ * `scripts/check-runtime-state-durability.ts`.
27712
+ */
27713
+ durability: "restored",
27714
+ /** Clock fields: written, but excluded from the compare that decides
27715
+ * whether persisting is worth a SQLite commit. */
27716
+ volatileStateFields: ["lastFetchedAt"]
27253
27717
  };
27254
27718
  /**
27255
27719
  * On-camera AI object detection cap. Surfaces per-device the classes
@@ -27323,7 +27787,17 @@ var nativeObjectDetectionCapability = {
27323
27787
  schema: NativeObjectDetectionStatusSchema,
27324
27788
  kind: "push"
27325
27789
  },
27326
- runtimeState: NativeObjectDetectionRuntimeStateSchema
27790
+ runtimeState: NativeObjectDetectionRuntimeStateSchema,
27791
+ /**
27792
+ * Runtime-state durability: **restored** — `enabled` is an operator toggle on a camera whose refresh is a no-op and whose staleMs is Infinity. There is no hardware value to re-read — losing it loses the setting.
27793
+ *
27794
+ * See `RuntimeStateDurability`. Enforced by
27795
+ * `scripts/check-runtime-state-durability.ts`.
27796
+ */
27797
+ durability: "restored",
27798
+ /** Clock fields: written, but excluded from the compare that decides
27799
+ * whether persisting is worth a SQLite commit. */
27800
+ volatileStateFields: ["lastFetchedAt"]
27327
27801
  };
27328
27802
  /**
27329
27803
  * network-quality — system-scoped singleton capability tracking RTT,
@@ -27710,7 +28184,14 @@ onSent: { data: object({
27710
28184
  * form reads `supports` to gate optional fields; history pane reads
27711
28185
  * `lastSentAt` / `lastError` / `queueDepth`.
27712
28186
  */
27713
- runtimeState: NotifierStatusSchema
28187
+ runtimeState: NotifierStatusSchema,
28188
+ /**
28189
+ * Runtime-state durability: **session** — live queue depth and last-send state; a restored queue depth describes a queue that no longer exists.
28190
+ *
28191
+ * See `RuntimeStateDurability`. Enforced by
28192
+ * `scripts/check-runtime-state-durability.ts`.
28193
+ */
28194
+ durability: "session"
27714
28195
  };
27715
28196
  /**
27716
28197
  * Generic numeric sensor — last-resort fallback when no typed numeric
@@ -27753,7 +28234,17 @@ var numericSensorCapability = {
27753
28234
  schema: NumericSensorStatusSchema,
27754
28235
  kind: "push"
27755
28236
  },
27756
- runtimeState: NumericSensorStatusSchema
28237
+ runtimeState: NumericSensorStatusSchema,
28238
+ /**
28239
+ * Runtime-state durability: **restored** — polled value, low churn once the clock is excluded (251 of 669 writes were the clock alone); restoring it removes the cold window before the first poll.
28240
+ *
28241
+ * See `RuntimeStateDurability`. Enforced by
28242
+ * `scripts/check-runtime-state-durability.ts`.
28243
+ */
28244
+ durability: "restored",
28245
+ /** Clock fields: written, but excluded from the compare that decides
28246
+ * whether persisting is worth a SQLite commit. */
28247
+ volatileStateFields: ["lastFetchedAt"]
27757
28248
  };
27758
28249
  /**
27759
28250
  * Generic on-screen-display (video overlay) cap. Each camera exposes
@@ -28231,7 +28722,14 @@ var petFeederCapability = {
28231
28722
  * the full slice via `device.state.petFeeder.value` and refresh on
28232
28723
  * every poll without re-querying the provider.
28233
28724
  */
28234
- runtimeState: PetFeederStatusSchema
28725
+ runtimeState: PetFeederStatusSchema,
28726
+ /**
28727
+ * Runtime-state durability: **session** — live appliance state re-published on connect.
28728
+ *
28729
+ * See `RuntimeStateDurability`. Enforced by
28730
+ * `scripts/check-runtime-state-durability.ts`.
28731
+ */
28732
+ durability: "session"
28235
28733
  };
28236
28734
  var VehicleSchema = object({
28237
28735
  id: string(),
@@ -28587,7 +29085,17 @@ var powerMeterCapability = {
28587
29085
  schema: PowerMeterStatusSchema,
28588
29086
  kind: "push"
28589
29087
  },
28590
- runtimeState: PowerMeterStatusSchema
29088
+ runtimeState: PowerMeterStatusSchema,
29089
+ /**
29090
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
29091
+ *
29092
+ * See `RuntimeStateDurability`. Enforced by
29093
+ * `scripts/check-runtime-state-durability.ts`.
29094
+ */
29095
+ durability: "restored",
29096
+ /** Clock fields: written, but excluded from the compare that decides
29097
+ * whether persisting is worth a SQLite commit. */
29098
+ volatileStateFields: ["lastFetchedAt"]
28591
29099
  };
28592
29100
  /**
28593
29101
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -28644,7 +29152,17 @@ var presenceCapability = {
28644
29152
  * the map pin is rendered (use `DeviceFeature.PresenceGps` for the
28645
29153
  * pre-fetch fast-path check).
28646
29154
  */
28647
- runtimeState: PresenceStatusSchema
29155
+ runtimeState: PresenceStatusSchema,
29156
+ /**
29157
+ * Runtime-state durability: **restored** — occupancy-relevant: the restored state is what an occupancy rule compares the first post-restart observation against.
29158
+ *
29159
+ * See `RuntimeStateDurability`. Enforced by
29160
+ * `scripts/check-runtime-state-durability.ts`.
29161
+ */
29162
+ durability: "restored",
29163
+ /** Clock fields: written, but excluded from the compare that decides
29164
+ * whether persisting is worth a SQLite commit. */
29165
+ volatileStateFields: ["lastChangedAt"]
28648
29166
  };
28649
29167
  /**
28650
29168
  * Atmospheric pressure reading in hectopascals. Drives Home Assistant
@@ -28680,7 +29198,17 @@ var pressureSensorCapability = {
28680
29198
  schema: PressureSensorStatusSchema,
28681
29199
  kind: "push"
28682
29200
  },
28683
- runtimeState: PressureSensorStatusSchema
29201
+ runtimeState: PressureSensorStatusSchema,
29202
+ /**
29203
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
29204
+ *
29205
+ * See `RuntimeStateDurability`. Enforced by
29206
+ * `scripts/check-runtime-state-durability.ts`.
29207
+ */
29208
+ durability: "restored",
29209
+ /** Clock fields: written, but excluded from the compare that decides
29210
+ * whether persisting is worth a SQLite commit. */
29211
+ volatileStateFields: ["lastFetchedAt"]
28684
29212
  };
28685
29213
  /**
28686
29214
  * PRIVACY — what the camera deliberately does not capture. Two planes:
@@ -28810,7 +29338,17 @@ var privacyMaskCapability = {
28810
29338
  schema: PrivacyMaskStatusSchema,
28811
29339
  kind: "poll"
28812
29340
  },
28813
- runtimeState: PrivacyMaskStatusSchema
29341
+ runtimeState: PrivacyMaskStatusSchema,
29342
+ /**
29343
+ * Runtime-state durability: **restored** — operator-drawn regions, zero real churn — 22 writes in 25 minutes, every one of them the clock.
29344
+ *
29345
+ * See `RuntimeStateDurability`. Enforced by
29346
+ * `scripts/check-runtime-state-durability.ts`.
29347
+ */
29348
+ durability: "restored",
29349
+ /** Clock fields: written, but excluded from the compare that decides
29350
+ * whether persisting is worth a SQLite commit. */
29351
+ volatileStateFields: ["lastFetchedAt"]
28814
29352
  };
28815
29353
  var PtzPresetSchema = object({
28816
29354
  id: string(),
@@ -29018,7 +29556,14 @@ var ptzAutotrackCapability = {
29018
29556
  * fetch / cache / fallback logic out of the four cap methods —
29019
29557
  * they become trampolines over `runtimeState`.
29020
29558
  */
29021
- runtimeState: PtzAutotrackRuntimeStateSchema
29559
+ runtimeState: PtzAutotrackRuntimeStateSchema,
29560
+ /**
29561
+ * Runtime-state durability: **session** — mirrors the camera's own autotrack config, re-read on connect.
29562
+ *
29563
+ * See `RuntimeStateDurability`. Enforced by
29564
+ * `scripts/check-runtime-state-durability.ts`.
29565
+ */
29566
+ durability: "session"
29022
29567
  };
29023
29568
  /**
29024
29569
  * reboot — device-scoped capability for "soft" device reboots (firmware
@@ -29826,7 +30371,14 @@ var sceneMonitorCapability = {
29826
30371
  schema: SceneMonitorStatusSchema,
29827
30372
  kind: "push"
29828
30373
  },
29829
- runtimeState: SceneMonitorStatusSchema
30374
+ runtimeState: SceneMonitorStatusSchema,
30375
+ /**
30376
+ * Runtime-state durability: **session** — re-derived from the current scene on the next evaluation.
30377
+ *
30378
+ * See `RuntimeStateDurability`. Enforced by
30379
+ * `scripts/check-runtime-state-durability.ts`.
30380
+ */
30381
+ durability: "session"
29830
30382
  };
29831
30383
  /**
29832
30384
  * Per-stage gating mode applied to the zones a rule references.
@@ -29970,7 +30522,14 @@ var scriptRunnerCapability = {
29970
30522
  * `isRunning` to render a spinner during execution and surfaces
29971
30523
  * `lastError` / `lastRunSuccess` in the recent-runs panel.
29972
30524
  */
29973
- runtimeState: ScriptRunnerStatusSchema
30525
+ runtimeState: ScriptRunnerStatusSchema,
30526
+ /**
30527
+ * Runtime-state durability: **session** — a restored `isRunning: true` describes a process that died with the previous hub.
30528
+ *
30529
+ * See `RuntimeStateDurability`. Enforced by
30530
+ * `scripts/check-runtime-state-durability.ts`.
30531
+ */
30532
+ durability: "session"
29974
30533
  };
29975
30534
  /**
29976
30535
  * Smoke alarm sensor — boolean "is smoke currently detected" with
@@ -29997,7 +30556,17 @@ var smokeCapability = {
29997
30556
  schema: SmokeStatusSchema,
29998
30557
  kind: "push"
29999
30558
  },
30000
- runtimeState: SmokeStatusSchema
30559
+ runtimeState: SmokeStatusSchema,
30560
+ /**
30561
+ * Runtime-state durability: **restored** — a safety sensor must not read "clear" merely because the hub restarted.
30562
+ *
30563
+ * See `RuntimeStateDurability`. Enforced by
30564
+ * `scripts/check-runtime-state-durability.ts`.
30565
+ */
30566
+ durability: "restored",
30567
+ /** Clock fields: written, but excluded from the compare that decides
30568
+ * whether persisting is worth a SQLite commit. */
30569
+ volatileStateFields: ["lastChangedAt"]
30001
30570
  };
30002
30571
  /**
30003
30572
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -30183,7 +30752,17 @@ var streamParamsCapability = {
30183
30752
  schema: StreamParamsStatusSchema,
30184
30753
  kind: "poll"
30185
30754
  },
30186
- runtimeState: StreamParamsStatusSchema
30755
+ runtimeState: StreamParamsStatusSchema,
30756
+ /**
30757
+ * Runtime-state durability: **restored** — operator-set encoder profile; mutation-driven.
30758
+ *
30759
+ * See `RuntimeStateDurability`. Enforced by
30760
+ * `scripts/check-runtime-state-durability.ts`.
30761
+ */
30762
+ durability: "restored",
30763
+ /** Clock fields: written, but excluded from the compare that decides
30764
+ * whether persisting is worth a SQLite commit. */
30765
+ volatileStateFields: ["lastFetchedAt"]
30187
30766
  };
30188
30767
  /**
30189
30768
  * Generic on/off switch cap for accessory children (siren, floodlight,
@@ -30229,6 +30808,16 @@ var switchCapability = {
30229
30808
  * not need to re-query the provider after a setState mutation.
30230
30809
  */
30231
30810
  runtimeState: SwitchStatusSchema,
30811
+ /**
30812
+ * Runtime-state durability: **restored** — device state an operator reads as authoritative; 55 devices, transition-driven.
30813
+ *
30814
+ * See `RuntimeStateDurability`. Enforced by
30815
+ * `scripts/check-runtime-state-durability.ts`.
30816
+ */
30817
+ durability: "restored",
30818
+ /** Clock fields: written, but excluded from the compare that decides
30819
+ * whether persisting is worth a SQLite commit. */
30820
+ volatileStateFields: ["lastChangedAt"],
30232
30821
  settings: { bindings: [{
30233
30822
  kind: "scalar",
30234
30823
  statusPath: "on",
@@ -30323,7 +30912,17 @@ var tamperCapability = {
30323
30912
  schema: TamperStatusSchema,
30324
30913
  kind: "push"
30325
30914
  },
30326
- runtimeState: TamperStatusSchema
30915
+ runtimeState: TamperStatusSchema,
30916
+ /**
30917
+ * Runtime-state durability: **restored** — as `smoke`.
30918
+ *
30919
+ * See `RuntimeStateDurability`. Enforced by
30920
+ * `scripts/check-runtime-state-durability.ts`.
30921
+ */
30922
+ durability: "restored",
30923
+ /** Clock fields: written, but excluded from the compare that decides
30924
+ * whether persisting is worth a SQLite commit. */
30925
+ volatileStateFields: ["lastChangedAt"]
30327
30926
  };
30328
30927
  /**
30329
30928
  * Single-metric temperature reading. Drives Home Assistant `sensor`
@@ -30368,7 +30967,17 @@ var temperatureSensorCapability = {
30368
30967
  schema: TemperatureSensorStatusSchema,
30369
30968
  kind: "push"
30370
30969
  },
30371
- runtimeState: TemperatureSensorStatusSchema
30970
+ runtimeState: TemperatureSensorStatusSchema,
30971
+ /**
30972
+ * Runtime-state durability: **restored** — as `numeric-sensor` (69 of 125 writes were the clock alone).
30973
+ *
30974
+ * See `RuntimeStateDurability`. Enforced by
30975
+ * `scripts/check-runtime-state-durability.ts`.
30976
+ */
30977
+ durability: "restored",
30978
+ /** Clock fields: written, but excluded from the compare that decides
30979
+ * whether persisting is worth a SQLite commit. */
30980
+ volatileStateFields: ["lastFetchedAt"]
30372
30981
  };
30373
30982
  /**
30374
30983
  * toast — system-scoped singleton capability that streams toast
@@ -30444,7 +31053,14 @@ var updateCapability = {
30444
31053
  schema: UpdateStatusSchema,
30445
31054
  kind: "poll"
30446
31055
  },
30447
- runtimeState: UpdateStatusSchema
31056
+ runtimeState: UpdateStatusSchema,
31057
+ /**
31058
+ * Runtime-state durability: **session** — a restored `inProgress: true` describes an update that is no longer running; versions are re-probed at boot.
31059
+ *
31060
+ * See `RuntimeStateDurability`. Enforced by
31061
+ * `scripts/check-runtime-state-durability.ts`.
31062
+ */
31063
+ durability: "session"
30448
31064
  };
30449
31065
  var UserSummarySchema = object({
30450
31066
  id: string(),
@@ -30820,7 +31436,14 @@ var vacuumControlCapability = {
30820
31436
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30821
31437
  * slice for live state + battery + fan-speed changes.
30822
31438
  */
30823
- runtimeState: VacuumControlStatusSchema
31439
+ runtimeState: VacuumControlStatusSchema,
31440
+ /**
31441
+ * Runtime-state durability: **session** — as `media-player` — a restored `state: cleaning` is a robot that is not cleaning.
31442
+ *
31443
+ * See `RuntimeStateDurability`. Enforced by
31444
+ * `scripts/check-runtime-state-durability.ts`.
31445
+ */
31446
+ durability: "session"
30824
31447
  };
30825
31448
  var ValveStatusSchema = object({
30826
31449
  /** Lifecycle state of the valve. */
@@ -30872,7 +31495,14 @@ var valveCapability = {
30872
31495
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30873
31496
  * slice for live position changes during a move.
30874
31497
  */
30875
- runtimeState: ValveStatusSchema
31498
+ runtimeState: ValveStatusSchema,
31499
+ /**
31500
+ * Runtime-state durability: **session** — as `brightness`.
31501
+ *
31502
+ * See `RuntimeStateDurability`. Enforced by
31503
+ * `scripts/check-runtime-state-durability.ts`.
31504
+ */
31505
+ durability: "session"
30876
31506
  };
30877
31507
  /**
30878
31508
  * Vibration / shake / impact sensor. Drives Home Assistant
@@ -30894,7 +31524,17 @@ var vibrationCapability = {
30894
31524
  schema: VibrationStatusSchema,
30895
31525
  kind: "push"
30896
31526
  },
30897
- runtimeState: VibrationStatusSchema
31527
+ runtimeState: VibrationStatusSchema,
31528
+ /**
31529
+ * Runtime-state durability: **restored** — as `smoke`.
31530
+ *
31531
+ * See `RuntimeStateDurability`. Enforced by
31532
+ * `scripts/check-runtime-state-durability.ts`.
31533
+ */
31534
+ durability: "restored",
31535
+ /** Clock fields: written, but excluded from the compare that decides
31536
+ * whether persisting is worth a SQLite commit. */
31537
+ volatileStateFields: ["lastChangedAt"]
30898
31538
  };
30899
31539
  /**
30900
31540
  * Water heater / boiler cap. Models HA `water_heater.*` entities — a
@@ -30968,7 +31608,14 @@ var waterHeaterCapability = {
30968
31608
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30969
31609
  * slice for live temperature / mode / away changes.
30970
31610
  */
30971
- runtimeState: WaterHeaterStatusSchema
31611
+ runtimeState: WaterHeaterStatusSchema,
31612
+ /**
31613
+ * Runtime-state durability: **session** — as `climate-control`.
31614
+ *
31615
+ * See `RuntimeStateDurability`. Enforced by
31616
+ * `scripts/check-runtime-state-durability.ts`.
31617
+ */
31618
+ durability: "session"
30972
31619
  };
30973
31620
  /**
30974
31621
  * Weather provider cap. Models HA `weather.*` entities — a read-only
@@ -31027,7 +31674,14 @@ var weatherCapability = {
31027
31674
  * Runtime-state slice — mirrored by the kernel. The UI reads the
31028
31675
  * current conditions directly from the slice on each weather push.
31029
31676
  */
31030
- runtimeState: WeatherStatusSchema
31677
+ runtimeState: WeatherStatusSchema,
31678
+ /**
31679
+ * Runtime-state durability: **session** — a forecast is stale the moment the hub is down; the provider re-fetches on connect.
31680
+ *
31681
+ * See `RuntimeStateDurability`. Enforced by
31682
+ * `scripts/check-runtime-state-durability.ts`.
31683
+ */
31684
+ durability: "session"
31031
31685
  };
31032
31686
  /**
31033
31687
  * Per-zone occupancy aggregation produced by the analytics frame
@@ -31189,7 +31843,14 @@ var zoneAnalyticsCapability = {
31189
31843
  * automatically; the explicit `getCurrentSnapshot` cap method is
31190
31844
  * still useful for one-off polls without a subscription.
31191
31845
  */
31192
- runtimeState: CameraOccupancySnapshotSchema
31846
+ runtimeState: CameraOccupancySnapshotSchema,
31847
+ /**
31848
+ * Runtime-state durability: **session** — per-frame analytics; with `audio-metrics` it is ~90 % of the offered write rate. Re-derived on the next frame.
31849
+ *
31850
+ * See `RuntimeStateDurability`. Enforced by
31851
+ * `scripts/check-runtime-state-durability.ts`.
31852
+ */
31853
+ durability: "session"
31193
31854
  };
31194
31855
  /**
31195
31856
  * Stages a {@link ZoneRule} can apply to. Discriminator on the rules
@@ -31267,7 +31928,14 @@ var zoneRulesCapability = {
31267
31928
  motion: array(ZoneRuleSchema).readonly(),
31268
31929
  detection: array(ZoneRuleSchema).readonly(),
31269
31930
  package: array(ZoneRuleSchema).readonly()
31270
- })
31931
+ }),
31932
+ /**
31933
+ * Runtime-state durability: **restored** — operator intent, mutation-only, same argument as `zones`.
31934
+ *
31935
+ * See `RuntimeStateDurability`. Enforced by
31936
+ * `scripts/check-runtime-state-durability.ts`.
31937
+ */
31938
+ durability: "restored"
31271
31939
  };
31272
31940
  /**
31273
31941
  * Most specific first. Extending this list is how a new device kind becomes
@@ -37134,6 +37802,7 @@ Object.freeze({
37134
37802
  "network-access": "ingress",
37135
37803
  "smtp-provider": "email"
37136
37804
  });
37805
+ new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37137
37806
  new Set(["devices", "classes"]);
37138
37807
  var WEEKDAY_TO_DAY = {
37139
37808
  Sun: 0,