@camstack/types 1.2.387 → 1.2.389

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/addon.js +24 -14
  2. package/dist/addon.mjs +24 -14
  3. package/dist/capabilities/alarm-panel.cap.d.ts +10 -2
  4. package/dist/capabilities/capability-definition.d.ts +21 -3
  5. package/dist/capabilities/connectivity.cap.d.ts +10 -2
  6. package/dist/capabilities/device-history.cap.d.ts +195 -3
  7. package/dist/capabilities/dishwasher.cap.d.ts +17 -0
  8. package/dist/capabilities/enum-sensor.cap.d.ts +12 -2
  9. package/dist/capabilities/index.d.ts +4 -3
  10. package/dist/capabilities/lock-control.cap.d.ts +9 -2
  11. package/dist/capabilities/log-destination.cap.d.ts +148 -19
  12. package/dist/capabilities/media-player.cap.d.ts +15 -2
  13. package/dist/capabilities/notification-rules.cap.d.ts +213 -13
  14. package/dist/capabilities/platform-probe.cap.d.ts +38 -0
  15. package/dist/capabilities/stream-params.cap.d.ts +24 -2
  16. package/dist/client.js +75 -6
  17. package/dist/client.mjs +75 -6
  18. package/dist/device/base-device.d.ts +34 -1
  19. package/dist/device/index.d.ts +2 -2
  20. package/dist/generated/addon-api.d.ts +89 -12
  21. package/dist/generated/cap-input-defaults.d.ts +1 -1
  22. package/dist/generated/collection-array-methods.d.ts +2 -2
  23. package/dist/generated/history-catalog.d.ts +1 -1
  24. package/dist/generated/method-access-map.d.ts +1 -1
  25. package/dist/generated/method-device-selectors.d.ts +2 -2
  26. package/dist/generated/system-proxy.d.ts +1 -1
  27. package/dist/index.d.ts +5 -2
  28. package/dist/index.js +655 -63
  29. package/dist/index.mjs +637 -64
  30. package/dist/inference/coral-units.d.ts +31 -0
  31. package/dist/inference/runtime-capabilities.d.ts +1 -1
  32. package/dist/interfaces/addon.d.ts +3 -4
  33. package/dist/interfaces/logging.d.ts +16 -1
  34. package/dist/interfaces/platform.d.ts +51 -13
  35. package/dist/notification/media-image.d.ts +67 -0
  36. package/dist/{sleep-yKL5gfrH.mjs → sleep-BSuCCFjf.mjs} +3 -1
  37. package/dist/{sleep-6sOPE0EV.js → sleep-DKtoyk6X.js} +3 -1
  38. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-6sOPE0EV.js");
2
+ const require_sleep = require("./sleep-DKtoyk6X.js");
3
3
  const require_event_category = require("./event-category-eSqkye-K.js");
4
4
  const require_composition = require("./composition-eoYYVfOp.js");
5
5
  const require_node_color = require("./node-color.js");
@@ -15034,11 +15034,30 @@ var DeviceHistorySubjectSchema = zod.z.discriminatedUnion("kind", [zod.z.object(
15034
15034
  kind: zod.z.literal("node"),
15035
15035
  nodeId: zod.z.string().min(1)
15036
15036
  })]);
15037
+ /**
15038
+ * `enum` stores a STATE as the ordinal of its value in the series' value list
15039
+ * (`DeviceHistorySeriesInfo.values`, append-only, so an ordinal never changes
15040
+ * meaning); a rollup keeps the time spent in each state (`states`).
15041
+ */
15037
15042
  var DeviceHistoryKindSchema = zod.z.enum([
15043
+ "gauge",
15044
+ "counter",
15045
+ "boolean",
15046
+ "enum"
15047
+ ]);
15048
+ /** What a PRODUCER may declare. `enum` is cap-only: its value list is the cap field's. */
15049
+ var DeviceHistoryProducerKindSchema = zod.z.enum([
15038
15050
  "gauge",
15039
15051
  "counter",
15040
15052
  "boolean"
15041
15053
  ]);
15054
+ /**
15055
+ * A key computed at READ time from stored series of the same device, never
15056
+ * stored itself. Energy (kWh per bucket) comes from the power meter's
15057
+ * `kwhTotal` counter when the device has one, else from integrating its
15058
+ * `watts` gauge over time.
15059
+ */
15060
+ var DEVICE_HISTORY_ENERGY_KEY = "derived:energy.kwh";
15042
15061
  var DeviceHistoryResolutionSchema = zod.z.enum([
15043
15062
  "raw",
15044
15063
  "5m",
@@ -15071,7 +15090,13 @@ var SeriesInfoSchema = zod.z.object({
15071
15090
  /** `null` is unknown (a `unitFrom` series whose device does not state a unit right now), never a guess. */
15072
15091
  unit: zod.z.string().nullable(),
15073
15092
  displayName: zod.z.string(),
15074
- source: zod.z.enum(["cap", "addon"]),
15093
+ source: zod.z.enum([
15094
+ "cap",
15095
+ "addon",
15096
+ "derived"
15097
+ ]),
15098
+ /** `enum`: the value of each ordinal (index = ordinal); `null` for every other kind. */
15099
+ values: zod.z.array(zod.z.string()).readonly().nullable(),
15075
15100
  firstTs: zod.z.number(),
15076
15101
  lastTs: zod.z.number()
15077
15102
  });
@@ -15085,17 +15110,31 @@ var QueryPointSchema = zod.z.object({
15085
15110
  twMean: zod.z.number().nullable(),
15086
15111
  min: zod.z.number(),
15087
15112
  max: zod.z.number(),
15088
- /** `null` is unknown, never zero (D393). */
15089
- last: zod.z.number().nullable()
15113
+ /** `null` is unknown, never zero (D393). For an `enum` series: the ordinal of {@link state}. */
15114
+ last: zod.z.number().nullable(),
15115
+ /**
15116
+ * The increase over the point: a `counter`'s growth since the previous point
15117
+ * or bucket (a drop is a reset, counted from zero), and for
15118
+ * {@link DEVICE_HISTORY_ENERGY_KEY} the kWh used in the bucket. `null` for
15119
+ * every other kind, and when it cannot be known.
15120
+ */
15121
+ delta: zod.z.number().nullable(),
15122
+ /** `enum`: the state in force at the end of the point (the name of `last`); `null` otherwise. */
15123
+ state: zod.z.string().nullable(),
15124
+ /** `enum` rollup: milliseconds spent in each state inside the bucket; `null` for raw points and other kinds. */
15125
+ states: zod.z.record(zod.z.string(), zod.z.number()).nullable()
15090
15126
  });
15091
15127
  var QuerySeriesSchema = zod.z.object({
15092
15128
  scope: zod.z.string(),
15093
15129
  /** The last point STRICTLY BEFORE `from` in the chosen tier, so a step chart can start at the held value. */
15094
15130
  before: zod.z.object({
15095
15131
  t: zod.z.number(),
15096
- value: zod.z.number().nullable()
15132
+ value: zod.z.number().nullable(),
15133
+ state: zod.z.string().nullable()
15097
15134
  }).nullable(),
15098
- points: zod.z.array(QueryPointSchema).readonly()
15135
+ points: zod.z.array(QueryPointSchema).readonly(),
15136
+ /** A derived key: the stored key its points were computed from; `null` for a stored series. */
15137
+ derivedFrom: zod.z.string().nullable()
15099
15138
  });
15100
15139
  /** The window part of a query, shared by the device and node forms. */
15101
15140
  var QueryWindowShape = {
@@ -15111,6 +15150,25 @@ var QueryResultSchema = zod.z.object({
15111
15150
  series: zod.z.array(QuerySeriesSchema).readonly(),
15112
15151
  truncated: zod.z.boolean()
15113
15152
  });
15153
+ /** Items one `queryMany` may carry: a dashboard's worth, not a scan. */
15154
+ var DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS = 24;
15155
+ /** One read of a batch: a device's key or a node's key (exactly one subject). */
15156
+ var QueryManyItemSchema = zod.z.union([zod.z.object({
15157
+ deviceId: zod.z.number().int(),
15158
+ key: zod.z.string(),
15159
+ scope: zod.z.string().optional()
15160
+ }), zod.z.object({
15161
+ forNodeId: zod.z.string().min(1),
15162
+ key: zod.z.string(),
15163
+ scope: zod.z.string().optional()
15164
+ })]);
15165
+ var QueryManyEntrySchema = zod.z.discriminatedUnion("ok", [zod.z.object({
15166
+ ok: zod.z.literal(true),
15167
+ result: QueryResultSchema
15168
+ }), zod.z.object({
15169
+ ok: zod.z.literal(false),
15170
+ error: zod.z.string()
15171
+ })]);
15114
15172
  var TierStatusSchema = zod.z.object({
15115
15173
  tier: DeviceHistoryResolutionSchema,
15116
15174
  /** `null` until the first background count answered. */
@@ -15179,26 +15237,45 @@ var deviceHistoryCapability = {
15179
15237
  * `listNodeSeries` / `queryNode`.
15180
15238
  */
15181
15239
  listSeries: require_sleep.method(zod.z.object({ deviceId: zod.z.number().int() }), zod.z.array(SeriesInfoSchema).readonly()),
15182
- listNodeSeries: require_sleep.method(zod.z.object({ nodeId: zod.z.string().min(1) }), zod.z.array(SeriesInfoSchema).readonly(), { auth: "admin" }),
15240
+ /**
15241
+ * A NODE's series. The subject is `forNodeId`, never `nodeId`: a top-level
15242
+ * `nodeId` is the cap ROUTING key, and `nodeId: 'little-unraid'` sent the
15243
+ * read to that agent, which hosts no device-history.
15244
+ */
15245
+ listNodeSeries: require_sleep.method(zod.z.object({ forNodeId: zod.z.string().min(1) }), zod.z.array(SeriesInfoSchema).readonly(), { auth: "admin" }),
15183
15246
  /**
15184
15247
  * One series (or every scope of it) at one resolution. `auto` picks the
15185
15248
  * finest tier whose retention covers `from` and whose point count fits
15186
15249
  * `maxPoints`; when even the coarsest tier does not fit, the newest
15187
- * `maxPoints` points are returned and `truncated` is true.
15250
+ * `maxPoints` points are returned and `truncated` is true. A series fed by
15251
+ * closed buckets has no raw rows, so `auto` never answers it from raw; nor
15252
+ * {@link DEVICE_HISTORY_ENERGY_KEY}, which is computed from rollups.
15188
15253
  */
15189
15254
  query: require_sleep.method(zod.z.object({
15190
15255
  deviceId: zod.z.number().int(),
15191
15256
  ...QueryWindowShape
15192
15257
  }), QueryResultSchema),
15193
15258
  queryNode: require_sleep.method(zod.z.object({
15194
- nodeId: zod.z.string().min(1),
15259
+ forNodeId: zod.z.string().min(1),
15195
15260
  ...QueryWindowShape
15196
15261
  }), QueryResultSchema, { auth: "admin" }),
15262
+ /**
15263
+ * Several reads over ONE window, answered in order (one entry per item).
15264
+ * Admin-only: its subjects are nested in `items`, where the device scope
15265
+ * gate cannot see them — a principal limited to one device uses `query`.
15266
+ */
15267
+ queryMany: require_sleep.method(zod.z.object({
15268
+ items: zod.z.array(QueryManyItemSchema).min(1).max(24),
15269
+ from: QueryWindowShape.from,
15270
+ to: QueryWindowShape.to,
15271
+ resolution: QueryWindowShape.resolution,
15272
+ maxPoints: QueryWindowShape.maxPoints
15273
+ }), zod.z.object({ results: zod.z.array(QueryManyEntrySchema).readonly() }), { auth: "admin" }),
15197
15274
  /** Producer: declare (idempotent) a series key this addon owns: `addon:<callerAddonId>/<name>`. */
15198
15275
  declareSeries: require_sleep.method(zod.z.object({
15199
15276
  name: zod.z.string().regex(/^[a-z0-9-]+$/),
15200
15277
  subjectKind: zod.z.enum(["device", "node"]),
15201
- kind: DeviceHistoryKindSchema,
15278
+ kind: DeviceHistoryProducerKindSchema,
15202
15279
  unit: zod.z.string().optional(),
15203
15280
  displayName: zod.z.string().min(1),
15204
15281
  ingest: zod.z.enum(["points", "buckets"])
@@ -40850,6 +40927,103 @@ var LogEntrySchema = zod.z.object({
40850
40927
  meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
40851
40928
  tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
40852
40929
  });
40930
+ /**
40931
+ * A history READ against one destination (D898).
40932
+ *
40933
+ * Every filter is applied by the PROVIDER, before the limit — a filter the
40934
+ * browser applied after the limit is what made a rare device's lines
40935
+ * unreachable behind a busy one's. `before` / `after` are cursors the provider
40936
+ * minted (opaque to everyone else): a file position for the rotated files, a
40937
+ * nanosecond timestamp for Loki.
40938
+ *
40939
+ * A read is BOUNDED by `budgetMs`: a filter that matches nothing on a 30-day
40940
+ * store answers `done: false` with the cursor it reached and the bytes it
40941
+ * scanned, and the caller decides whether to go on. That is what makes a long
40942
+ * scan show progress and stop when asked — `cancelRead` ends the slice in
40943
+ * flight, and not asking for the next slice ends the scan.
40944
+ */
40945
+ var LogReadQuerySchema = zod.z.object({
40946
+ /** Correlates `cancelRead`. Caller-chosen, unique per in-flight read. */
40947
+ requestId: zod.z.string().min(1).max(64).optional(),
40948
+ /** The device the line is about (`tags.deviceId`, numeric). */
40949
+ deviceId: zod.z.number().int().optional(),
40950
+ /**
40951
+ * The emitting addon — bare manifest id; `@<version>` / `::<sub>` suffixes
40952
+ * match. Not `addonId`: a cap router reads that name as "which provider".
40953
+ */
40954
+ fromAddonId: zod.z.string().min(1).max(200).optional(),
40955
+ /**
40956
+ * The emitting node: matches the agent that emitted the line, the node
40957
+ * itself, or a worker under it (`<agent>/<runner>`) — the `agentId`
40958
+ * semantics of `matchesLogTags`. Not `nodeId`: a cap router reads that
40959
+ * name as "which node's provider".
40960
+ */
40961
+ fromNodeId: zod.z.string().min(1).max(200).optional(),
40962
+ /** Any other tag, exact match (AND). */
40963
+ tags: zod.z.record(zod.z.string(), zod.z.union([zod.z.string(), zod.z.number()])).optional(),
40964
+ /** Severity FLOOR: this level and above. */
40965
+ level: LogLevelSchema.optional(),
40966
+ /** Case-insensitive substring over message, scope, tag values and meta values. */
40967
+ text: zod.z.string().min(1).max(200).optional(),
40968
+ /** Epoch ms, inclusive. */
40969
+ since: zod.z.number().optional(),
40970
+ /** Epoch ms, inclusive. */
40971
+ until: zod.z.number().optional(),
40972
+ /** Older than this cursor (exclusive). Absent with `after` absent ⇒ from the newest. */
40973
+ before: zod.z.string().min(1).max(1024).optional(),
40974
+ /** Newer than this cursor (exclusive). */
40975
+ after: zod.z.string().min(1).max(1024).optional(),
40976
+ limit: zod.z.number().int().min(1).max(1e3).optional(),
40977
+ /** Wall-clock bound of this slice. The provider caps it. */
40978
+ budgetMs: zod.z.number().int().min(50).max(1e4).optional()
40979
+ });
40980
+ var LogReadEntrySchema = zod.z.object({
40981
+ /** `<bootId>.<seq>` minted by the emitting LogManager — the dedupe key. */
40982
+ id: zod.z.string().optional(),
40983
+ /** Epoch ms. */
40984
+ timestamp: zod.z.number(),
40985
+ level: LogLevelSchema,
40986
+ scope: zod.z.string().optional(),
40987
+ message: zod.z.string(),
40988
+ tags: zod.z.record(zod.z.string(), zod.z.union([zod.z.string(), zod.z.number()])).optional(),
40989
+ meta: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
40990
+ });
40991
+ var LogReadResultSchema = zod.z.object({
40992
+ /** Newest first. */
40993
+ entries: zod.z.array(LogReadEntrySchema),
40994
+ /** Pass as `before` for the next older page; null when nothing older exists. */
40995
+ nextBefore: zod.z.string().nullable(),
40996
+ /** Pass as `after` to read what arrived since; null when unknown. */
40997
+ nextAfter: zod.z.string().nullable(),
40998
+ /** Bytes this slice read (files) or received (remote). Progress, not a promise. */
40999
+ scannedBytes: zod.z.number().int().nonnegative(),
41000
+ /**
41001
+ * True when the scan in this direction reached the end of the store.
41002
+ * False with fewer than `limit` entries means the budget ran out first —
41003
+ * call again with `nextBefore` to go on.
41004
+ */
41005
+ done: zod.z.boolean(),
41006
+ /** True when `cancelRead` ended this slice early. */
41007
+ cancelled: zod.z.boolean().optional()
41008
+ });
41009
+ var LogReadCapabilitiesSchema = zod.z.object({
41010
+ /** False ⇒ this destination only ships lines (console, forwarder, an unconfigured Loki). */
41011
+ readable: zod.z.boolean(),
41012
+ /** Why not, when `readable` is false — rendered next to the source. */
41013
+ reason: zod.z.string().nullable(),
41014
+ /** Filters the provider applies itself. A false one is applied by nobody. */
41015
+ filters: zod.z.object({
41016
+ deviceId: zod.z.boolean(),
41017
+ addonId: zod.z.boolean(),
41018
+ nodeId: zod.z.boolean(),
41019
+ tags: zod.z.boolean(),
41020
+ level: zod.z.boolean(),
41021
+ text: zod.z.boolean(),
41022
+ timeRange: zod.z.boolean()
41023
+ }),
41024
+ /** How far back the store reaches, when the provider knows (days). */
41025
+ retentionDays: zod.z.number().nullable()
41026
+ });
40853
41027
  var logDestinationCapability = {
40854
41028
  name: "log-destination",
40855
41029
  scope: "system",
@@ -40857,14 +41031,13 @@ var logDestinationCapability = {
40857
41031
  internal: true,
40858
41032
  methods: {
40859
41033
  write: require_sleep.method(LogEntrySchema, zod.z.void(), { kind: "mutation" }),
40860
- query: require_sleep.method(zod.z.object({
40861
- scope: zod.z.array(zod.z.string()).optional(),
40862
- level: LogLevelSchema.optional(),
40863
- since: zod.z.date().optional(),
40864
- until: zod.z.date().optional(),
40865
- limit: zod.z.number().optional(),
40866
- tags: zod.z.record(zod.z.string(), zod.z.string()).optional()
40867
- }), zod.z.array(LogEntrySchema).readonly())
41034
+ /** Absent ⇒ the destination cannot read back (same as `readable: false`). */
41035
+ describeRead: require_sleep.optionalMethod(zod.z.void(), LogReadCapabilitiesSchema, { auth: "admin" }),
41036
+ read: require_sleep.optionalMethod(LogReadQuerySchema, LogReadResultSchema, { auth: "admin" }),
41037
+ cancelRead: require_sleep.optionalMethod(zod.z.object({ requestId: zod.z.string().min(1).max(64) }), zod.z.object({ cancelled: zod.z.boolean() }), {
41038
+ kind: "mutation",
41039
+ auth: "admin"
41040
+ })
40868
41041
  },
40869
41042
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
40870
41043
  mount: { kind: "skip" }
@@ -43189,9 +43362,11 @@ var alarmPanelCapability = {
43189
43362
  */
43190
43363
  durability: "restored",
43191
43364
  /**
43192
- * History (D850): none — enum state; arm/disarm history belongs in the events ledger.
43365
+ * History (D850): the panel's lifecycle state as an `enum` series —
43366
+ * change-only, no floor, so an `arming -> pending -> triggered` sequence of a
43367
+ * few seconds keeps every step. Who armed it stays in the events ledger.
43193
43368
  */
43194
- history: "none",
43369
+ history: { fields: { state: { kind: "enum" } } },
43195
43370
  /** Clock fields: written, but excluded from the compare that decides
43196
43371
  * whether persisting is worth a SQLite commit. */
43197
43372
  volatileStateFields: ["lastChangedAt"]
@@ -44184,6 +44359,33 @@ var NcRuleTargetSchema = zod.z.object({
44184
44359
  params: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
44185
44360
  });
44186
44361
  /**
44362
+ * WHAT THE NOTIFICATION'S PICTURE IS (D897) — the ONE media choice of a rule.
44363
+ *
44364
+ * Every mode starts from the same CLEAN source frame of the trigger moment
44365
+ * (keyFrame → sceneFrame → lastSceneFrame, narrowed to the trigger instant and
44366
+ * to the zone that fired the rule) and renders it once at send time:
44367
+ * - `scene-boxes` — the whole scene, the subject outlined thick in the accent
44368
+ * colour and every OTHER track visible at that instant outlined thin, each
44369
+ * labelled with class and confidence ("dog 87%").
44370
+ * - `crop` — the subject cut out of that frame around its box at that instant.
44371
+ * - `clean` — the whole scene with nothing drawn on it.
44372
+ * - `none` — no picture.
44373
+ *
44374
+ * A mode that cannot be honoured (no box at that instant, a photograph with no
44375
+ * tracked subject) ships `clean` and names the reason in the log, per camera.
44376
+ * It replaces the `attach` × `frame` × `zoneCrop` triple, which let one rule
44377
+ * ship a crop on one event and a boxed scene on the next depending on which
44378
+ * stored kind had landed.
44379
+ */
44380
+ var NcMediaImageSchema = zod.z.enum([
44381
+ "scene-boxes",
44382
+ "crop",
44383
+ "clean",
44384
+ "none"
44385
+ ]);
44386
+ /**
44387
+ * @deprecated Read only by `normalizeNcMediaPolicy` (D897) — see {@link NcMediaImageSchema}.
44388
+ *
44187
44389
  * Media attachment policy (P1 still-image subset).
44188
44390
  * - `best` — the best AVAILABLE subject image at dispatch time (D-3).
44189
44391
  * - `best-matching` — the media that explains WHY the rule fired: a rule
@@ -44197,6 +44399,8 @@ var NcRuleTargetSchema = zod.z.object({
44197
44399
  * - `none` — no attachment.
44198
44400
  */
44199
44401
  /**
44402
+ * @deprecated Read only by `normalizeNcMediaPolicy` (D897) — see {@link NcMediaImageSchema}.
44403
+ *
44200
44404
  * WHAT THE PICTURE SHOWS — chosen explicitly, because the implicit ladders
44201
44405
  * conflate it with the selection strategy and betray the request: asking for
44202
44406
  * the clean scene frame on an object-event owner used to start at
@@ -44243,16 +44447,29 @@ var NcLinkedCameraModeSchema = zod.z.enum(["mosaic", "each"]);
44243
44447
  var NC_CLIP_PRE_ROLL_DEFAULT_SEC = 4;
44244
44448
  var NC_CLIP_POST_ROLL_DEFAULT_SEC = 6;
44245
44449
  var NcMediaPolicySchema = zod.z.object({
44450
+ /**
44451
+ * The picture — see {@link NcMediaImageSchema}. Absent on a rule nobody has
44452
+ * saved since D897: `normalizeNcMediaPolicy` derives it from the legacy
44453
+ * triple below, at load and at every write, and the store keeps only this.
44454
+ */
44455
+ image: NcMediaImageSchema.optional(),
44456
+ /**
44457
+ * Draw the zone that FIRED the rule on the picture (`scene-boxes` / `clean`;
44458
+ * a `crop` never shows it). Valid only on a rule whose trigger is a zone —
44459
+ * `conditions.zones` or an occupancy zone — and refused on save otherwise.
44460
+ * Replaces `zoneCrop`.
44461
+ */
44462
+ showTriggerZone: zod.z.boolean().optional(),
44463
+ /** @deprecated D897 — accepted from an editor that predates `image`, folded into it on save. */
44246
44464
  attach: zod.z.enum([
44247
44465
  "best",
44248
44466
  "best-matching",
44249
44467
  "keyFrame",
44250
44468
  "none"
44251
- ]).default("best"),
44252
- /** What the still shows. Absent = `cropped` (the historical `best` shape). */
44469
+ ]).optional(),
44470
+ /** @deprecated D897 — accepted from an editor that predates `image`, folded into it on save. */
44253
44471
  frame: NcMediaFrameSchema.optional(),
44254
- /** Crop the attached still to the rule's condition-zone bbox (padded) —
44255
- * "show me the ZONE", not the whole scene or the subject crop. */
44472
+ /** @deprecated D897 — folded into `image: 'clean'` + `showTriggerZone` on save. */
44256
44473
  zoneCrop: zod.z.boolean().optional(),
44257
44474
  /**
44258
44475
  * Also attach a short GIF cut from the stream broker's clip ring around the
@@ -44438,7 +44655,9 @@ var NcRuleInputSchema = zod.z.object({
44438
44655
  * targets.
44439
44656
  */
44440
44657
  targetUsers: zod.z.array(zod.z.string()).optional(),
44441
- media: NcMediaPolicySchema.default({ attach: "best" }),
44658
+ /** Absent = the kind's default picture (`resolveNcMediaImage`): the subject crop for a
44659
+ * detection, the clean scene for a sensor or a sound. Set explicitly on save (D897). */
44660
+ media: NcMediaPolicySchema.default({}),
44442
44661
  throttle: NcThrottleSchema.default({
44443
44662
  cooldownSec: 60,
44444
44663
  scope: "rule-device"
@@ -46010,13 +46229,37 @@ var NcAlarmModeOpeningsSchema = zod.z.object({
46010
46229
  /** Covered devices whose state could not be read (D49): neither open nor closed. */
46011
46230
  unknown: zod.z.array(zod.z.number().int())
46012
46231
  });
46232
+ /**
46233
+ * One alarm INPUT — a (rule, device) pair — the operator bypassed until the
46234
+ * next disarm ("Bypass this input until disarm" on a trigger notification, or
46235
+ * `nc.bypassAlarmInput`). Reported so an armed alarm with a silenced input
46236
+ * never looks fully armed (D62: off must never look like broken).
46237
+ */
46238
+ var NcAlarmInputBypassViewSchema = zod.z.object({
46239
+ ruleId: zod.z.string(),
46240
+ /** The rule's current name (its frozen name when the rule is gone). */
46241
+ ruleName: zod.z.string(),
46242
+ /** The input: the sensor of the match, else its camera. */
46243
+ deviceId: zod.z.number().int(),
46244
+ /** The input's current name. */
46245
+ name: zod.z.string(),
46246
+ /** When it was bypassed (ms epoch). */
46247
+ since: zod.z.number(),
46248
+ /** Triggers from this input dropped since. */
46249
+ dropped: zod.z.number().int().min(0)
46250
+ });
46013
46251
  var NcAlarmLiveSchema = zod.z.object({
46014
46252
  state: AlarmStateSchema,
46015
46253
  /** The mode being armed into (exit delay) or held; null when disarmed. */
46016
46254
  targetMode: AlarmArmModeSchema.nullable(),
46017
46255
  availableModes: zod.z.array(AlarmArmModeSchema),
46018
46256
  openings: zod.z.array(NcAlarmModeOpeningsSchema),
46019
- exclusions: zod.z.array(NcAlarmExclusionViewSchema)
46257
+ exclusions: zod.z.array(NcAlarmExclusionViewSchema),
46258
+ /**
46259
+ * Inputs bypassed for the rest of this arm session; cleared on disarm.
46260
+ * Defaulted so an older hub answer still parses (as "none known").
46261
+ */
46262
+ bypassedInputs: zod.z.array(NcAlarmInputBypassViewSchema).default([])
46020
46263
  });
46021
46264
  var NcAlarmConfigSchema = zod.z.object({
46022
46265
  /**
@@ -60985,9 +61228,11 @@ var connectivityCapability = {
60985
61228
  */
60986
61229
  durability: "restored",
60987
61230
  /**
60988
- * History (D850): none — duplicates device-status.online (one authority for availability).
61231
+ * History (D850): `connected` as a boolean series — change-only, no floor. It
61232
+ * is the upstream entity's own link (a HA connectivity sensor), not the
61233
+ * device's reachability, which stays `device-status.online`.
60989
61234
  */
60990
- history: "none",
61235
+ history: { fields: { connected: { kind: "boolean" } } },
60991
61236
  /** Clock fields: written, but excluded from the compare that decides
60992
61237
  * whether persisting is worth a SQLite commit. */
60993
61238
  volatileStateFields: ["lastChangedAt"]
@@ -61871,11 +62116,22 @@ var dishwasherCapability = {
61871
62116
  },
61872
62117
  runtimeState: DishwasherStatusSchema,
61873
62118
  durability: "session",
61874
- history: { fields: { progressPercent: {
61875
- kind: "gauge",
61876
- unit: "%",
61877
- deadband: 2
61878
- } } }
62119
+ history: { fields: {
62120
+ progressPercent: {
62121
+ kind: "gauge",
62122
+ unit: "%",
62123
+ deadband: 2
62124
+ },
62125
+ state: { kind: "enum" },
62126
+ operationState: { kind: "enum" },
62127
+ remainingSeconds: {
62128
+ kind: "gauge",
62129
+ unit: "s",
62130
+ minIntervalMs: 6e4
62131
+ },
62132
+ doorOpen: { kind: "boolean" },
62133
+ powerOn: { kind: "boolean" }
62134
+ } }
61879
62135
  };
61880
62136
  //#endregion
61881
62137
  //#region src/capabilities/doorbell.cap.ts
@@ -61996,9 +62252,15 @@ var enumSensorCapability = {
61996
62252
  */
61997
62253
  durability: "restored",
61998
62254
  /**
61999
- * History (D850): none — text/enum state has no history (non-goal).
62255
+ * History (D850): the value as an `enum` series (an appliance's program or
62256
+ * operation state, an HVAC action). The option set is per device, so the
62257
+ * value list is LEARNED as values are first seen (bounded). A sensor with a
62258
+ * `format` carries a timestamp, not a state, and is not recorded.
62000
62259
  */
62001
- history: "none",
62260
+ history: { fields: { value: {
62261
+ kind: "enum",
62262
+ skipWhenSet: "format"
62263
+ } } },
62002
62264
  /** Clock fields: written, but excluded from the compare that decides
62003
62265
  * whether persisting is worth a SQLite commit. */
62004
62266
  volatileStateFields: ["lastFetchedAt"]
@@ -64252,9 +64514,10 @@ var lockControlCapability = {
64252
64514
  */
64253
64515
  durability: "restored",
64254
64516
  /**
64255
- * History (D850): none — enum state; lock events belong in the events ledger.
64517
+ * History (D850): the lock's state as an `enum` series — change-only, no
64518
+ * floor: a jam or an unlock lasting seconds is the edge an operator looks for.
64256
64519
  */
64257
- history: "none",
64520
+ history: { fields: { state: { kind: "enum" } } },
64258
64521
  /** Clock fields: written, but excluded from the compare that decides
64259
64522
  * whether persisting is worth a SQLite commit. */
64260
64523
  volatileStateFields: ["lastChangedAt"]
@@ -64430,9 +64693,18 @@ var mediaPlayerCapability = {
64430
64693
  */
64431
64694
  durability: "session",
64432
64695
  /**
64433
- * History (D850): none — `positionMs` moves continuously while playing; volume is transient.
64696
+ * History (D850): volume (a level an operator or an automation sets; the
64697
+ * default 10 s floor folds a slider drag into its end value) and mute.
64698
+ * `positionMs` is not recorded: it moves continuously while playing.
64434
64699
  */
64435
- history: "none"
64700
+ history: { fields: {
64701
+ volumeLevel: {
64702
+ kind: "gauge",
64703
+ unit: "%",
64704
+ deadband: 1
64705
+ },
64706
+ isMuted: { kind: "boolean" }
64707
+ } }
64436
64708
  };
64437
64709
  //#endregion
64438
64710
  //#region src/capabilities/mesh-network.cap.ts
@@ -67130,9 +67402,24 @@ var NpuInfoSchema = zod.z.object({
67130
67402
  name: zod.z.string().optional(),
67131
67403
  generation: zod.z.enum(["xdna1", "xdna2"]).optional()
67132
67404
  });
67405
+ var CoralBusSchema = zod.z.enum(["usb", "pci"]);
67133
67406
  var CoralInfoSchema = zod.z.object({
67134
67407
  type: zod.z.literal("coral-edgetpu"),
67135
- bus: zod.z.string().optional()
67408
+ /** Legacy single-bus report (older nodes) — read via `coralUsableUnits`. */
67409
+ bus: zod.z.string().optional(),
67410
+ /** Legacy USB unit count (older nodes) — read via `coralUsableUnits`. */
67411
+ count: zod.z.number().int().nonnegative().optional(),
67412
+ /** Usable units per bus — each one is its own `edgetpu:<bus>[:N]` device. */
67413
+ units: zod.z.array(zod.z.object({
67414
+ bus: CoralBusSchema,
67415
+ count: zod.z.number().int().nonnegative()
67416
+ })).readonly().optional(),
67417
+ /** Present-but-unusable units, each with its typed reason (never "absent"). */
67418
+ unavailable: zod.z.array(zod.z.object({
67419
+ bus: CoralBusSchema,
67420
+ count: zod.z.number().int().nonnegative(),
67421
+ reason: zod.z.enum(["driver-missing", "device-not-accessible"])
67422
+ })).readonly().optional()
67136
67423
  });
67137
67424
  var HardwareInfoSchema = zod.z.object({
67138
67425
  platform: HardwarePlatformSchema,
@@ -71384,9 +71671,29 @@ var streamParamsCapability = {
71384
71671
  */
71385
71672
  durability: "restored",
71386
71673
  /**
71387
- * History (D850): none — encoder configuration.
71674
+ * History (D850): the configured bitrate and frame rate of the main and sub
71675
+ * profiles. Configuration, so change-only on the default floor: a point is an
71676
+ * operator's (or a tuner's) edit, which is exactly what a chart of a stream's
71677
+ * quality over time must show.
71388
71678
  */
71389
- history: "none",
71679
+ history: { fields: {
71680
+ "main.bitrate": {
71681
+ kind: "gauge",
71682
+ unit: "kbps"
71683
+ },
71684
+ "main.framerate": {
71685
+ kind: "gauge",
71686
+ unit: "fps"
71687
+ },
71688
+ "sub.bitrate": {
71689
+ kind: "gauge",
71690
+ unit: "kbps"
71691
+ },
71692
+ "sub.framerate": {
71693
+ kind: "gauge",
71694
+ unit: "fps"
71695
+ }
71696
+ } },
71390
71697
  /** Clock fields: written, but excluded from the compare that decides
71391
71698
  * whether persisting is worth a SQLite commit. */
71392
71699
  volatileStateFields: ["lastFetchedAt"]
@@ -76327,6 +76634,15 @@ var BaseDevice = class {
76327
76634
  * Drivers can use the `getProbeFlags()` helper to read the flag bag
76328
76635
  * with a typed cast.
76329
76636
  *
76637
+ * Answer what you KNOW. A plain array is read as the complete list only
76638
+ * while this device is `online` and probed; return
76639
+ * `accessoryChildrenUnknown(reason)` whenever you cannot know the list right
76640
+ * now (no credentials, parent not connected, a failed or partial read) —
76641
+ * the kernel then deletes nothing. Return `accessoryChildrenComplete(list)`
76642
+ * to say "connected, and this is the complete list" explicitly. A shrink is
76643
+ * deleted only after a second agreeing complete read on a later probe (D49;
76644
+ * `packages/system/src/kernel/moleculer/accessory-prune-gate.ts`).
76645
+ *
76330
76646
  * Default: no children.
76331
76647
  */
76332
76648
  getAccessoryChildren() {
@@ -76350,6 +76666,22 @@ var BaseDevice = class {
76350
76666
  return (this.runtimeState.getCapState("feature-probe")?.lastProbedAt ?? 0) > 0;
76351
76667
  }
76352
76668
  };
76669
+ function accessoryChildrenUnknown(reason, known) {
76670
+ return known === void 0 ? {
76671
+ kind: "unknown",
76672
+ reason
76673
+ } : {
76674
+ kind: "unknown",
76675
+ reason,
76676
+ known
76677
+ };
76678
+ }
76679
+ function accessoryChildrenComplete(children) {
76680
+ return {
76681
+ kind: "complete",
76682
+ children
76683
+ };
76684
+ }
76353
76685
  //#endregion
76354
76686
  //#region src/device/device-restore-retry.ts
76355
76687
  /**
@@ -81281,7 +81613,19 @@ var METHOD_ACCESS_MAP = Object.freeze({
81281
81613
  addonId: null,
81282
81614
  access: "view"
81283
81615
  },
81284
- "logDestination.query": {
81616
+ "logDestination.cancelRead": {
81617
+ capName: "log-destination",
81618
+ capScope: "system",
81619
+ addonId: null,
81620
+ access: "create"
81621
+ },
81622
+ "logDestination.describeRead": {
81623
+ capName: "log-destination",
81624
+ capScope: "system",
81625
+ addonId: null,
81626
+ access: "view"
81627
+ },
81628
+ "logDestination.read": {
81285
81629
  capName: "log-destination",
81286
81630
  capScope: "system",
81287
81631
  addonId: null,
@@ -90700,6 +91044,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
90700
91044
  form: "single",
90701
91045
  optional: false
90702
91046
  }],
91047
+ "logDestination.read": [{
91048
+ name: "deviceId",
91049
+ form: "single",
91050
+ optional: true
91051
+ }],
90703
91052
  "mediaPlayer.getStatus": [{
90704
91053
  name: "deviceId",
90705
91054
  form: "single",
@@ -92467,6 +92816,7 @@ var SYSTEM_SCOPE_DEVICE_METHODS = [
92467
92816
  "faceGallery.getFaceByTrack",
92468
92817
  "faceGallery.listRecentFaces",
92469
92818
  "keychain.deleteDeviceScope",
92819
+ "logDestination.read",
92470
92820
  "networkQuality.getDeviceStats",
92471
92821
  "networkQuality.reportClientStats",
92472
92822
  "notificationRules.getAlarmSensors",
@@ -93062,7 +93412,7 @@ var BucketAccumulator = class {
93062
93412
  //#region src/generated/history-catalog.ts
93063
93413
  /**
93064
93414
  * Cap history catalog, projected from every `*.cap.ts` `history` block.
93065
- * 34 caps with series (62 series), 37 caps answered `'none'`.
93415
+ * 40 caps with series (77 series), 31 caps answered `'none'`.
93066
93416
  * Field keys are `runtimeState` paths (`a.b` into fixed objects only).
93067
93417
  */
93068
93418
  var HISTORY_CATALOG = {
@@ -93093,6 +93443,21 @@ var HISTORY_CATALOG = {
93093
93443
  deadband: 1
93094
93444
  }
93095
93445
  },
93446
+ "alarm-panel": { "state": {
93447
+ kind: "enum",
93448
+ values: [
93449
+ "disarmed",
93450
+ "armed_home",
93451
+ "armed_away",
93452
+ "armed_night",
93453
+ "armed_vacation",
93454
+ "armed_custom_bypass",
93455
+ "arming",
93456
+ "disarming",
93457
+ "pending",
93458
+ "triggered"
93459
+ ]
93460
+ } },
93096
93461
  "ambient-light-sensor": { "lux": {
93097
93462
  kind: "gauge",
93098
93463
  unit: "lx",
@@ -93137,6 +93502,7 @@ var HISTORY_CATALOG = {
93137
93502
  minIntervalMs: 0
93138
93503
  }
93139
93504
  },
93505
+ "connectivity": { "connected": { kind: "boolean" } },
93140
93506
  "contact": { "entryOpen": { kind: "boolean" } },
93141
93507
  "cover": { "position": {
93142
93508
  kind: "gauge",
@@ -93144,10 +93510,48 @@ var HISTORY_CATALOG = {
93144
93510
  minIntervalMs: 0
93145
93511
  } },
93146
93512
  "device-status": { "online": { kind: "boolean" } },
93147
- "dishwasher": { "progressPercent": {
93148
- kind: "gauge",
93149
- unit: "%",
93150
- deadband: 2
93513
+ "dishwasher": {
93514
+ "progressPercent": {
93515
+ kind: "gauge",
93516
+ unit: "%",
93517
+ deadband: 2
93518
+ },
93519
+ "state": {
93520
+ kind: "enum",
93521
+ values: [
93522
+ "idle",
93523
+ "running",
93524
+ "paused",
93525
+ "finished",
93526
+ "error",
93527
+ "unavailable"
93528
+ ]
93529
+ },
93530
+ "operationState": {
93531
+ kind: "enum",
93532
+ values: [
93533
+ "Inactive",
93534
+ "Ready",
93535
+ "DelayedStart",
93536
+ "Run",
93537
+ "Pause",
93538
+ "ActionRequired",
93539
+ "Finished",
93540
+ "Error",
93541
+ "Aborting"
93542
+ ]
93543
+ },
93544
+ "remainingSeconds": {
93545
+ kind: "gauge",
93546
+ unit: "s",
93547
+ minIntervalMs: 6e4
93548
+ },
93549
+ "doorOpen": { kind: "boolean" },
93550
+ "powerOn": { kind: "boolean" }
93551
+ },
93552
+ "enum-sensor": { "value": {
93553
+ kind: "enum",
93554
+ skipWhenSet: "format"
93151
93555
  } },
93152
93556
  "fan-control": {
93153
93557
  "percentage": {
@@ -93182,6 +93586,24 @@ var HISTORY_CATALOG = {
93182
93586
  unit: "%",
93183
93587
  deadband: 2
93184
93588
  } },
93589
+ "lock-control": { "state": {
93590
+ kind: "enum",
93591
+ values: [
93592
+ "locked",
93593
+ "unlocked",
93594
+ "locking",
93595
+ "unlocking",
93596
+ "jammed"
93597
+ ]
93598
+ } },
93599
+ "media-player": {
93600
+ "volumeLevel": {
93601
+ kind: "gauge",
93602
+ unit: "%",
93603
+ deadband: 1
93604
+ },
93605
+ "isMuted": { kind: "boolean" }
93606
+ },
93185
93607
  "motion": { "detected": {
93186
93608
  kind: "boolean",
93187
93609
  excludeDeviceTypes: [require_err_msg.DeviceType.Camera]
@@ -93268,6 +93690,24 @@ var HISTORY_CATALOG = {
93268
93690
  deadband: .1
93269
93691
  } },
93270
93692
  "smoke": { "detected": { kind: "boolean" } },
93693
+ "stream-params": {
93694
+ "main.bitrate": {
93695
+ kind: "gauge",
93696
+ unit: "kbps"
93697
+ },
93698
+ "main.framerate": {
93699
+ kind: "gauge",
93700
+ unit: "fps"
93701
+ },
93702
+ "sub.bitrate": {
93703
+ kind: "gauge",
93704
+ unit: "kbps"
93705
+ },
93706
+ "sub.framerate": {
93707
+ kind: "gauge",
93708
+ unit: "fps"
93709
+ }
93710
+ },
93271
93711
  "switch": { "on": { kind: "boolean" } },
93272
93712
  "tamper": { "tampered": { kind: "boolean" } },
93273
93713
  "temperature-sensor": { "celsius": {
@@ -93345,25 +93785,20 @@ var HISTORY_CATALOG = {
93345
93785
  };
93346
93786
  /** Caps that declared `history: 'none'`, sorted. */
93347
93787
  var HISTORY_NONE_CAPS = [
93348
- "alarm-panel",
93349
93788
  "audio-metrics",
93350
93789
  "automation-control",
93351
93790
  "camera-streams",
93352
93791
  "color",
93353
- "connectivity",
93354
93792
  "consumables",
93355
93793
  "control",
93356
93794
  "day-night",
93357
93795
  "device-discovery",
93358
93796
  "doorbell",
93359
- "enum-sensor",
93360
93797
  "event-emitter",
93361
93798
  "feature-probe",
93362
93799
  "image",
93363
93800
  "image-settings",
93364
93801
  "intercom",
93365
- "lock-control",
93366
- "media-player",
93367
93802
  "motion-trigger",
93368
93803
  "motion-zones",
93369
93804
  "native-object-detection",
@@ -93376,7 +93811,6 @@ var HISTORY_NONE_CAPS = [
93376
93811
  "scene-monitor",
93377
93812
  "script-runner",
93378
93813
  "stream-catalog",
93379
- "stream-params",
93380
93814
  "stream-signals",
93381
93815
  "update",
93382
93816
  "zone-analytics",
@@ -93649,6 +94083,7 @@ function createSystemProxy(api) {
93649
94083
  deviceHistory: {
93650
94084
  listNodeSeries: (input) => dispatch("deviceHistory", "listNodeSeries", "query", input),
93651
94085
  queryNode: (input) => dispatch("deviceHistory", "queryNode", "query", input),
94086
+ queryMany: (input) => dispatch("deviceHistory", "queryMany", "query", input),
93652
94087
  declareSeries: (input) => dispatch("deviceHistory", "declareSeries", "mutation", input),
93653
94088
  record: (input) => dispatch("deviceHistory", "record", "mutation", input),
93654
94089
  recordBuckets: (input) => dispatch("deviceHistory", "recordBuckets", "mutation", input),
@@ -95922,6 +96357,100 @@ function ruleEditorSectionsForKind(kind) {
95922
96357
  return sections;
95923
96358
  }
95924
96359
  //#endregion
96360
+ //#region src/notification/media-image.ts
96361
+ /** Every mode, in the order an editor offers them. */
96362
+ var NC_MEDIA_IMAGE_MODES = [
96363
+ "scene-boxes",
96364
+ "crop",
96365
+ "clean",
96366
+ "none"
96367
+ ];
96368
+ /**
96369
+ * The modes a rule of this kind can honour. A kind that carries no media gets
96370
+ * none (its row ships text, or — an alarm — its SOURCE rule's picture); a kind
96371
+ * whose subject has no region of the frame (a sound) is never offered `crop`.
96372
+ */
96373
+ function imageModesForKind(kind) {
96374
+ const spec = ruleKindSpec(kind);
96375
+ if (!spec.carriesMedia) return [];
96376
+ return spec.carriesSubjectCrop ? NC_MEDIA_IMAGE_MODES : NC_MEDIA_IMAGE_MODES.filter((mode) => mode !== "crop");
96377
+ }
96378
+ /**
96379
+ * The zone ids that FIRE a rule: its `zones` condition, or an occupancy rule's
96380
+ * own scoped zone. Empty when the trigger is not a zone — and then
96381
+ * `showTriggerZone` has nothing to draw and is refused on save.
96382
+ */
96383
+ function ruleTriggerZoneIds(conditions) {
96384
+ if (conditions.zones !== void 0) return conditions.zones.ids;
96385
+ const zoneId = conditions.occupancy?.zoneId;
96386
+ return zoneId !== void 0 ? [zoneId] : [];
96387
+ }
96388
+ /**
96389
+ * What the legacy triple MEANT, as a mode. `zoneCrop` forced the clean
96390
+ * keyFrame ladder and dropped `frame`, so it is `clean` whatever else was set;
96391
+ * `frame` decided over `attach` when present; `keyFrame` was the clean scene;
96392
+ * every other still (`best`, `best-matching`, the default) was the subject.
96393
+ */
96394
+ function legacyImageMode(media) {
96395
+ if (media.attach === "none") return "none";
96396
+ if (media.zoneCrop === true) return "clean";
96397
+ if (media.frame === "full") return "clean";
96398
+ if (media.frame === "boxed") return "scene-boxes";
96399
+ if (media.frame === "cropped") return "crop";
96400
+ if (media.attach === "keyFrame") return "clean";
96401
+ return "crop";
96402
+ }
96403
+ /** A mode the kind cannot honour, mapped to the nearest one it can. */
96404
+ function clampToKind(mode, kind) {
96405
+ const offered = imageModesForKind(kind);
96406
+ if (offered.length === 0 || offered.includes(mode)) return mode;
96407
+ return mode === "crop" ? "clean" : mode;
96408
+ }
96409
+ /**
96410
+ * The mode a rule's picture is rendered in. `image` when stored; else what the
96411
+ * legacy triple meant — and, for a SENSOR rule written before D897, `clean`
96412
+ * whatever it stored (even `attach: 'none'`), because its stored triple was
96413
+ * ignored and one plain snapshot always shipped (operator ruling, 2026-10-01):
96414
+ * the migration keeps what the rule actually DID. Clamped to what the kind can
96415
+ * honour.
96416
+ */
96417
+ function resolveNcMediaImage(media, kind) {
96418
+ if (media.image !== void 0) return clampToKind(media.image, kind);
96419
+ if (kind === "sensor") return "clean";
96420
+ return clampToKind(legacyImageMode(media), kind);
96421
+ }
96422
+ /**
96423
+ * The stored shape of a media policy: `image` set, the legacy triple gone.
96424
+ * `showTriggerZone` survives only where it can be drawn — a zone trigger and a
96425
+ * mode other than `crop`/`none`; a legacy `zoneCrop` on a zone rule becomes it.
96426
+ * Every other field (gif, clip, rolls, speed, profile, linked cameras) is
96427
+ * carried through untouched. Pure; never throws.
96428
+ */
96429
+ function normalizeNcMediaPolicy(media, rule) {
96430
+ const image = resolveNcMediaImage(media, ruleKindOf(rule));
96431
+ const { attach: _attach, frame: _frame, zoneCrop, showTriggerZone, image: _image, ...rest } = media;
96432
+ const drawable = (showTriggerZone === true || media.image === void 0 && zoneCrop === true) && image !== "crop" && image !== "none" && ruleTriggerZoneIds(rule.conditions).length > 0;
96433
+ return {
96434
+ ...rest,
96435
+ image,
96436
+ ...drawable ? { showTriggerZone: true } : {}
96437
+ };
96438
+ }
96439
+ /**
96440
+ * Why a media policy cannot be SAVED on this rule, or null. Server-side and
96441
+ * editor alike: `showTriggerZone` on a rule whose trigger is not a zone, and a
96442
+ * mode the rule's kind cannot honour.
96443
+ */
96444
+ function ncMediaPolicyError(media, rule) {
96445
+ const kind = ruleKindOf(rule);
96446
+ if (media.image !== void 0) {
96447
+ const offered = imageModesForKind(kind);
96448
+ if (offered.length > 0 && !offered.includes(media.image)) return `media.image '${media.image}' is not available for a ${kind} rule (offered: ${offered.join(", ")})`;
96449
+ }
96450
+ if (media.showTriggerZone === true && ruleTriggerZoneIds(rule.conditions).length === 0) return "media.showTriggerZone needs a rule triggered by a zone (a zones condition or an occupancy zone)";
96451
+ return null;
96452
+ }
96453
+ //#endregion
95925
96454
  //#region src/notification/schedule.ts
95926
96455
  var WEEKDAY_TO_DAY = {
95927
96456
  Sun: 0,
@@ -102797,6 +103326,43 @@ function bindAddonActions(api, addonId, catalog) {
102797
103326
  return out;
102798
103327
  }
102799
103328
  //#endregion
103329
+ //#region src/inference/coral-units.ts
103330
+ /** Bus order of the enumeration: USB first (the historical key), then PCIe. */
103331
+ var BUS_ORDER = ["usb", "pci"];
103332
+ function unitDevice(bus, index) {
103333
+ return index === 0 ? bus : `${bus}:${index}`;
103334
+ }
103335
+ function countOn(coral, bus) {
103336
+ if (coral.units !== void 0) return coral.units.filter((u) => u.bus === bus).reduce((n, u) => n + Math.max(0, u.count), 0);
103337
+ return bus === (coral.bus === "pci" ? "pci" : "usb") ? Math.max(1, coral.count ?? 1) : 0;
103338
+ }
103339
+ /** Every usable Coral unit on a node, USB first then PCIe, in ordinal order. */
103340
+ function coralUsableUnits(coral) {
103341
+ if (coral == null) return [];
103342
+ const out = [];
103343
+ for (const bus of BUS_ORDER) {
103344
+ const count = countOn(coral, bus);
103345
+ for (let index = 0; index < count; index++) out.push({
103346
+ bus,
103347
+ index,
103348
+ device: unitDevice(bus, index)
103349
+ });
103350
+ }
103351
+ return out;
103352
+ }
103353
+ var KEY_DEVICE = /^(usb|pci)(?::(\d+))?$/;
103354
+ /**
103355
+ * Map an edgetpu key suffix (`usb`, `usb:1`, `pci`, `pci:1`) to the explicit
103356
+ * libedgetpu device string (`usb:0`, `usb:1`, `pci:0`, `pci:1`). Throws on any
103357
+ * other string — a pool must never open "whichever Edge TPU comes first".
103358
+ */
103359
+ function edgetpuDelegateDevice(device) {
103360
+ const match = KEY_DEVICE.exec(device.trim());
103361
+ if (match === null) throw new Error(`edgetpu device "${device}" is not usb[:N] or pci[:N]`);
103362
+ const [, bus, ordinal] = match;
103363
+ return `${bus}:${ordinal ?? "0"}`;
103364
+ }
103365
+ //#endregion
102800
103366
  //#region src/inference/runtime-capabilities.ts
102801
103367
  var AUTO = {
102802
103368
  value: "auto",
@@ -102810,6 +103376,14 @@ var USB = {
102810
103376
  value: "usb",
102811
103377
  label: "Coral USB"
102812
103378
  };
103379
+ function coralUnitOption(unit) {
103380
+ const bus = unit.bus === "pci" ? "PCIe" : "USB";
103381
+ const label = unit.index === 0 ? `Coral ${bus}` : `Coral ${bus} ${unit.index + 1}`;
103382
+ return {
103383
+ value: unit.device,
103384
+ label
103385
+ };
103386
+ }
102813
103387
  var RUNTIMES = [
102814
103388
  {
102815
103389
  id: "onnx",
@@ -102876,8 +103450,11 @@ var RUNTIMES = [
102876
103450
  },
102877
103451
  {
102878
103452
  id: "edgetpu",
102879
- supports: (hw) => hw?.coral != null,
102880
- devices: () => [USB],
103453
+ supports: (hw) => coralUsableUnits(hw?.coral).length > 0,
103454
+ devices: (hw) => {
103455
+ const units = coralUsableUnits(hw?.coral);
103456
+ return units.length > 0 ? units.map(coralUnitOption) : [USB];
103457
+ },
102881
103458
  defaultDevice: "usb"
102882
103459
  }
102883
103460
  ];
@@ -102921,7 +103498,7 @@ function modelFormatForRuntime(id) {
102921
103498
  * - coreml: score 95, darwin+arm64
102922
103499
  * - cuda: score 85, nvidia gpu
102923
103500
  * - openvino: score 90 if intel-npu present, else 80 if intel gpu
102924
- * - edgetpu: score 70, coral usb present
103501
+ * - edgetpu: score 70, a usable Coral unit (USB or PCIe)
102925
103502
  * - cpu: score 50 (universal floor, always included)
102926
103503
  *
102927
103504
  * All hardware-matched entries are `available: true`.
@@ -102975,7 +103552,7 @@ function scoreRuntimes(hw) {
102975
103552
  available: true
102976
103553
  });
102977
103554
  }
102978
- if (hw.coral != null) scores.push({
103555
+ if (coralUsableUnits(hw.coral).length > 0) scores.push({
102979
103556
  runtime: "python",
102980
103557
  backend: "edgetpu",
102981
103558
  format: "tflite",
@@ -103034,11 +103611,7 @@ function enumerateInferenceDevices(hw) {
103034
103611
  continue;
103035
103612
  }
103036
103613
  if (s.backend === "edgetpu") {
103037
- const coralCount = Math.max(1, hw.coral?.count ?? 1);
103038
- for (let i = 0; i < coralCount; i++) {
103039
- const dev = i === 0 ? "usb" : `usb:${i}`;
103040
- out.push(device("edgetpu", dev, s.format, s.score));
103041
- }
103614
+ for (const unit of coralUsableUnits(hw.coral)) out.push(device("edgetpu", unit.device, s.format, s.score));
103042
103615
  continue;
103043
103616
  }
103044
103617
  if (s.backend === "onnx" && s.reason.startsWith("ONNX WebGPU")) {
@@ -103579,6 +104152,8 @@ exports.DEVICE_CHILDREN_BATCH_MAX = require_sleep.DEVICE_CHILDREN_BATCH_MAX;
103579
104152
  exports.DEVICE_DEFINITIVE_REFUSALS_BEFORE_DELETE = DEVICE_DEFINITIVE_REFUSALS_BEFORE_DELETE;
103580
104153
  exports.DEVICE_DO_ACTIONS = DEVICE_DO_ACTIONS;
103581
104154
  exports.DEVICE_ENROLLMENT_TTL_MS = DEVICE_ENROLLMENT_TTL_MS;
104155
+ exports.DEVICE_HISTORY_ENERGY_KEY = DEVICE_HISTORY_ENERGY_KEY;
104156
+ exports.DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS = DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS;
103582
104157
  exports.DEVICE_NONCE_TTL_MS = DEVICE_NONCE_TTL_MS;
103583
104158
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
103584
104159
  exports.DEVICE_RAMP = DEVICE_RAMP;
@@ -103637,6 +104212,7 @@ exports.DeviceExportStatusSchema = DeviceExportStatusSchema;
103637
104212
  exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
103638
104213
  exports.DeviceFeature = require_err_msg.DeviceFeature;
103639
104214
  exports.DeviceHistoryKindSchema = DeviceHistoryKindSchema;
104215
+ exports.DeviceHistoryProducerKindSchema = DeviceHistoryProducerKindSchema;
103640
104216
  exports.DeviceHistoryRefusalReasonSchema = DeviceHistoryRefusalReasonSchema;
103641
104217
  exports.DeviceHistoryResolutionSchema = DeviceHistoryResolutionSchema;
103642
104218
  exports.DeviceHistoryStatusSchema = DeviceHistoryStatusSchema;
@@ -103979,6 +104555,10 @@ exports.LogChannelWindowSchema = LogChannelWindowSchema;
103979
104555
  exports.LogChannelWindowStateSchema = LogChannelWindowStateSchema;
103980
104556
  exports.LogEntrySchema = LogEntrySchema;
103981
104557
  exports.LogLevelSchema = LogLevelSchema;
104558
+ exports.LogReadCapabilitiesSchema = LogReadCapabilitiesSchema;
104559
+ exports.LogReadEntrySchema = LogReadEntrySchema;
104560
+ exports.LogReadQuerySchema = LogReadQuerySchema;
104561
+ exports.LogReadResultSchema = LogReadResultSchema;
103982
104562
  exports.LogStreamEntrySchema = LogStreamEntrySchema;
103983
104563
  exports.LoggingEffectiveSchema = LoggingEffectiveSchema;
103984
104564
  exports.LoggingExplicitSchema = LoggingExplicitSchema;
@@ -104160,6 +104740,7 @@ exports.NC_GROUPING_MIN_SEC = NC_GROUPING_MIN_SEC;
104160
104740
  exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
104161
104741
  exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
104162
104742
  exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
104743
+ exports.NC_MEDIA_IMAGE_MODES = NC_MEDIA_IMAGE_MODES;
104163
104744
  exports.NC_OCCUPANCY_DEFAULTS = NC_OCCUPANCY_DEFAULTS;
104164
104745
  exports.NC_RULE_CLEARABLE_KEYS = NC_RULE_CLEARABLE_KEYS;
104165
104746
  exports.NC_RULE_EDITOR_SECTION_ORDER = NC_RULE_EDITOR_SECTION_ORDER;
@@ -104258,6 +104839,7 @@ exports.NcHookBindingsSchema = NcHookBindingsSchema;
104258
104839
  exports.NcLegacyRuleActionSequenceSchema = NcLegacyRuleActionSequenceSchema;
104259
104840
  exports.NcLinkedCameraModeSchema = NcLinkedCameraModeSchema;
104260
104841
  exports.NcMediaFrameSchema = NcMediaFrameSchema;
104842
+ exports.NcMediaImageSchema = NcMediaImageSchema;
104261
104843
  exports.NcMediaPolicySchema = NcMediaPolicySchema;
104262
104844
  exports.NcOccupancyConditionSchema = NcOccupancyConditionSchema;
104263
104845
  exports.NcPlateMatcherSchema = NcPlateMatcherSchema;
@@ -105032,6 +105614,8 @@ exports.ZoneScopeBreakdownSchema = ZoneScopeBreakdownSchema;
105032
105614
  exports.__resetLogChannelRegistryForTests = __resetLogChannelRegistryForTests;
105033
105615
  exports.acceleratorKindOf = acceleratorKindOf;
105034
105616
  exports.accessoriesCapability = accessoriesCapability;
105617
+ exports.accessoryChildrenComplete = accessoryChildrenComplete;
105618
+ exports.accessoryChildrenUnknown = accessoryChildrenUnknown;
105035
105619
  exports.accessoryStableId = accessoryStableId;
105036
105620
  exports.actionsForBoundCaps = actionsForBoundCaps;
105037
105621
  exports.actionsForDevice = actionsForDevice;
@@ -105205,6 +105789,7 @@ exports.containingParentIndex = containingParentIndex;
105205
105789
  exports.controlCapability = controlCapability;
105206
105790
  exports.convertPresetFeature = convertPresetFeature;
105207
105791
  exports.convertUnit = convertUnit;
105792
+ exports.coralUsableUnits = coralUsableUnits;
105208
105793
  exports.coreBlockAddonId = coreBlockAddonId;
105209
105794
  exports.coreBlockComposition = coreBlockComposition;
105210
105795
  exports.coreBlockIdFromAddonId = coreBlockIdFromAddonId;
@@ -105297,6 +105882,7 @@ exports.doActionSelectValue = doActionSelectValue;
105297
105882
  exports.doorbellCapability = doorbellCapability;
105298
105883
  exports.doorbellFeatureSources = doorbellFeatureSources;
105299
105884
  exports.droppedConditionsForKind = droppedConditionsForKind;
105885
+ exports.edgetpuDelegateDevice = edgetpuDelegateDevice;
105300
105886
  exports.effectiveStepSettings = effectiveStepSettings;
105301
105887
  exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
105302
105888
  exports.egressTransportFromRequest = egressTransportFromRequest;
@@ -105402,6 +105988,7 @@ exports.iconCategoryLabel = iconCategoryLabel;
105402
105988
  exports.iconEntry = iconEntry;
105403
105989
  exports.iconsInCategory = iconsInCategory;
105404
105990
  exports.imageCapability = imageCapability;
105991
+ exports.imageModesForKind = imageModesForKind;
105405
105992
  exports.imageSettingsCapability = imageSettingsCapability;
105406
105993
  exports.inferExpressionKinds = inferExpressionKinds;
105407
105994
  exports.inferModelProvider = inferModelProvider;
@@ -105494,6 +106081,7 @@ exports.knownSecretCount = require_sleep.knownSecretCount;
105494
106081
  exports.knownValues = knownValues;
105495
106082
  exports.lawnMowerControlCapability = lawnMowerControlCapability;
105496
106083
  exports.legacyAutoLinkIds = legacyAutoLinkIds;
106084
+ exports.legacyImageMode = legacyImageMode;
105497
106085
  exports.legacyModeOf = legacyModeOf;
105498
106086
  exports.lifecycleJobSchema = lifecycleJobSchema;
105499
106087
  exports.lifecycleJobScopeSchema = lifecycleJobScopeSchema;
@@ -105556,6 +106144,7 @@ exports.mqttBrokerCapability = mqttBrokerCapability;
105556
106144
  exports.nativeObjectDetectionCapability = nativeObjectDetectionCapability;
105557
106145
  exports.navigationCapability = navigationCapability;
105558
106146
  exports.ncAlarmSystemDefaultSettings = ncAlarmSystemDefaultSettings;
106147
+ exports.ncMediaPolicyError = ncMediaPolicyError;
105559
106148
  exports.networkAccessCapability = networkAccessCapability;
105560
106149
  exports.networkLinkCapability = networkLinkCapability;
105561
106150
  exports.networkQualityCapability = networkQualityCapability;
@@ -105567,6 +106156,7 @@ exports.nodesCapability = nodesCapability;
105567
106156
  exports.normalizeAddonInitResult = require_sleep.normalizeAddonInitResult;
105568
106157
  exports.normalizeAudioLabel = normalizeAudioLabel;
105569
106158
  exports.normalizeCoreBlockWrite = normalizeCoreBlockWrite;
106159
+ exports.normalizeNcMediaPolicy = normalizeNcMediaPolicy;
105570
106160
  exports.normalizeNodeId = require_sleep.normalizeNodeId;
105571
106161
  exports.normalizeOrigin = normalizeOrigin;
105572
106162
  exports.normalizeSecretOwner = require_sleep.normalizeSecretOwner;
@@ -105724,6 +106314,7 @@ exports.resolveModeDelays = resolveModeDelays;
105724
106314
  exports.resolveModeNotifyPolicy = resolveModeNotifyPolicy;
105725
106315
  exports.resolveModelBoundSetting = resolveModelBoundSetting;
105726
106316
  exports.resolveMutate = resolveMutate;
106317
+ exports.resolveNcMediaImage = resolveNcMediaImage;
105727
106318
  exports.resolveNotificationTapAddress = resolveNotificationTapAddress;
105728
106319
  exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
105729
106320
  exports.resolvePublicEndpoint = resolvePublicEndpoint;
@@ -105757,6 +106348,7 @@ exports.ruleMatchesSection = ruleMatchesSection;
105757
106348
  exports.ruleSection = ruleSection;
105758
106349
  exports.ruleSectionOf = ruleSectionOf;
105759
106350
  exports.ruleSeedForSection = ruleSeedForSection;
106351
+ exports.ruleTriggerZoneIds = ruleTriggerZoneIds;
105760
106352
  exports.runInferenceStep = runInferenceStep;
105761
106353
  exports.runtimeDevices = runtimeDevices;
105762
106354
  exports.runtimeStatePolicyFor = runtimeStatePolicyFor;