@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.mjs CHANGED
@@ -11389,7 +11389,14 @@ var cameraStreamsCapability = {
11389
11389
  low: string().optional()
11390
11390
  }),
11391
11391
  lastChangedAt: number()
11392
- })
11392
+ }),
11393
+ /**
11394
+ * 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.
11395
+ *
11396
+ * See `RuntimeStateDurability`. Enforced by
11397
+ * `scripts/check-runtime-state-durability.ts`.
11398
+ */
11399
+ durability: "session"
11393
11400
  };
11394
11401
  /** Where a block runs. The operator chooses — a block driving a device on an
11395
11402
  * agent is the reason placement is not fixed to the hub. */
@@ -12156,6 +12163,13 @@ var deviceDiscoveryCapability = {
12156
12163
  kind: "poll"
12157
12164
  },
12158
12165
  runtimeState: DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number().int().nonnegative() }),
12166
+ /**
12167
+ * Runtime-state durability: **session** — 5.7 KB of scan output on the largest device, fully re-derivable by re-scanning.
12168
+ *
12169
+ * See `RuntimeStateDurability`. Enforced by
12170
+ * `scripts/check-runtime-state-durability.ts`.
12171
+ */
12172
+ durability: "session",
12159
12173
  methods: {
12160
12174
  /**
12161
12175
  * Snapshot of the current `discovered` list. Returns the
@@ -14704,11 +14718,33 @@ var NotificationFormatSchema = _enum([
14704
14718
  * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
14705
14719
  * renderer's icon set; "acknowledge" survives an adapter that draws it
14706
14720
  * differently.
14721
+ *
14722
+ * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
14723
+ *
14724
+ * These names are for US. **No adapter may forward one verbatim.** Each maps
14725
+ * the whole set onto its own renderer's vocabulary through a
14726
+ * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
14727
+ * fallback, so adding a member here fails every adapter's build until someone
14728
+ * decides its glyph, which is the only place that decision can be made
14729
+ * honestly.
14730
+ *
14731
+ * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
14732
+ * `disarm` straight through; iOS feeds that string to
14733
+ * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
14734
+ * and every snooze and alarm button arrived BLANK. A pass-through is not a
14735
+ * mapping, and "the field is documented" is not "the value renders".
14736
+ *
14737
+ * Adding a member is TRAIN-BOUND. The enum lives in the published
14738
+ * `@camstack/server` closure and the cap seam validates against the HUB's copy,
14739
+ * so an addon that emits a token the running hub does not know does not lose an
14740
+ * icon — its whole `send` fails Zod validation and the notification never
14741
+ * arrives. Never emit a new token from an addon before the train carrying it.
14707
14742
  */
14708
14743
  var NotificationActionIconSchema = _enum([
14709
14744
  "acknowledge",
14710
14745
  "dismiss",
14711
14746
  "silence",
14747
+ "snooze",
14712
14748
  "view",
14713
14749
  "play",
14714
14750
  "open",
@@ -14716,9 +14752,13 @@ var NotificationActionIconSchema = _enum([
14716
14752
  "lock",
14717
14753
  "unlock",
14718
14754
  "arm",
14755
+ "arm-home",
14756
+ "arm-away",
14757
+ "arm-night",
14719
14758
  "disarm",
14720
14759
  "light",
14721
- "alert"
14760
+ "alert",
14761
+ "camera"
14722
14762
  ]);
14723
14763
  /** A single tap-through action button. */
14724
14764
  var NotificationActionSchema = object({
@@ -14734,7 +14774,23 @@ var NotificationActionSchema = object({
14734
14774
  * else — see `notification-center/action-token.ts` for what that does and
14735
14775
  * does not buy.
14736
14776
  */
14737
- destructive: boolean().optional()
14777
+ destructive: boolean().optional(),
14778
+ /**
14779
+ * How the tap should REACH the url.
14780
+ *
14781
+ * `navigate` (absent, and every button authored before this field) opens it:
14782
+ * the phone leaves the notification and shows whatever the callback returns.
14783
+ * That is right for a button whose answer the operator wants to read.
14784
+ *
14785
+ * `background` fires it as a POST and stays put. It exists for the buttons
14786
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
14787
+ * an answer to the notification, and being thrown into a browser tab to
14788
+ * confirm it costs more attention than the notification did. A backend that
14789
+ * cannot do a background call renders it as an ordinary link (the adapters
14790
+ * fall back rather than dropping the button), so this is a preference, never
14791
+ * a requirement.
14792
+ */
14793
+ mode: _enum(["navigate", "background"]).optional()
14738
14794
  });
14739
14795
  /**
14740
14796
  * The canonical notification. `body` is the only hard field (Apprise model).
@@ -14916,6 +14972,24 @@ var notificationOutputCapability = {
14916
14972
  }), _void(), { kind: "mutation" })
14917
14973
  }
14918
14974
  };
14975
+ new Set([
14976
+ {
14977
+ id: "person",
14978
+ name: "Person"
14979
+ },
14980
+ {
14981
+ id: "vehicle",
14982
+ name: "Vehicle"
14983
+ },
14984
+ {
14985
+ id: "animal",
14986
+ name: "Animal"
14987
+ },
14988
+ {
14989
+ id: "package",
14990
+ name: "Package"
14991
+ }
14992
+ ].map((l) => l.id));
14919
14993
  var COCO_TO_MACRO = {
14920
14994
  mapping: {
14921
14995
  person: "person",
@@ -15600,7 +15674,17 @@ var alarmPanelCapability = {
15600
15674
  * full slice; renders an arm button per `availableModes` entry and
15601
15675
  * a PIN field iff `requiresCode === true`.
15602
15676
  */
15603
- runtimeState: AlarmPanelStatusSchema
15677
+ runtimeState: AlarmPanelStatusSchema,
15678
+ /**
15679
+ * Runtime-state durability: **restored** — armed state is the one thing a panel must not lose across a restart.
15680
+ *
15681
+ * See `RuntimeStateDurability`. Enforced by
15682
+ * `scripts/check-runtime-state-durability.ts`.
15683
+ */
15684
+ durability: "restored",
15685
+ /** Clock fields: written, but excluded from the compare that decides
15686
+ * whether persisting is worth a SQLite commit. */
15687
+ volatileStateFields: ["lastChangedAt"]
15604
15688
  };
15605
15689
  /**
15606
15690
  * Shared geometry vocabulary for on-frame shape caps — privacy-mask,
@@ -15741,6 +15825,8 @@ var NcSystemEventKindSchema = _enum([
15741
15825
  "alarm-triggered",
15742
15826
  "alarm-armed",
15743
15827
  "alarm-disarmed",
15828
+ "alarm-arming",
15829
+ "alarm-arm-refused",
15744
15830
  "camera-online",
15745
15831
  "camera-offline",
15746
15832
  "camera-disabled",
@@ -15777,6 +15863,9 @@ var NcSystemEventConditionSchema = object({
15777
15863
  nodeIds: array(string().min(1)).min(1).optional(),
15778
15864
  packageNames: array(string().min(1)).min(1).optional()
15779
15865
  });
15866
+ /** Hard ceiling on a window (24h). A snooze that could not expire would be an
15867
+ * outage the operator asked for once and forgot. */
15868
+ var NC_SNOOZE_MAX_MINUTES = 1440;
15780
15869
  /** Weekly schedule — OR of windows; absence on the rule = always active. */
15781
15870
  var NcScheduleSchema = object({
15782
15871
  windows: array(object({
@@ -16153,15 +16242,15 @@ var NcConditionsSchema = object({
16153
16242
  * (an `immediate` rule naming an `audio-*` class, one notification per
16154
16243
  * classified sample) stays exactly as it was for rules that already use it.
16155
16244
  *
16156
- * NOT in {@link NC_CONDITION_CATALOG} yet, and that is the sequencing rule
16157
- * rather than an oversight: the viewer mirrors the descriptor enums BY HAND
16158
- * (`camstack/src/data/notification-center.ts`, guarded by
16159
- * `scripts/check-viewer-condition-mirror.ts`) and its rule editor STRIPS the
16245
+ * In {@link NC_CONDITION_CATALOG} since P2, and the ORDER it got there is the
16246
+ * rule rather than an accident: the viewer mirrors the descriptor enums BY
16247
+ * HAND (`camstack/src/data/notification-center.ts`, guarded by
16248
+ * `scripts/check-viewer-condition-mirror.ts`) and its rule editor strips the
16160
16249
  * condition fields it does not know when a rule is saved from the phone.
16161
16250
  * Publishing an editor for a condition the app cannot round-trip is how an
16162
- * operator loses a rule's conditions by opening it — so the descriptor, the
16163
- * admin widget and the viewer mirror land together (P2 + P3), and only then
16164
- * does an audio rule become authorable.
16251
+ * operator loses a rule's conditions by opening it — so the viewer mirror
16252
+ * (P3, shipped) went FIRST, and the descriptor an editor renders from
16253
+ * follows here.
16165
16254
  */
16166
16255
  audio: NcAudioConditionSchema.optional()
16167
16256
  });
@@ -16397,6 +16486,30 @@ var NcRuleInputSchema = object({
16397
16486
  */
16398
16487
  snoozeAllowGlobal: boolean().optional(),
16399
16488
  /**
16489
+ * The snooze durations THIS rule's notification offers as buttons, in
16490
+ * minutes.
16491
+ *
16492
+ * Three states, and all three are distinct — which is exactly why this is
16493
+ * `.optional()` and never `.default()`. A Zod default does not run on the
16494
+ * addon cap path (three production failures in one day), so a schema default
16495
+ * would collapse the first two:
16496
+ *
16497
+ * | value | meaning |
16498
+ * | --- | --- |
16499
+ * | absent | the operator never said ⇒ {@link NC_DEFAULT_SNOOZE_MINUTES} |
16500
+ * | `[]` | **no snooze buttons on this rule** — the explicit override |
16501
+ * | a list | these choices, de-duplicated and sorted, at most four |
16502
+ *
16503
+ * `.max(4)` because the notifier's own action budget is small (ntfy allows
16504
+ * three buttons in total) and a rule that spent it all on snooze choices
16505
+ * would push its own tap-through actions off the notification.
16506
+ *
16507
+ * An empty list is NOT an alarm exemption: a rule the alarm is about, or
16508
+ * that arms the panel, is exempt automatically and cannot be silenced by a
16509
+ * window from anywhere (D133).
16510
+ */
16511
+ snoozeOptions: array(number().int().min(1).max(NC_SNOOZE_MAX_MINUTES)).max(4).optional(),
16512
+ /**
16400
16513
  * Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
16401
16514
  *
16402
16515
  * This is what makes the rule set the alarm's trigger set without the alarm
@@ -16496,6 +16609,7 @@ var NcConditionDescriptorSchema = object({
16496
16609
  "device",
16497
16610
  "package",
16498
16611
  "occupancy",
16612
+ "audio",
16499
16613
  "system"
16500
16614
  ]),
16501
16615
  label: string(),
@@ -16514,6 +16628,7 @@ var NcConditionDescriptorSchema = object({
16514
16628
  "crossingSelect",
16515
16629
  "polygonDraw",
16516
16630
  "occupancy",
16631
+ "audio",
16517
16632
  "deviceState",
16518
16633
  "systemEvent"
16519
16634
  ]),
@@ -16604,13 +16719,17 @@ var NC_CONDITION_CATALOG = [
16604
16719
  label: "Server update available"
16605
16720
  },
16606
16721
  {
16607
- value: "alarm-triggered",
16608
- label: "Alarm triggered"
16722
+ value: "alarm-arming",
16723
+ label: "Alarm arming (exit delay)"
16609
16724
  },
16610
16725
  {
16611
16726
  value: "alarm-armed",
16612
16727
  label: "Alarm armed"
16613
16728
  },
16729
+ {
16730
+ value: "alarm-triggered",
16731
+ label: "Alarm triggered"
16732
+ },
16614
16733
  {
16615
16734
  value: "alarm-disarmed",
16616
16735
  label: "Alarm disarmed"
@@ -16911,6 +17030,16 @@ var NC_CONDITION_CATALOG = [
16911
17030
  phase: "P1",
16912
17031
  description: "ZoneAnalytics occupancy edge (optionally zone/class-scoped): count crosses the threshold and holds for sustainSeconds. Fail-closed on a missing snapshot."
16913
17032
  },
17033
+ {
17034
+ id: "audio",
17035
+ group: "audio",
17036
+ label: "Sound",
17037
+ valueType: "audio",
17038
+ operator: "anyOf",
17039
+ appliesTo: ["immediate"],
17040
+ phase: "P2",
17041
+ 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)."
17042
+ },
16914
17043
  {
16915
17044
  id: "customZones",
16916
17045
  group: "zones",
@@ -17055,7 +17184,20 @@ var NcSnoozeInputSchema = object({
17055
17184
  ruleId: string().optional(),
17056
17185
  /** Required when `scope: 'device'`. */
17057
17186
  deviceId: number().int().optional(),
17058
- durationMinutes: number().int().min(1).max(1440),
17187
+ /**
17188
+ * Narrow the window to these subject classes — "the cat, not the person".
17189
+ *
17190
+ * ORTHOGONAL to `scope`, deliberately, and absent means EVERY class: that is
17191
+ * what every window authored before this field meant, so no persisted row
17192
+ * changes meaning and no client has to learn anything to keep working.
17193
+ *
17194
+ * It is what makes the window's real key `(deviceId, classes[])` and lets it
17195
+ * cross rules (D133): the operator points at a camera and a kind of thing,
17196
+ * not at whichever of their four rules happened to produce the notification
17197
+ * they are dismissing.
17198
+ */
17199
+ classes: array(string().min(1)).min(1).optional(),
17200
+ durationMinutes: number().int().min(1).max(NC_SNOOZE_MAX_MINUTES),
17059
17201
  /**
17060
17202
  * Silence this for EVERY recipient, not just the caller. Permission is
17061
17203
  * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
@@ -17080,6 +17222,10 @@ var NcSnoozeSchema = object({
17080
17222
  scope: NcSnoozeScopeSchema,
17081
17223
  ruleId: string().optional(),
17082
17224
  deviceId: number().int().optional(),
17225
+ /** Subject classes this window covers. ABSENT = every class — see
17226
+ * {@link NcSnoozeInputSchema.shape.classes}. Lives in the JSON blob and has
17227
+ * no SQLite column: nothing queries a window by class. */
17228
+ classes: array(string().min(1)).min(1).optional(),
17083
17229
  startedAt: number(),
17084
17230
  /** Exclusive: at exactly this instant the snooze is over. Expiry is a
17085
17231
  * COMPARISON, not a job — no sweeper can leave the operator silenced. */
@@ -19833,7 +19979,14 @@ var zonesCapability = {
19833
19979
  * handle. Slice shape is `{ zones: Zone[] }` so future extensions
19834
19980
  * (e.g. zone groupings) can sit alongside the polygon list.
19835
19981
  */
19836
- runtimeState: object({ zones: array(ZoneSchema).readonly() })
19982
+ runtimeState: object({ zones: array(ZoneSchema).readonly() }),
19983
+ /**
19984
+ * 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.
19985
+ *
19986
+ * See `RuntimeStateDurability`. Enforced by
19987
+ * `scripts/check-runtime-state-durability.ts`.
19988
+ */
19989
+ durability: "restored"
19837
19990
  };
19838
19991
  /**
19839
19992
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
@@ -20003,6 +20156,22 @@ var detectionFpsField = {
20003
20156
  default: 10,
20004
20157
  step: 1
20005
20158
  };
20159
+ /**
20160
+ * The occupancy re-check interval. DEFAULT 300 s (2026-08-13 — was 30 s).
20161
+ *
20162
+ * The recheck is now on by default (a parked car is invisible to occupancy
20163
+ * rules until the stationary registry has been rebuilt by motion, which after a
20164
+ * restart may be never on a quiet camera). Each cycle re-subscribes a detection
20165
+ * session — an RTSP re-dial — so the switch is only affordable at a WIDE
20166
+ * interval: 300 s is ~12 re-dials an hour per camera, against 120 at the old
20167
+ * 30 s. A parked car is therefore counted within 5 minutes of a restart.
20168
+ *
20169
+ * Why not wider: `max` is 300 and raising it is TRAIN-BOUND, not addon-bound —
20170
+ * the host validates `attachCamera` against ITS copy of this schema, so a
20171
+ * runner asked for 600 would be rejected by the hub until a `@camstack/server`
20172
+ * carrying the wider bound is installed everywhere. 300 is the widest value
20173
+ * that ships with an addon deploy.
20174
+ */
20006
20175
  var occupancyRecheckSecField = {
20007
20176
  min: 0,
20008
20177
  max: 300,
@@ -20204,15 +20373,21 @@ var RunnerCameraConfigSchema = object({
20204
20373
  */
20205
20374
  onboardMotionDrivesAnalyzer: boolean().default(true),
20206
20375
  /**
20207
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
20208
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec`
20209
- * this is off by default because the recheck re-subscribes a detection session
20210
- * every N seconds while `watching`, a major source of pull-decoder re-dial
20211
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
20376
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
20377
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
20212
20378
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
20213
20379
  * (and only render) when this is enabled.
20214
- */
20215
- occupancyRecheckEnabled: boolean().default(false),
20380
+ *
20381
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
20382
+ * recheck re-subscribes a detection session every N seconds while `watching`
20383
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
20384
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
20385
+ * object is counted only while the stationary registry holds it, and the
20386
+ * registry rebuilds from motion, so after a restart a parked car was invisible
20387
+ * to every occupancy rule until something moved in front of it. The churn is
20388
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
20389
+ */
20390
+ occupancyRecheckEnabled: boolean().default(true),
20216
20391
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
20217
20392
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
20218
20393
  /**
@@ -23775,7 +23950,17 @@ var airQualitySensorCapability = {
23775
23950
  schema: AirQualitySensorStatusSchema,
23776
23951
  kind: "push"
23777
23952
  },
23778
- runtimeState: AirQualitySensorStatusSchema
23953
+ runtimeState: AirQualitySensorStatusSchema,
23954
+ /**
23955
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
23956
+ *
23957
+ * See `RuntimeStateDurability`. Enforced by
23958
+ * `scripts/check-runtime-state-durability.ts`.
23959
+ */
23960
+ durability: "restored",
23961
+ /** Clock fields: written, but excluded from the compare that decides
23962
+ * whether persisting is worth a SQLite commit. */
23963
+ volatileStateFields: ["lastFetchedAt"]
23779
23964
  };
23780
23965
  /**
23781
23966
  * Ambient illuminance reading in lux. Drives Home Assistant `sensor`
@@ -23807,7 +23992,17 @@ var ambientLightSensorCapability = {
23807
23992
  schema: AmbientLightSensorStatusSchema,
23808
23993
  kind: "push"
23809
23994
  },
23810
- runtimeState: AmbientLightSensorStatusSchema
23995
+ runtimeState: AmbientLightSensorStatusSchema,
23996
+ /**
23997
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
23998
+ *
23999
+ * See `RuntimeStateDurability`. Enforced by
24000
+ * `scripts/check-runtime-state-durability.ts`.
24001
+ */
24002
+ durability: "restored",
24003
+ /** Clock fields: written, but excluded from the compare that decides
24004
+ * whether persisting is worth a SQLite commit. */
24005
+ volatileStateFields: ["lastFetchedAt"]
23811
24006
  };
23812
24007
  /**
23813
24008
  * Per-class audio metrics aggregated over a sliding window.
@@ -23925,7 +24120,14 @@ var audioMetricsCapability = {
23925
24120
  }), AudioMetricsHistorySchema)
23926
24121
  },
23927
24122
  /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
23928
- runtimeState: AudioMetricsSnapshotSchema
24123
+ runtimeState: AudioMetricsSnapshotSchema,
24124
+ /**
24125
+ * 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.
24126
+ *
24127
+ * See `RuntimeStateDurability`. Enforced by
24128
+ * `scripts/check-runtime-state-durability.ts`.
24129
+ */
24130
+ durability: "session"
23929
24131
  };
23930
24132
  /**
23931
24133
  * Automation-control cap. Models HA `automation.*` entities on
@@ -23987,7 +24189,14 @@ var automationControlCapability = {
23987
24189
  * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
23988
24190
  * (badge) directly.
23989
24191
  */
23990
- runtimeState: AutomationControlStatusSchema
24192
+ runtimeState: AutomationControlStatusSchema,
24193
+ /**
24194
+ * 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.
24195
+ *
24196
+ * See `RuntimeStateDurability`. Enforced by
24197
+ * `scripts/check-runtime-state-durability.ts`.
24198
+ */
24199
+ durability: "session"
23991
24200
  };
23992
24201
  /**
23993
24202
  * Battery status snapshot. Emitted by providers whose device is
@@ -24093,7 +24302,17 @@ onStatusChanged: { data: object({
24093
24302
  * via `device.runtimeState.getCapState('battery')` regardless of
24094
24303
  * the underlying driver.
24095
24304
  */
24096
- runtimeState: BatteryStatusSchema
24305
+ runtimeState: BatteryStatusSchema,
24306
+ /**
24307
+ * 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.
24308
+ *
24309
+ * See `RuntimeStateDurability`. Enforced by
24310
+ * `scripts/check-runtime-state-durability.ts`.
24311
+ */
24312
+ durability: "restored",
24313
+ /** Clock fields: written, but excluded from the compare that decides
24314
+ * whether persisting is worth a SQLite commit. */
24315
+ volatileStateFields: ["lastUpdated"]
24097
24316
  };
24098
24317
  /**
24099
24318
  * Generic boolean sensor — last-resort fallback when no domain-
@@ -24122,7 +24341,17 @@ var binaryCapability = {
24122
24341
  schema: BinaryStatusSchema,
24123
24342
  kind: "push"
24124
24343
  },
24125
- runtimeState: BinaryStatusSchema
24344
+ runtimeState: BinaryStatusSchema,
24345
+ /**
24346
+ * Runtime-state durability: **restored** — transition-driven sensor state; the restored value gives the boot comparison.
24347
+ *
24348
+ * See `RuntimeStateDurability`. Enforced by
24349
+ * `scripts/check-runtime-state-durability.ts`.
24350
+ */
24351
+ durability: "restored",
24352
+ /** Clock fields: written, but excluded from the compare that decides
24353
+ * whether persisting is worth a SQLite commit. */
24354
+ volatileStateFields: ["lastChangedAt"]
24126
24355
  };
24127
24356
  /**
24128
24357
  * Dimmable-light brightness control. Co-exists with `switch` on the
@@ -24175,7 +24404,14 @@ onBrightnessChanged: { data: object({
24175
24404
  * by the kernel. Read via `device.state.brightness.value` so UI
24176
24405
  * sliders surface the current level without polling the provider.
24177
24406
  */
24178
- runtimeState: BrightnessStatusSchema
24407
+ runtimeState: BrightnessStatusSchema,
24408
+ /**
24409
+ * Runtime-state durability: **session** — live lamp state, re-published by the provider on connect.
24410
+ *
24411
+ * See `RuntimeStateDurability`. Enforced by
24412
+ * `scripts/check-runtime-state-durability.ts`.
24413
+ */
24414
+ durability: "session"
24179
24415
  };
24180
24416
  /**
24181
24417
  * button — device-scoped capability for HA `button.*` / `input_button.*`
@@ -24304,7 +24540,17 @@ var carbonMonoxideCapability = {
24304
24540
  schema: CarbonMonoxideStatusSchema,
24305
24541
  kind: "push"
24306
24542
  },
24307
- runtimeState: CarbonMonoxideStatusSchema
24543
+ runtimeState: CarbonMonoxideStatusSchema,
24544
+ /**
24545
+ * Runtime-state durability: **restored** — as `smoke`.
24546
+ *
24547
+ * See `RuntimeStateDurability`. Enforced by
24548
+ * `scripts/check-runtime-state-durability.ts`.
24549
+ */
24550
+ durability: "restored",
24551
+ /** Clock fields: written, but excluded from the compare that decides
24552
+ * whether persisting is worth a SQLite commit. */
24553
+ volatileStateFields: ["lastChangedAt"]
24308
24554
  };
24309
24555
  /**
24310
24556
  * HVAC / climate control cap. Models the full surface of a HA
@@ -24464,7 +24710,14 @@ var climateControlCapability = {
24464
24710
  * the full slice via `device.state.climate-control.value` and refresh
24465
24711
  * on every push without re-querying the provider.
24466
24712
  */
24467
- runtimeState: ClimateControlStatusSchema
24713
+ runtimeState: ClimateControlStatusSchema,
24714
+ /**
24715
+ * Runtime-state durability: **session** — as `brightness`; `currentTemp` moves continuously and is re-published on connect.
24716
+ *
24717
+ * See `RuntimeStateDurability`. Enforced by
24718
+ * `scripts/check-runtime-state-durability.ts`.
24719
+ */
24720
+ durability: "session"
24468
24721
  };
24469
24722
  /**
24470
24723
  * Color-light cap. Coexists with `switch` (on/off) and `brightness`
@@ -24574,7 +24827,14 @@ onColorChanged: { data: object({
24574
24827
  * kernel. Read via `device.state.color.value` so UI pickers surface
24575
24828
  * the current chromaticity without polling the provider.
24576
24829
  */
24577
- runtimeState: ColorStatusSchema
24830
+ runtimeState: ColorStatusSchema,
24831
+ /**
24832
+ * Runtime-state durability: **session** — as `brightness`.
24833
+ *
24834
+ * See `RuntimeStateDurability`. Enforced by
24835
+ * `scripts/check-runtime-state-durability.ts`.
24836
+ */
24837
+ durability: "session"
24578
24838
  };
24579
24839
  var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
24580
24840
  object({
@@ -24640,7 +24900,17 @@ var connectivityCapability = {
24640
24900
  schema: ConnectivityStatusSchema,
24641
24901
  kind: "push"
24642
24902
  },
24643
- runtimeState: ConnectivityStatusSchema
24903
+ runtimeState: ConnectivityStatusSchema,
24904
+ /**
24905
+ * Runtime-state durability: **restored** — same shape and same argument as `device-status`, for links rather than devices.
24906
+ *
24907
+ * See `RuntimeStateDurability`. Enforced by
24908
+ * `scripts/check-runtime-state-durability.ts`.
24909
+ */
24910
+ durability: "restored",
24911
+ /** Clock fields: written, but excluded from the compare that decides
24912
+ * whether persisting is worth a SQLite commit. */
24913
+ volatileStateFields: ["lastChangedAt"]
24644
24914
  };
24645
24915
  /**
24646
24916
  * Generic device-consumables capability — surfaces a device's
@@ -24722,7 +24992,14 @@ reset: method(object({
24722
24992
  }
24723
24993
  }
24724
24994
  },
24725
- runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() })
24995
+ runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() }),
24996
+ /**
24997
+ * Runtime-state durability: **session** — the authority is the appliance; the provider re-reads the whole item array on connect.
24998
+ *
24999
+ * See `RuntimeStateDurability`. Enforced by
25000
+ * `scripts/check-runtime-state-durability.ts`.
25001
+ */
25002
+ durability: "session"
24726
25003
  };
24727
25004
  /**
24728
25005
  * Door / window / opening / garage / valve contact sensor. Boolean
@@ -24753,7 +25030,17 @@ var contactCapability = {
24753
25030
  schema: ContactStatusSchema,
24754
25031
  kind: "push"
24755
25032
  },
24756
- runtimeState: ContactStatusSchema
25033
+ runtimeState: ContactStatusSchema,
25034
+ /**
25035
+ * Runtime-state durability: **restored** — a door left open across a restart must still read open.
25036
+ *
25037
+ * See `RuntimeStateDurability`. Enforced by
25038
+ * `scripts/check-runtime-state-durability.ts`.
25039
+ */
25040
+ durability: "restored",
25041
+ /** Clock fields: written, but excluded from the compare that decides
25042
+ * whether persisting is worth a SQLite commit. */
25043
+ volatileStateFields: ["lastChangedAt"]
24757
25044
  };
24758
25045
  /**
24759
25046
  * Status slice — flat object (the framework's `runtimeState` contract
@@ -24859,7 +25146,14 @@ var controlCapability = {
24859
25146
  * dropdown / text field / date picker) read the slice's discriminant
24860
25147
  * and value directly without polling the provider.
24861
25148
  */
24862
- runtimeState: ControlStatusSchema
25149
+ runtimeState: ControlStatusSchema,
25150
+ /**
25151
+ * Runtime-state durability: **session** — a generic control mirrors an external entity that re-publishes on connect; the options array is re-derived with it.
25152
+ *
25153
+ * See `RuntimeStateDurability`. Enforced by
25154
+ * `scripts/check-runtime-state-durability.ts`.
25155
+ */
25156
+ durability: "session"
24863
25157
  };
24864
25158
  var CoverStatusSchema = object({
24865
25159
  /** Lifecycle state of the cover. */
@@ -24920,7 +25214,17 @@ var coverCapability = {
24920
25214
  * Runtime-state slice — mirrored by the kernel. UI controls watch
24921
25215
  * the slice for live position changes during a move.
24922
25216
  */
24923
- runtimeState: CoverStatusSchema
25217
+ runtimeState: CoverStatusSchema,
25218
+ /**
25219
+ * Runtime-state durability: **restored** — position survives a restart on the device; the mirror should agree at boot rather than read blank.
25220
+ *
25221
+ * See `RuntimeStateDurability`. Enforced by
25222
+ * `scripts/check-runtime-state-durability.ts`.
25223
+ */
25224
+ durability: "restored",
25225
+ /** Clock fields: written, but excluded from the compare that decides
25226
+ * whether persisting is worth a SQLite commit. */
25227
+ volatileStateFields: ["lastChangedAt"]
24924
25228
  };
24925
25229
  /**
24926
25230
  * Vendor-neutral day/night (IR-cut) control — the per-camera config cap
@@ -25014,7 +25318,17 @@ var dayNightCapability = {
25014
25318
  schema: DayNightStatusSchema,
25015
25319
  kind: "poll"
25016
25320
  },
25017
- runtimeState: DayNightStatusSchema
25321
+ runtimeState: DayNightStatusSchema,
25322
+ /**
25323
+ * Runtime-state durability: **restored** — operator-set IR-cut behaviour; mutation-driven.
25324
+ *
25325
+ * See `RuntimeStateDurability`. Enforced by
25326
+ * `scripts/check-runtime-state-durability.ts`.
25327
+ */
25328
+ durability: "restored",
25329
+ /** Clock fields: written, but excluded from the compare that decides
25330
+ * whether persisting is worth a SQLite commit. */
25331
+ volatileStateFields: ["lastFetchedAt"]
25018
25332
  };
25019
25333
  /**
25020
25334
  * Generic device-level status snapshot. Auto-registered by `BaseDevice`
@@ -25061,7 +25375,17 @@ onStatusChanged: { data: object({
25061
25375
  schema: DeviceStatusSchema,
25062
25376
  kind: "push"
25063
25377
  },
25064
- runtimeState: DeviceStatusSchema
25378
+ runtimeState: DeviceStatusSchema,
25379
+ /**
25380
+ * 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.
25381
+ *
25382
+ * See `RuntimeStateDurability`. Enforced by
25383
+ * `scripts/check-runtime-state-durability.ts`.
25384
+ */
25385
+ durability: "restored",
25386
+ /** Clock fields: written, but excluded from the compare that decides
25387
+ * whether persisting is worth a SQLite commit. */
25388
+ volatileStateFields: ["lastChangedAt"]
25065
25389
  };
25066
25390
  /**
25067
25391
  * Doorbell button cap. Two kinds of providers coexist behind this cap
@@ -25123,7 +25447,14 @@ onPressed: { data: DoorbellPressEventSchema } },
25123
25447
  * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
25124
25448
  * without subscribing.
25125
25449
  */
25126
- runtimeState: DoorbellStatusSchema
25450
+ runtimeState: DoorbellStatusSchema,
25451
+ /**
25452
+ * 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.
25453
+ *
25454
+ * See `RuntimeStateDurability`. Enforced by
25455
+ * `scripts/check-runtime-state-durability.ts`.
25456
+ */
25457
+ durability: "restored"
25127
25458
  };
25128
25459
  /**
25129
25460
  * Enum-state sensor — a string value picked from a finite option set.
@@ -25163,7 +25494,17 @@ var enumSensorCapability = {
25163
25494
  schema: EnumSensorStatusSchema,
25164
25495
  kind: "push"
25165
25496
  },
25166
- runtimeState: EnumSensorStatusSchema
25497
+ runtimeState: EnumSensorStatusSchema,
25498
+ /**
25499
+ * Runtime-state durability: **restored** — as `numeric-sensor`; 80 devices.
25500
+ *
25501
+ * See `RuntimeStateDurability`. Enforced by
25502
+ * `scripts/check-runtime-state-durability.ts`.
25503
+ */
25504
+ durability: "restored",
25505
+ /** Clock fields: written, but excluded from the compare that decides
25506
+ * whether persisting is worth a SQLite commit. */
25507
+ volatileStateFields: ["lastFetchedAt"]
25167
25508
  };
25168
25509
  /**
25169
25510
  * Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
@@ -25199,7 +25540,14 @@ var eventEmitterCapability = {
25199
25540
  schema: EventEmitterStatusSchema,
25200
25541
  kind: "push"
25201
25542
  },
25202
- runtimeState: EventEmitterStatusSchema
25543
+ runtimeState: EventEmitterStatusSchema,
25544
+ /**
25545
+ * Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
25546
+ *
25547
+ * See `RuntimeStateDurability`. Enforced by
25548
+ * `scripts/check-runtime-state-durability.ts`.
25549
+ */
25550
+ durability: "session"
25203
25551
  };
25204
25552
  var EventItemSchema = object({
25205
25553
  id: string(),
@@ -25516,7 +25864,14 @@ var fanControlCapability = {
25516
25864
  * Runtime-state slice — mirrored by the kernel. UI fan speed
25517
25865
  * sliders read `percentage` for live updates.
25518
25866
  */
25519
- runtimeState: FanControlStatusSchema
25867
+ runtimeState: FanControlStatusSchema,
25868
+ /**
25869
+ * Runtime-state durability: **session** — as `brightness`.
25870
+ *
25871
+ * See `RuntimeStateDurability`. Enforced by
25872
+ * `scripts/check-runtime-state-durability.ts`.
25873
+ */
25874
+ durability: "session"
25520
25875
  };
25521
25876
  /**
25522
25877
  * Per-device feature/identity probe slice. Holds the runtime-resolved
@@ -25592,7 +25947,14 @@ onProbeChanged: { data: object({
25592
25947
  schema: FeatureProbeStatusSchema,
25593
25948
  kind: "push"
25594
25949
  },
25595
- runtimeState: FeatureProbeStatusSchema
25950
+ runtimeState: FeatureProbeStatusSchema,
25951
+ /**
25952
+ * 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.
25953
+ *
25954
+ * See `RuntimeStateDurability`. Enforced by
25955
+ * `scripts/check-runtime-state-durability.ts`.
25956
+ */
25957
+ durability: "session"
25596
25958
  };
25597
25959
  /**
25598
25960
  * Water leak / moisture sensor. Boolean "is liquid currently
@@ -25619,7 +25981,17 @@ var floodCapability = {
25619
25981
  schema: FloodStatusSchema,
25620
25982
  kind: "push"
25621
25983
  },
25622
- runtimeState: FloodStatusSchema
25984
+ runtimeState: FloodStatusSchema,
25985
+ /**
25986
+ * Runtime-state durability: **restored** — as `smoke`.
25987
+ *
25988
+ * See `RuntimeStateDurability`. Enforced by
25989
+ * `scripts/check-runtime-state-durability.ts`.
25990
+ */
25991
+ durability: "restored",
25992
+ /** Clock fields: written, but excluded from the compare that decides
25993
+ * whether persisting is worth a SQLite commit. */
25994
+ volatileStateFields: ["lastChangedAt"]
25623
25995
  };
25624
25996
  /**
25625
25997
  * Combustible-gas (LPG / methane / hydrogen) alarm sensor. Drives
@@ -25642,7 +26014,17 @@ var gasCapability = {
25642
26014
  schema: GasStatusSchema,
25643
26015
  kind: "push"
25644
26016
  },
25645
- runtimeState: GasStatusSchema
26017
+ runtimeState: GasStatusSchema,
26018
+ /**
26019
+ * Runtime-state durability: **restored** — as `smoke`.
26020
+ *
26021
+ * See `RuntimeStateDurability`. Enforced by
26022
+ * `scripts/check-runtime-state-durability.ts`.
26023
+ */
26024
+ durability: "restored",
26025
+ /** Clock fields: written, but excluded from the compare that decides
26026
+ * whether persisting is worth a SQLite commit. */
26027
+ volatileStateFields: ["lastChangedAt"]
25646
26028
  };
25647
26029
  /**
25648
26030
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
@@ -25718,7 +26100,14 @@ var humidifierCapability = {
25718
26100
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
25719
26101
  * slice for live humidity / mode changes.
25720
26102
  */
25721
- runtimeState: HumidifierStatusSchema
26103
+ runtimeState: HumidifierStatusSchema,
26104
+ /**
26105
+ * Runtime-state durability: **session** — as `climate-control`.
26106
+ *
26107
+ * See `RuntimeStateDurability`. Enforced by
26108
+ * `scripts/check-runtime-state-durability.ts`.
26109
+ */
26110
+ durability: "session"
25722
26111
  };
25723
26112
  /**
25724
26113
  * Single-metric humidity reading. Drives Home Assistant `sensor`
@@ -25754,7 +26143,17 @@ var humiditySensorCapability = {
25754
26143
  schema: HumiditySensorStatusSchema,
25755
26144
  kind: "push"
25756
26145
  },
25757
- runtimeState: HumiditySensorStatusSchema
26146
+ runtimeState: HumiditySensorStatusSchema,
26147
+ /**
26148
+ * Runtime-state durability: **restored** — as `numeric-sensor` (67 of 75 writes were the clock alone).
26149
+ *
26150
+ * See `RuntimeStateDurability`. Enforced by
26151
+ * `scripts/check-runtime-state-durability.ts`.
26152
+ */
26153
+ durability: "restored",
26154
+ /** Clock fields: written, but excluded from the compare that decides
26155
+ * whether persisting is worth a SQLite commit. */
26156
+ volatileStateFields: ["lastFetchedAt"]
25758
26157
  };
25759
26158
  /**
25760
26159
  * Image display cap. Models a single still image exposed by an integration —
@@ -25792,7 +26191,14 @@ var imageCapability = {
25792
26191
  * Runtime-state slice — mirrored by the kernel. The UI reads `url`
25793
26192
  * directly and renders the still image.
25794
26193
  */
25795
- runtimeState: ImageStatusSchema
26194
+ runtimeState: ImageStatusSchema,
26195
+ /**
26196
+ * Runtime-state durability: **session** — a snapshot URL is a session-scoped handle; a restored one points at nothing.
26197
+ *
26198
+ * See `RuntimeStateDurability`. Enforced by
26199
+ * `scripts/check-runtime-state-durability.ts`.
26200
+ */
26201
+ durability: "session"
25796
26202
  };
25797
26203
  /**
25798
26204
  * Vendor-neutral image / picture-adjustment cap — the per-camera config
@@ -25941,7 +26347,17 @@ var imageSettingsCapability = {
25941
26347
  schema: ImageSettingsStatusSchema,
25942
26348
  kind: "poll"
25943
26349
  },
25944
- runtimeState: ImageSettingsStatusSchema
26350
+ runtimeState: ImageSettingsStatusSchema,
26351
+ /**
26352
+ * Runtime-state durability: **restored** — operator-set camera imaging; mutation-driven.
26353
+ *
26354
+ * See `RuntimeStateDurability`. Enforced by
26355
+ * `scripts/check-runtime-state-durability.ts`.
26356
+ */
26357
+ durability: "restored",
26358
+ /** Clock fields: written, but excluded from the compare that decides
26359
+ * whether persisting is worth a SQLite commit. */
26360
+ volatileStateFields: ["lastFetchedAt"]
25945
26361
  };
25946
26362
  /**
25947
26363
  * integrations — system-scoped singleton capability for integration
@@ -26350,7 +26766,14 @@ var lawnMowerControlCapability = {
26350
26766
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
26351
26767
  * slice for live activity + battery changes.
26352
26768
  */
26353
- runtimeState: LawnMowerControlStatusSchema
26769
+ runtimeState: LawnMowerControlStatusSchema,
26770
+ /**
26771
+ * Runtime-state durability: **session** — as `vacuum-control`.
26772
+ *
26773
+ * See `RuntimeStateDurability`. Enforced by
26774
+ * `scripts/check-runtime-state-durability.ts`.
26775
+ */
26776
+ durability: "session"
26354
26777
  };
26355
26778
  /**
26356
26779
  * local-network — hub-only singleton.
@@ -26626,7 +27049,17 @@ var lockControlCapability = {
26626
27049
  * read `state` and disable themselves during `locking`/`unlocking`
26627
27050
  * transitions.
26628
27051
  */
26629
- runtimeState: LockControlStatusSchema
27052
+ runtimeState: LockControlStatusSchema,
27053
+ /**
27054
+ * Runtime-state durability: **restored** — a lock left locked must still read locked.
27055
+ *
27056
+ * See `RuntimeStateDurability`. Enforced by
27057
+ * `scripts/check-runtime-state-durability.ts`.
27058
+ */
27059
+ durability: "restored",
27060
+ /** Clock fields: written, but excluded from the compare that decides
27061
+ * whether persisting is worth a SQLite commit. */
27062
+ volatileStateFields: ["lastChangedAt"]
26630
27063
  };
26631
27064
  /**
26632
27065
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
@@ -26788,7 +27221,14 @@ var mediaPlayerCapability = {
26788
27221
  * full slice for live now-playing, volume, and progress updates
26789
27222
  * without polling.
26790
27223
  */
26791
- runtimeState: MediaPlayerStatusSchema
27224
+ runtimeState: MediaPlayerStatusSchema,
27225
+ /**
27226
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
27227
+ *
27228
+ * See `RuntimeStateDurability`. Enforced by
27229
+ * `scripts/check-runtime-state-durability.ts`.
27230
+ */
27231
+ durability: "session"
26792
27232
  };
26793
27233
  /**
26794
27234
  * mesh-network — collection cap for mesh-VPN providers.
@@ -27113,7 +27553,14 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
27113
27553
  * `device.state.motion.value`. Reads never invoke the provider, so
27114
27554
  * UIs and other addons can poll the cached state safely.
27115
27555
  */
27116
- runtimeState: MotionStatusSchema
27556
+ runtimeState: MotionStatusSchema,
27557
+ /**
27558
+ * 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.
27559
+ *
27560
+ * See `RuntimeStateDurability`. Enforced by
27561
+ * `scripts/check-runtime-state-durability.ts`.
27562
+ */
27563
+ durability: "session"
27117
27564
  };
27118
27565
  /**
27119
27566
  * Motion-trigger toggle for accessory devices.
@@ -27178,7 +27625,14 @@ var motionTriggerCapability = {
27178
27625
  schema: MotionTriggerStatusSchema,
27179
27626
  kind: "command-driven"
27180
27627
  },
27181
- runtimeState: MotionTriggerRuntimeStateSchema
27628
+ runtimeState: MotionTriggerRuntimeStateSchema,
27629
+ /**
27630
+ * 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.
27631
+ *
27632
+ * See `RuntimeStateDurability`. Enforced by
27633
+ * `scripts/check-runtime-state-durability.ts`.
27634
+ */
27635
+ durability: "session"
27182
27636
  };
27183
27637
  /**
27184
27638
  * Motion-zones share the same MaskShape vocabulary as privacy-mask — the
@@ -27245,7 +27699,17 @@ var motionZonesCapability = {
27245
27699
  schema: MotionZoneStatusSchema,
27246
27700
  kind: "poll"
27247
27701
  },
27248
- runtimeState: MotionZoneStatusSchema
27702
+ runtimeState: MotionZoneStatusSchema,
27703
+ /**
27704
+ * 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.
27705
+ *
27706
+ * See `RuntimeStateDurability`. Enforced by
27707
+ * `scripts/check-runtime-state-durability.ts`.
27708
+ */
27709
+ durability: "restored",
27710
+ /** Clock fields: written, but excluded from the compare that decides
27711
+ * whether persisting is worth a SQLite commit. */
27712
+ volatileStateFields: ["lastFetchedAt"]
27249
27713
  };
27250
27714
  /**
27251
27715
  * On-camera AI object detection cap. Surfaces per-device the classes
@@ -27319,7 +27783,17 @@ var nativeObjectDetectionCapability = {
27319
27783
  schema: NativeObjectDetectionStatusSchema,
27320
27784
  kind: "push"
27321
27785
  },
27322
- runtimeState: NativeObjectDetectionRuntimeStateSchema
27786
+ runtimeState: NativeObjectDetectionRuntimeStateSchema,
27787
+ /**
27788
+ * 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.
27789
+ *
27790
+ * See `RuntimeStateDurability`. Enforced by
27791
+ * `scripts/check-runtime-state-durability.ts`.
27792
+ */
27793
+ durability: "restored",
27794
+ /** Clock fields: written, but excluded from the compare that decides
27795
+ * whether persisting is worth a SQLite commit. */
27796
+ volatileStateFields: ["lastFetchedAt"]
27323
27797
  };
27324
27798
  /**
27325
27799
  * network-quality — system-scoped singleton capability tracking RTT,
@@ -27706,7 +28180,14 @@ onSent: { data: object({
27706
28180
  * form reads `supports` to gate optional fields; history pane reads
27707
28181
  * `lastSentAt` / `lastError` / `queueDepth`.
27708
28182
  */
27709
- runtimeState: NotifierStatusSchema
28183
+ runtimeState: NotifierStatusSchema,
28184
+ /**
28185
+ * Runtime-state durability: **session** — live queue depth and last-send state; a restored queue depth describes a queue that no longer exists.
28186
+ *
28187
+ * See `RuntimeStateDurability`. Enforced by
28188
+ * `scripts/check-runtime-state-durability.ts`.
28189
+ */
28190
+ durability: "session"
27710
28191
  };
27711
28192
  /**
27712
28193
  * Generic numeric sensor — last-resort fallback when no typed numeric
@@ -27749,7 +28230,17 @@ var numericSensorCapability = {
27749
28230
  schema: NumericSensorStatusSchema,
27750
28231
  kind: "push"
27751
28232
  },
27752
- runtimeState: NumericSensorStatusSchema
28233
+ runtimeState: NumericSensorStatusSchema,
28234
+ /**
28235
+ * 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.
28236
+ *
28237
+ * See `RuntimeStateDurability`. Enforced by
28238
+ * `scripts/check-runtime-state-durability.ts`.
28239
+ */
28240
+ durability: "restored",
28241
+ /** Clock fields: written, but excluded from the compare that decides
28242
+ * whether persisting is worth a SQLite commit. */
28243
+ volatileStateFields: ["lastFetchedAt"]
27753
28244
  };
27754
28245
  /**
27755
28246
  * Generic on-screen-display (video overlay) cap. Each camera exposes
@@ -28227,7 +28718,14 @@ var petFeederCapability = {
28227
28718
  * the full slice via `device.state.petFeeder.value` and refresh on
28228
28719
  * every poll without re-querying the provider.
28229
28720
  */
28230
- runtimeState: PetFeederStatusSchema
28721
+ runtimeState: PetFeederStatusSchema,
28722
+ /**
28723
+ * Runtime-state durability: **session** — live appliance state re-published on connect.
28724
+ *
28725
+ * See `RuntimeStateDurability`. Enforced by
28726
+ * `scripts/check-runtime-state-durability.ts`.
28727
+ */
28728
+ durability: "session"
28231
28729
  };
28232
28730
  var VehicleSchema = object({
28233
28731
  id: string(),
@@ -28583,7 +29081,17 @@ var powerMeterCapability = {
28583
29081
  schema: PowerMeterStatusSchema,
28584
29082
  kind: "push"
28585
29083
  },
28586
- runtimeState: PowerMeterStatusSchema
29084
+ runtimeState: PowerMeterStatusSchema,
29085
+ /**
29086
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
29087
+ *
29088
+ * See `RuntimeStateDurability`. Enforced by
29089
+ * `scripts/check-runtime-state-durability.ts`.
29090
+ */
29091
+ durability: "restored",
29092
+ /** Clock fields: written, but excluded from the compare that decides
29093
+ * whether persisting is worth a SQLite commit. */
29094
+ volatileStateFields: ["lastFetchedAt"]
28587
29095
  };
28588
29096
  /**
28589
29097
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -28640,7 +29148,17 @@ var presenceCapability = {
28640
29148
  * the map pin is rendered (use `DeviceFeature.PresenceGps` for the
28641
29149
  * pre-fetch fast-path check).
28642
29150
  */
28643
- runtimeState: PresenceStatusSchema
29151
+ runtimeState: PresenceStatusSchema,
29152
+ /**
29153
+ * Runtime-state durability: **restored** — occupancy-relevant: the restored state is what an occupancy rule compares the first post-restart observation against.
29154
+ *
29155
+ * See `RuntimeStateDurability`. Enforced by
29156
+ * `scripts/check-runtime-state-durability.ts`.
29157
+ */
29158
+ durability: "restored",
29159
+ /** Clock fields: written, but excluded from the compare that decides
29160
+ * whether persisting is worth a SQLite commit. */
29161
+ volatileStateFields: ["lastChangedAt"]
28644
29162
  };
28645
29163
  /**
28646
29164
  * Atmospheric pressure reading in hectopascals. Drives Home Assistant
@@ -28676,7 +29194,17 @@ var pressureSensorCapability = {
28676
29194
  schema: PressureSensorStatusSchema,
28677
29195
  kind: "push"
28678
29196
  },
28679
- runtimeState: PressureSensorStatusSchema
29197
+ runtimeState: PressureSensorStatusSchema,
29198
+ /**
29199
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
29200
+ *
29201
+ * See `RuntimeStateDurability`. Enforced by
29202
+ * `scripts/check-runtime-state-durability.ts`.
29203
+ */
29204
+ durability: "restored",
29205
+ /** Clock fields: written, but excluded from the compare that decides
29206
+ * whether persisting is worth a SQLite commit. */
29207
+ volatileStateFields: ["lastFetchedAt"]
28680
29208
  };
28681
29209
  /**
28682
29210
  * PRIVACY — what the camera deliberately does not capture. Two planes:
@@ -28806,7 +29334,17 @@ var privacyMaskCapability = {
28806
29334
  schema: PrivacyMaskStatusSchema,
28807
29335
  kind: "poll"
28808
29336
  },
28809
- runtimeState: PrivacyMaskStatusSchema
29337
+ runtimeState: PrivacyMaskStatusSchema,
29338
+ /**
29339
+ * Runtime-state durability: **restored** — operator-drawn regions, zero real churn — 22 writes in 25 minutes, every one of them the clock.
29340
+ *
29341
+ * See `RuntimeStateDurability`. Enforced by
29342
+ * `scripts/check-runtime-state-durability.ts`.
29343
+ */
29344
+ durability: "restored",
29345
+ /** Clock fields: written, but excluded from the compare that decides
29346
+ * whether persisting is worth a SQLite commit. */
29347
+ volatileStateFields: ["lastFetchedAt"]
28810
29348
  };
28811
29349
  var PtzPresetSchema = object({
28812
29350
  id: string(),
@@ -29014,7 +29552,14 @@ var ptzAutotrackCapability = {
29014
29552
  * fetch / cache / fallback logic out of the four cap methods —
29015
29553
  * they become trampolines over `runtimeState`.
29016
29554
  */
29017
- runtimeState: PtzAutotrackRuntimeStateSchema
29555
+ runtimeState: PtzAutotrackRuntimeStateSchema,
29556
+ /**
29557
+ * Runtime-state durability: **session** — mirrors the camera's own autotrack config, re-read on connect.
29558
+ *
29559
+ * See `RuntimeStateDurability`. Enforced by
29560
+ * `scripts/check-runtime-state-durability.ts`.
29561
+ */
29562
+ durability: "session"
29018
29563
  };
29019
29564
  /**
29020
29565
  * reboot — device-scoped capability for "soft" device reboots (firmware
@@ -29822,7 +30367,14 @@ var sceneMonitorCapability = {
29822
30367
  schema: SceneMonitorStatusSchema,
29823
30368
  kind: "push"
29824
30369
  },
29825
- runtimeState: SceneMonitorStatusSchema
30370
+ runtimeState: SceneMonitorStatusSchema,
30371
+ /**
30372
+ * Runtime-state durability: **session** — re-derived from the current scene on the next evaluation.
30373
+ *
30374
+ * See `RuntimeStateDurability`. Enforced by
30375
+ * `scripts/check-runtime-state-durability.ts`.
30376
+ */
30377
+ durability: "session"
29826
30378
  };
29827
30379
  /**
29828
30380
  * Per-stage gating mode applied to the zones a rule references.
@@ -29966,7 +30518,14 @@ var scriptRunnerCapability = {
29966
30518
  * `isRunning` to render a spinner during execution and surfaces
29967
30519
  * `lastError` / `lastRunSuccess` in the recent-runs panel.
29968
30520
  */
29969
- runtimeState: ScriptRunnerStatusSchema
30521
+ runtimeState: ScriptRunnerStatusSchema,
30522
+ /**
30523
+ * Runtime-state durability: **session** — a restored `isRunning: true` describes a process that died with the previous hub.
30524
+ *
30525
+ * See `RuntimeStateDurability`. Enforced by
30526
+ * `scripts/check-runtime-state-durability.ts`.
30527
+ */
30528
+ durability: "session"
29970
30529
  };
29971
30530
  /**
29972
30531
  * Smoke alarm sensor — boolean "is smoke currently detected" with
@@ -29993,7 +30552,17 @@ var smokeCapability = {
29993
30552
  schema: SmokeStatusSchema,
29994
30553
  kind: "push"
29995
30554
  },
29996
- runtimeState: SmokeStatusSchema
30555
+ runtimeState: SmokeStatusSchema,
30556
+ /**
30557
+ * Runtime-state durability: **restored** — a safety sensor must not read "clear" merely because the hub restarted.
30558
+ *
30559
+ * See `RuntimeStateDurability`. Enforced by
30560
+ * `scripts/check-runtime-state-durability.ts`.
30561
+ */
30562
+ durability: "restored",
30563
+ /** Clock fields: written, but excluded from the compare that decides
30564
+ * whether persisting is worth a SQLite commit. */
30565
+ volatileStateFields: ["lastChangedAt"]
29997
30566
  };
29998
30567
  /**
29999
30568
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -30179,7 +30748,17 @@ var streamParamsCapability = {
30179
30748
  schema: StreamParamsStatusSchema,
30180
30749
  kind: "poll"
30181
30750
  },
30182
- runtimeState: StreamParamsStatusSchema
30751
+ runtimeState: StreamParamsStatusSchema,
30752
+ /**
30753
+ * Runtime-state durability: **restored** — operator-set encoder profile; mutation-driven.
30754
+ *
30755
+ * See `RuntimeStateDurability`. Enforced by
30756
+ * `scripts/check-runtime-state-durability.ts`.
30757
+ */
30758
+ durability: "restored",
30759
+ /** Clock fields: written, but excluded from the compare that decides
30760
+ * whether persisting is worth a SQLite commit. */
30761
+ volatileStateFields: ["lastFetchedAt"]
30183
30762
  };
30184
30763
  /**
30185
30764
  * Generic on/off switch cap for accessory children (siren, floodlight,
@@ -30225,6 +30804,16 @@ var switchCapability = {
30225
30804
  * not need to re-query the provider after a setState mutation.
30226
30805
  */
30227
30806
  runtimeState: SwitchStatusSchema,
30807
+ /**
30808
+ * Runtime-state durability: **restored** — device state an operator reads as authoritative; 55 devices, transition-driven.
30809
+ *
30810
+ * See `RuntimeStateDurability`. Enforced by
30811
+ * `scripts/check-runtime-state-durability.ts`.
30812
+ */
30813
+ durability: "restored",
30814
+ /** Clock fields: written, but excluded from the compare that decides
30815
+ * whether persisting is worth a SQLite commit. */
30816
+ volatileStateFields: ["lastChangedAt"],
30228
30817
  settings: { bindings: [{
30229
30818
  kind: "scalar",
30230
30819
  statusPath: "on",
@@ -30319,7 +30908,17 @@ var tamperCapability = {
30319
30908
  schema: TamperStatusSchema,
30320
30909
  kind: "push"
30321
30910
  },
30322
- runtimeState: TamperStatusSchema
30911
+ runtimeState: TamperStatusSchema,
30912
+ /**
30913
+ * Runtime-state durability: **restored** — as `smoke`.
30914
+ *
30915
+ * See `RuntimeStateDurability`. Enforced by
30916
+ * `scripts/check-runtime-state-durability.ts`.
30917
+ */
30918
+ durability: "restored",
30919
+ /** Clock fields: written, but excluded from the compare that decides
30920
+ * whether persisting is worth a SQLite commit. */
30921
+ volatileStateFields: ["lastChangedAt"]
30323
30922
  };
30324
30923
  /**
30325
30924
  * Single-metric temperature reading. Drives Home Assistant `sensor`
@@ -30364,7 +30963,17 @@ var temperatureSensorCapability = {
30364
30963
  schema: TemperatureSensorStatusSchema,
30365
30964
  kind: "push"
30366
30965
  },
30367
- runtimeState: TemperatureSensorStatusSchema
30966
+ runtimeState: TemperatureSensorStatusSchema,
30967
+ /**
30968
+ * Runtime-state durability: **restored** — as `numeric-sensor` (69 of 125 writes were the clock alone).
30969
+ *
30970
+ * See `RuntimeStateDurability`. Enforced by
30971
+ * `scripts/check-runtime-state-durability.ts`.
30972
+ */
30973
+ durability: "restored",
30974
+ /** Clock fields: written, but excluded from the compare that decides
30975
+ * whether persisting is worth a SQLite commit. */
30976
+ volatileStateFields: ["lastFetchedAt"]
30368
30977
  };
30369
30978
  /**
30370
30979
  * toast — system-scoped singleton capability that streams toast
@@ -30440,7 +31049,14 @@ var updateCapability = {
30440
31049
  schema: UpdateStatusSchema,
30441
31050
  kind: "poll"
30442
31051
  },
30443
- runtimeState: UpdateStatusSchema
31052
+ runtimeState: UpdateStatusSchema,
31053
+ /**
31054
+ * Runtime-state durability: **session** — a restored `inProgress: true` describes an update that is no longer running; versions are re-probed at boot.
31055
+ *
31056
+ * See `RuntimeStateDurability`. Enforced by
31057
+ * `scripts/check-runtime-state-durability.ts`.
31058
+ */
31059
+ durability: "session"
30444
31060
  };
30445
31061
  var UserSummarySchema = object({
30446
31062
  id: string(),
@@ -30816,7 +31432,14 @@ var vacuumControlCapability = {
30816
31432
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30817
31433
  * slice for live state + battery + fan-speed changes.
30818
31434
  */
30819
- runtimeState: VacuumControlStatusSchema
31435
+ runtimeState: VacuumControlStatusSchema,
31436
+ /**
31437
+ * Runtime-state durability: **session** — as `media-player` — a restored `state: cleaning` is a robot that is not cleaning.
31438
+ *
31439
+ * See `RuntimeStateDurability`. Enforced by
31440
+ * `scripts/check-runtime-state-durability.ts`.
31441
+ */
31442
+ durability: "session"
30820
31443
  };
30821
31444
  var ValveStatusSchema = object({
30822
31445
  /** Lifecycle state of the valve. */
@@ -30868,7 +31491,14 @@ var valveCapability = {
30868
31491
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30869
31492
  * slice for live position changes during a move.
30870
31493
  */
30871
- runtimeState: ValveStatusSchema
31494
+ runtimeState: ValveStatusSchema,
31495
+ /**
31496
+ * Runtime-state durability: **session** — as `brightness`.
31497
+ *
31498
+ * See `RuntimeStateDurability`. Enforced by
31499
+ * `scripts/check-runtime-state-durability.ts`.
31500
+ */
31501
+ durability: "session"
30872
31502
  };
30873
31503
  /**
30874
31504
  * Vibration / shake / impact sensor. Drives Home Assistant
@@ -30890,7 +31520,17 @@ var vibrationCapability = {
30890
31520
  schema: VibrationStatusSchema,
30891
31521
  kind: "push"
30892
31522
  },
30893
- runtimeState: VibrationStatusSchema
31523
+ runtimeState: VibrationStatusSchema,
31524
+ /**
31525
+ * Runtime-state durability: **restored** — as `smoke`.
31526
+ *
31527
+ * See `RuntimeStateDurability`. Enforced by
31528
+ * `scripts/check-runtime-state-durability.ts`.
31529
+ */
31530
+ durability: "restored",
31531
+ /** Clock fields: written, but excluded from the compare that decides
31532
+ * whether persisting is worth a SQLite commit. */
31533
+ volatileStateFields: ["lastChangedAt"]
30894
31534
  };
30895
31535
  /**
30896
31536
  * Water heater / boiler cap. Models HA `water_heater.*` entities — a
@@ -30964,7 +31604,14 @@ var waterHeaterCapability = {
30964
31604
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30965
31605
  * slice for live temperature / mode / away changes.
30966
31606
  */
30967
- runtimeState: WaterHeaterStatusSchema
31607
+ runtimeState: WaterHeaterStatusSchema,
31608
+ /**
31609
+ * Runtime-state durability: **session** — as `climate-control`.
31610
+ *
31611
+ * See `RuntimeStateDurability`. Enforced by
31612
+ * `scripts/check-runtime-state-durability.ts`.
31613
+ */
31614
+ durability: "session"
30968
31615
  };
30969
31616
  /**
30970
31617
  * Weather provider cap. Models HA `weather.*` entities — a read-only
@@ -31023,7 +31670,14 @@ var weatherCapability = {
31023
31670
  * Runtime-state slice — mirrored by the kernel. The UI reads the
31024
31671
  * current conditions directly from the slice on each weather push.
31025
31672
  */
31026
- runtimeState: WeatherStatusSchema
31673
+ runtimeState: WeatherStatusSchema,
31674
+ /**
31675
+ * Runtime-state durability: **session** — a forecast is stale the moment the hub is down; the provider re-fetches on connect.
31676
+ *
31677
+ * See `RuntimeStateDurability`. Enforced by
31678
+ * `scripts/check-runtime-state-durability.ts`.
31679
+ */
31680
+ durability: "session"
31027
31681
  };
31028
31682
  /**
31029
31683
  * Per-zone occupancy aggregation produced by the analytics frame
@@ -31185,7 +31839,14 @@ var zoneAnalyticsCapability = {
31185
31839
  * automatically; the explicit `getCurrentSnapshot` cap method is
31186
31840
  * still useful for one-off polls without a subscription.
31187
31841
  */
31188
- runtimeState: CameraOccupancySnapshotSchema
31842
+ runtimeState: CameraOccupancySnapshotSchema,
31843
+ /**
31844
+ * Runtime-state durability: **session** — per-frame analytics; with `audio-metrics` it is ~90 % of the offered write rate. Re-derived on the next frame.
31845
+ *
31846
+ * See `RuntimeStateDurability`. Enforced by
31847
+ * `scripts/check-runtime-state-durability.ts`.
31848
+ */
31849
+ durability: "session"
31189
31850
  };
31190
31851
  /**
31191
31852
  * Stages a {@link ZoneRule} can apply to. Discriminator on the rules
@@ -31263,7 +31924,14 @@ var zoneRulesCapability = {
31263
31924
  motion: array(ZoneRuleSchema).readonly(),
31264
31925
  detection: array(ZoneRuleSchema).readonly(),
31265
31926
  package: array(ZoneRuleSchema).readonly()
31266
- })
31927
+ }),
31928
+ /**
31929
+ * Runtime-state durability: **restored** — operator intent, mutation-only, same argument as `zones`.
31930
+ *
31931
+ * See `RuntimeStateDurability`. Enforced by
31932
+ * `scripts/check-runtime-state-durability.ts`.
31933
+ */
31934
+ durability: "restored"
31267
31935
  };
31268
31936
  /**
31269
31937
  * Most specific first. Extending this list is how a new device kind becomes
@@ -37130,6 +37798,7 @@ Object.freeze({
37130
37798
  "network-access": "ingress",
37131
37799
  "smtp-provider": "email"
37132
37800
  });
37801
+ new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37133
37802
  new Set(["devices", "classes"]);
37134
37803
  var WEEKDAY_TO_DAY = {
37135
37804
  Sun: 0,