@camstack/addon-provider-homeassistant 1.2.146 → 1.2.148

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.
@@ -5370,7 +5370,7 @@ var ZodIssueCode = {
5370
5370
  var ZodFirstPartyTypeKind;
5371
5371
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5372
5372
  //#endregion
5373
- //#region ../types/dist/sleep-jkRQgtSc.mjs
5373
+ //#region ../types/dist/sleep-C-v96WpE.mjs
5374
5374
  /**
5375
5375
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5376
5376
  * window to float samples (D455).
@@ -6985,6 +6985,10 @@ function toNodeId(idOrRunnerId) {
6985
6985
  const head = idOrRunnerId.split("/")[0];
6986
6986
  return head === void 0 || head.length === 0 ? idOrRunnerId : head;
6987
6987
  }
6988
+ object({
6989
+ field: string().min(1),
6990
+ on: literal("advance")
6991
+ });
6988
6992
  /**
6989
6993
  * Output schema shared by the contribution + live methods.
6990
6994
  *
@@ -7468,7 +7472,45 @@ function errMsg(err) {
7468
7472
  return String(err);
7469
7473
  }
7470
7474
  //#endregion
7471
- //#region ../types/dist/composition-CPFIlFfw.mjs
7475
+ //#region ../types/dist/composition-BK-GTFYb.mjs
7476
+ /**
7477
+ * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7478
+ * valutatore puro del suo FRONTE.
7479
+ *
7480
+ * Viveva dentro il builtin virtual-doorbell
7481
+ * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
7482
+ * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
7483
+ * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
7484
+ * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
7485
+ * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
7486
+ * SPOSTA qui e il doorbell la ri-esporta.
7487
+ *
7488
+ * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
7489
+ * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
7490
+ * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
7491
+ * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
7492
+ */
7493
+ /**
7494
+ * Known binary / switch source caps → the boolean slice field whose
7495
+ * false→true rise counts as ACTIVE. Every entry is "fire on active".
7496
+ * Sensors whose "active" reading is not a plain boolean (presence's string
7497
+ * state, connectivity's connected flag) are deliberately excluded — a
7498
+ * reconnect is not a doorbell press, and it is not a recording either.
7499
+ */
7500
+ var SOURCE_CAP_ACTIVE_FIELD = {
7501
+ contact: "entryOpen",
7502
+ binary: "on",
7503
+ switch: "on",
7504
+ motion: "detected",
7505
+ flood: "flooded",
7506
+ gas: "detected",
7507
+ smoke: "detected",
7508
+ "carbon-monoxide": "detected",
7509
+ vibration: "detected",
7510
+ tamper: "tampered"
7511
+ };
7512
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
7513
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
7472
7514
  /**
7473
7515
  * Error types for the safe expression engine. Two distinct classes so callers
7474
7516
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
@@ -7666,6 +7708,20 @@ var table = {
7666
7708
  const present = args.flatMap((a, i) => a === null ? [] : [asFiniteNumber(a, "latest", i)]);
7667
7709
  return present.length === 0 ? null : finiteResult(Math.max(...present), "latest");
7668
7710
  }
7711
+ },
7712
+ rose: {
7713
+ minArgs: 2,
7714
+ maxArgs: 3,
7715
+ apply: () => {
7716
+ throw new ExpressionEvalError("rose: stateful evaluation context is required");
7717
+ }
7718
+ },
7719
+ count: {
7720
+ minArgs: 1,
7721
+ maxArgs: 1,
7722
+ apply: () => {
7723
+ throw new ExpressionEvalError("count: stateful evaluation context is required");
7724
+ }
7669
7725
  }
7670
7726
  };
7671
7727
  Object.freeze(Object.assign(Object.create(null), table));
@@ -8180,6 +8236,7 @@ var CompositionItemEntrySchema = object({
8180
8236
  var CompositionFeatureSchema = discriminatedUnion("kind", [object({
8181
8237
  kind: literal("fields"),
8182
8238
  cap: string().min(1),
8239
+ enabled: boolean().optional(),
8183
8240
  fields: record(string().min(1), CompositionFieldSourceSchema).refine((fields) => Object.keys(fields).length <= 32, { message: `at most 32 fields per capability` }),
8184
8241
  /**
8185
8242
  * Items of the cap's item array, keyed by item key (D663). Naming `items`
@@ -8191,6 +8248,7 @@ var CompositionFeatureSchema = discriminatedUnion("kind", [object({
8191
8248
  }), object({
8192
8249
  kind: literal("passthrough"),
8193
8250
  cap: string().min(1),
8251
+ enabled: boolean().optional(),
8194
8252
  source: CompositionSourceRefSchema
8195
8253
  })]);
8196
8254
  var CompositionSchema = object({
@@ -8220,7 +8278,6 @@ var CompositionSchema = object({
8220
8278
  });
8221
8279
  new Set([
8222
8280
  "track",
8223
- "summary",
8224
8281
  "face",
8225
8282
  "identity",
8226
8283
  "plate",
@@ -13138,6 +13195,15 @@ var cameraStreamsCapability = {
13138
13195
  */
13139
13196
  durability: "session"
13140
13197
  };
13198
+ EventCategory.DoorbellOnPressed;
13199
+ var CompositionEventPlanSchema = object({
13200
+ featureIndex: number().int().nonnegative(),
13201
+ cap: string(),
13202
+ eventName: string(),
13203
+ category: _enum(EventCategory),
13204
+ stateField: string(),
13205
+ fieldKey: string()
13206
+ });
13141
13207
  /** One refusal, named (D391). `path` points into the composition, e.g. `features[0].fields.celsius`. */
13142
13208
  var CompositionProblemSchema = object({
13143
13209
  code: _enum([
@@ -13187,12 +13253,14 @@ var CompositionUnavailableModeSchema = _enum([
13187
13253
  ]);
13188
13254
  /**
13189
13255
  * `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
13190
- * field of its cap changes value · `constant` reads nothing, computed once.
13256
+ * field of its cap changes value · `constant` reads nothing, computed once ·
13257
+ * `accumulator` advances from the target's seed/current output.
13191
13258
  */
13192
13259
  var CompositionFieldRoleSchema = _enum([
13193
13260
  "derived",
13194
13261
  "clock",
13195
- "constant"
13262
+ "constant",
13263
+ "accumulator"
13196
13264
  ]);
13197
13265
  /** The item a planned field belongs to; its `path` is then the dotted path INSIDE the item. */
13198
13266
  var CompositionFieldItemSchema = object({
@@ -13228,6 +13296,7 @@ var CompositionFeatureModeSchema = _enum([
13228
13296
  "replace"
13229
13297
  ]);
13230
13298
  var CompositionFeaturePlanSchema = object({
13299
+ /** Index in the stored feature array; disabled features are omitted rather than re-indexed. */
13231
13300
  featureIndex: number().int().nonnegative(),
13232
13301
  cap: string(),
13233
13302
  mode: CompositionFeatureModeSchema
@@ -13236,7 +13305,8 @@ var CompositionValidationSchema = object({
13236
13305
  ok: boolean(),
13237
13306
  problems: array(CompositionProblemSchema),
13238
13307
  fields: array(CompositionFieldPlanSchema),
13239
- features: array(CompositionFeaturePlanSchema)
13308
+ features: array(CompositionFeaturePlanSchema),
13309
+ events: array(CompositionEventPlanSchema)
13240
13310
  });
13241
13311
  /**
13242
13312
  * Mirrors `CoreBlockStatus`, pinned by `core-blocks-source.spec.ts`. `degraded`:
@@ -15253,6 +15323,34 @@ var DeviceBindingsForDeviceSchema = object({
15253
15323
  nativeAddonId: string()
15254
15324
  }))
15255
15325
  });
15326
+ /**
15327
+ * The STORED wrapper activation for one `(device, capability)` pair — the row
15328
+ * `setWrapperActive` writes, read back three-way (D675):
15329
+ *
15330
+ * - `value` — the store answered and holds an activation: the bound wrapper's
15331
+ * addon id, or `null`, the operator's explicit "off" marker (D62);
15332
+ * - `missing` — the store answered, its activation map exists, and it holds
15333
+ * nothing for this pair: the operator never activated or deactivated a
15334
+ * wrapper here;
15335
+ * - `failed` — the store could not be read, holds no activation map at all
15336
+ * (an empty or unready snapshot, D44), or holds something that is not an
15337
+ * activation. It carries NO information: a consumer must never read it as
15338
+ * `missing` (D315, D393).
15339
+ *
15340
+ * This is the persisted INPUT, not the derived `getBindings` view — no registry
15341
+ * resolution, no `defaultActive` auto-binding, no cluster native-cap folding.
15342
+ */
15343
+ var WrapperActivationReadSchema = discriminatedUnion("kind", [
15344
+ object({
15345
+ kind: literal("value"),
15346
+ wrapperAddonId: string().nullable()
15347
+ }),
15348
+ object({ kind: literal("missing") }),
15349
+ object({
15350
+ kind: literal("failed"),
15351
+ error: string()
15352
+ })
15353
+ ]);
15256
15354
  var SavedDeviceRowSchema = object({
15257
15355
  /** Numeric id reserved at allocateDeviceId time. */
15258
15356
  id: number(),
@@ -15603,7 +15701,10 @@ method(object({
15603
15701
  }), _void(), {
15604
15702
  kind: "mutation",
15605
15703
  auth: "admin"
15606
- }), method(object({ capName: string() }), array(string())), method(object({ deviceType: string() }), array(object({
15704
+ }), method(object({
15705
+ deviceId: number(),
15706
+ capName: string().min(1)
15707
+ }), WrapperActivationReadSchema), method(object({ capName: string() }), array(string())), method(object({ deviceType: string() }), array(object({
15607
15708
  capName: string(),
15608
15709
  wrappers: array(string())
15609
15710
  }))), method(object({ deviceId: number() }), SettingsSchemaWithValuesSchema.nullable()), method(object({ deviceId: number() }), SettingsSchemaWithValuesSchema.nullable()), method(object({ deviceId: number() }), object({
@@ -20782,6 +20883,102 @@ var oauthIntegrationCapability = {
20782
20883
  getDescriptor: method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" }) }
20783
20884
  };
20784
20885
  /**
20886
+ * What started a camera's onboard motion (D672). `camera` is the camera's own
20887
+ * firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
20888
+ * wire means the camera: only the camera-sensors bridge ever says `sensor`.
20889
+ */
20890
+ var MotionTriggeredBySchema = discriminatedUnion("kind", [object({ kind: literal("camera") }), object({
20891
+ kind: literal("sensor"),
20892
+ deviceId: number().int().positive()
20893
+ })]);
20894
+ Object.freeze({ kind: "camera" });
20895
+ var CameraSensorRefSchema = object({
20896
+ addonId: string().min(1),
20897
+ stableId: string().min(1)
20898
+ });
20899
+ function cameraSensorRefKey(ref) {
20900
+ return `${ref.addonId}/${ref.stableId}`;
20901
+ }
20902
+ /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20903
+ var CameraSensorsModeSchema = _enum(["in-addition"]);
20904
+ object({
20905
+ mode: CameraSensorsModeSchema,
20906
+ sensors: array(CameraSensorRefSchema).min(1).max(8)
20907
+ }).superRefine((value, ctx) => {
20908
+ const seen = /* @__PURE__ */ new Set();
20909
+ value.sensors.forEach((ref, index) => {
20910
+ const key = cameraSensorRefKey(ref);
20911
+ if (seen.has(key)) ctx.addIssue({
20912
+ code: "custom",
20913
+ path: ["sensors", index],
20914
+ message: `duplicate camera sensor ${key}`
20915
+ });
20916
+ seen.add(key);
20917
+ });
20918
+ });
20919
+ var RefFields = {
20920
+ addonId: string(),
20921
+ stableId: string()
20922
+ };
20923
+ var CameraSensorAttachmentSchema = discriminatedUnion("state", [
20924
+ object({
20925
+ ...RefFields,
20926
+ state: literal("ready"),
20927
+ deviceId: number(),
20928
+ name: string(),
20929
+ caps: array(string()).readonly()
20930
+ }),
20931
+ object({
20932
+ ...RefFields,
20933
+ state: literal("offline"),
20934
+ deviceId: number(),
20935
+ name: string(),
20936
+ reason: _enum(["offline", "disabled"])
20937
+ }),
20938
+ object({
20939
+ ...RefFields,
20940
+ state: literal("missing"),
20941
+ reason: string()
20942
+ }),
20943
+ object({
20944
+ ...RefFields,
20945
+ state: literal("unsupported"),
20946
+ deviceId: number(),
20947
+ name: string(),
20948
+ reason: string()
20949
+ }),
20950
+ object({
20951
+ ...RefFields,
20952
+ state: literal("unreadable"),
20953
+ reason: string()
20954
+ })
20955
+ ]);
20956
+ /**
20957
+ * Why a configured camera forwards NOTHING from its sensors, however ready they
20958
+ * are (D672): it does not list `onboard` in `motionSources` (attaching a sensor
20959
+ * does not add it), or it has no active detection config at all (the bridge
20960
+ * reads the active config). Absent = forwarded, or an older node that does not
20961
+ * say.
20962
+ */
20963
+ var CameraSensorsNotForwardedSchema = _enum(["onboard-not-listed", "no-active-detection"]);
20964
+ var CameraSensorsStatusSchema = discriminatedUnion("state", [
20965
+ object({ state: literal("not-configured") }),
20966
+ object({
20967
+ state: literal("unreadable"),
20968
+ reason: string()
20969
+ }),
20970
+ object({
20971
+ state: literal("configured"),
20972
+ mode: CameraSensorsModeSchema,
20973
+ sensors: array(CameraSensorAttachmentSchema).readonly(),
20974
+ notForwarded: CameraSensorsNotForwardedSchema.optional()
20975
+ })
20976
+ ]);
20977
+ Object.freeze({
20978
+ state: "unreadable",
20979
+ reason: "camera sensor mappings not loaded"
20980
+ });
20981
+ /**
20785
20982
  * A model choice has a SCOPE, and a scope change has a workflow.
20786
20983
  *
20787
20984
  * ## The two scopes
@@ -21530,6 +21727,12 @@ var MotionEventSchema = object({
21530
21727
  /** Populated by B5 (recording playback URL for this event). */
21531
21728
  mediaUrl: string().optional(),
21532
21729
  /**
21730
+ * The FIRST trigger of an `onboard` episode, written once at open (D672).
21731
+ * Absent = not recorded (a row written before D672, or an analyzer /
21732
+ * device-activity episode) — never read as "the camera" (D393).
21733
+ */
21734
+ triggeredBy: MotionTriggeredBySchema.optional(),
21735
+ /**
21533
21736
  * One row per motion EPISODE, not one per push (D475). `null` while the
21534
21737
  * episode is still open — a further rising edge extends it in place rather
21535
21738
  * than inserting a new row. Set once, at close, to `lastOnAt - startedAt`
@@ -21688,6 +21891,21 @@ var ObjectEventSchema = object({
21688
21891
  keyFrameMediaId: number().int().optional(),
21689
21892
  /** Populated by B5 (recording playback URL for this event). */
21690
21893
  mediaUrl: string().optional(),
21894
+ /**
21895
+ * When the change this row records HAPPENED, epoch ms, when that is earlier
21896
+ * than {@link timestamp} and the producer could date it (D684).
21897
+ *
21898
+ * Set on a package PICK-UP row only: its `timestamp` is when the departure
21899
+ * was CONCLUDED — five minutes of unseen observed time after the parcel went
21900
+ * — and a notification's footage has to be cut around the pick-up, not the
21901
+ * empty doormat after it (D682). Stored so every path that rebuilds a
21902
+ * notification subject from the row (the boot reconcile, a dry-run) anchors
21903
+ * on the same instant the live hook did.
21904
+ *
21905
+ * `timestamp` keeps its meaning. Absent on every other row and on every
21906
+ * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
21907
+ */
21908
+ observedAt: number().optional(),
21691
21909
  /** The parent track's key-event importance [0,1], propagated to every object
21692
21910
  * event of the track (so an event row can be sorted by importance without a
21693
21911
  * track join). Absent on legacy rows / before the track was scored. */
@@ -22023,131 +22241,6 @@ var RecentTracksQueryInput = object({
22023
22241
  /** See {@link ExcludeSourcesDoc}. */
22024
22242
  excludeSources: array(TrackSourceSchema).optional()
22025
22243
  });
22026
- /**
22027
- * A Summary (D359): the per-camera session envelope that references tracks.
22028
- * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
22029
- * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
22030
- * first frame with a member visible has been materialised (D482 — it was the
22031
- * row's composite key, which embedded the owner and could name another table's
22032
- * row once event ids became per-table rowids).
22033
- */
22034
- var SummarySchema = object({
22035
- id: string(),
22036
- deviceId: number(),
22037
- openedAt: number(),
22038
- lastActiveAt: number(),
22039
- sealedAt: number().nullable(),
22040
- memberCount: number().int().nonnegative(),
22041
- groupShotMediaId: number().int().nullable()
22042
- });
22043
- var SummariesQueryInput = object({
22044
- deviceIds: array(number()).min(1),
22045
- since: number().optional(),
22046
- until: number().optional(),
22047
- limit: number().int().min(1).max(200).default(50),
22048
- /** The same member-wise filter {@link listRecentSummaries} takes — one
22049
- * meaning of "dog" on every summary surface. */
22050
- classes: array(string().min(1).max(60)).max(40).optional()
22051
- });
22052
- var SummaryDetailSchema = object({
22053
- summary: SummarySchema,
22054
- /** Members in absorption order, oldest join first. */
22055
- members: array(object({
22056
- trackId: string(),
22057
- /**
22058
- * The track's class at the time it JOINED, or `null` when it was unresolved
22059
- * and the track no longer exists to fill it.
22060
- *
22061
- * The join, not the latest opinion: class voting can re-decide a track's
22062
- * class afterwards, and a group's composition is the history of who came
22063
- * together. A consumer counts these to badge a thumbnail — "two people and a
22064
- * dog" — rather than being handed a second, denormalised tally that would
22065
- * drift from the members it claims to describe.
22066
- */
22067
- className: string().nullable(),
22068
- /** `primary` is the one member that carries the group's identity. */
22069
- role: _enum([
22070
- "primary",
22071
- "sibling",
22072
- "child"
22073
- ]).nullable(),
22074
- joinedAt: number(),
22075
- /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
22076
- * at read time. Absent when nothing resolved, never an empty string. */
22077
- label: string().optional(),
22078
- /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
22079
- * recognised nobody, and absent for a member whose track has been pruned —
22080
- * a thumbnail that names the wrong person is worse than one naming nobody. */
22081
- subLabel: string().optional()
22082
- })).readonly()
22083
- });
22084
- /**
22085
- * What a LIST of groups carries: the envelope plus what it is MADE OF.
22086
- *
22087
- * Coarse on purpose — the operator's rule is "only the rough data on the list,
22088
- * we extend when we notice we need more". Enough to draw the card ("two people
22089
- * and a dog") and no more. The MEMBERS, their names and the per-member links
22090
- * are on {@link getSummary}: shipping every member of every group so a client
22091
- * can count them turns a page of two hundred groups into a thousand records to
22092
- * draw two hundred badges.
22093
- */
22094
- var SummaryListItemSchema = object({
22095
- summary: SummarySchema,
22096
- /** Class counts, most numerous first; `className: null` is a member whose
22097
- * class never resolved, counted so the counts add up to `memberCount`. */
22098
- classes: array(object({
22099
- className: string().nullable(),
22100
- count: number().int().positive()
22101
- })).readonly()
22102
- });
22103
- var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
22104
- /**
22105
- * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
22106
- *
22107
- * The reel on the cameras page and the Events gallery both ride a cursor, so a
22108
- * summaries feed that could only answer "the last N for these cameras"
22109
- * (`listSummaries`) would make groups available in the timeline and nowhere
22110
- * else. Two feeds with different ordering contracts would be two ideas of what
22111
- * "newest" means, on one screen.
22112
- */
22113
- var RecentSummariesQueryInput = object({
22114
- /** Devices to merge. An empty array yields an empty page. */
22115
- deviceIds: array(number()),
22116
- /** Window lower bound on `lastActiveAt` (inclusive). */
22117
- since: number().optional(),
22118
- /** Window upper bound on `lastActiveAt` (inclusive). */
22119
- until: number().optional(),
22120
- limit: number().int().min(1).max(500).default(100),
22121
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
22122
- cursor: string().optional(),
22123
- /**
22124
- * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
22125
- * deliberately the same name: the events filter tree emits one list of ticked
22126
- * taxonomy nodes, and a summaries feed that called it something else would
22127
- * make the operator's one filter component produce two shapes.
22128
- *
22129
- * A question about the MEMBERS: a group matches when ANY of them does. "Show
22130
- * me the arrivals with a dog" has to return the group of four people who had
22131
- * one with them; a filter on the group's own fields could only ever answer
22132
- * about its primary.
22133
- *
22134
- * Matched against all THREE tiers of a member — the class (`animal`), the
22135
- * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
22136
- * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
22137
- * an event kind in the tree and a tier-1 label on an `animal` track; a filter
22138
- * that read only the class would answer "animal" and never "dog".
22139
- *
22140
- * ABSENT or EMPTY means no filter — absent is not a selection of none.
22141
- */
22142
- classes: array(string().min(1).max(60)).max(40).optional()
22143
- });
22144
- var RecentSummariesPageSchema = object({
22145
- /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
22146
- * {@link listSummaries} — one list shape, so a card renders the same
22147
- * whichever feed drew it. */
22148
- summaries: array(SummaryListItemSchema).readonly(),
22149
- nextCursor: string().nullable()
22150
- });
22151
22244
  var RecentTracksPageSchema = object({
22152
22245
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
22153
22246
  tracks: array(TrackSchema).readonly(),
@@ -22659,7 +22752,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
22659
22752
  classes: array(string()).optional(),
22660
22753
  /** See {@link ExcludeSourcesDoc}. */
22661
22754
  excludeSources: array(TrackSourceSchema).optional()
22662
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
22755
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
22663
22756
  kind: "mutation",
22664
22757
  auth: "admin"
22665
22758
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -23879,6 +23972,23 @@ var MotionSourceEnum = _enum([
23879
23972
  */
23880
23973
  var MotionSourcesSchema = array(MotionSourceEnum);
23881
23974
  /**
23975
+ * The operator-facing name of each MOTION source (D672: `onboard` = the
23976
+ * camera's sensors — its own motion plus any sensor attached to it). The wire
23977
+ * value stays `onboard`; only the label changed. `detectionSources: 'onboard'`
23978
+ * ("Camera onboard AI") and the notification source filter keep their labels.
23979
+ */
23980
+ var MOTION_SOURCE_LABELS = {
23981
+ analyzer: "Frame-diff Analyzer (motion addon)",
23982
+ onboard: "Camera sensors",
23983
+ "device-activity": "Device activity (the device says it is working)"
23984
+ };
23985
+ /** The order the motion-source picker offers them in (not the enum's order). */
23986
+ var MOTION_SOURCE_PICKER_ORDER = [
23987
+ "analyzer",
23988
+ "onboard",
23989
+ "device-activity"
23990
+ ];
23991
+ /**
23882
23992
  * Which root detector a camera runs — EXACTLY ONE.
23883
23993
  *
23884
23994
  * Deliberately the SAME vocabulary post-analysis already tags every detection
@@ -24225,7 +24335,14 @@ var RunnerCameraConfigSchema = object({
24225
24335
  */
24226
24336
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
24227
24337
  });
24228
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
24338
+ MOTION_SOURCE_PICKER_ORDER.map((value) => value === "onboard" ? {
24339
+ value,
24340
+ label: MOTION_SOURCE_LABELS[value],
24341
+ description: "The camera's own motion, plus any sensor attached to it"
24342
+ } : {
24343
+ value,
24344
+ label: MOTION_SOURCE_LABELS[value]
24345
+ }), motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
24229
24346
  /**
24230
24347
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
24231
24348
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -24799,6 +24916,11 @@ var CameraStatusSchema = object({
24799
24916
  */
24800
24917
  switchedOff: array(CameraSwitchIdSchema).readonly(),
24801
24918
  /**
24919
+ * The camera's attached sensors (D672). Optional only for a status composed
24920
+ * by an orchestrator older than D672; this orchestrator always sets it.
24921
+ */
24922
+ cameraSensors: CameraSensorsStatusSchema.optional(),
24923
+ /**
24802
24924
  * Stages of the bounded fan-out that were CUT SHORT — a timeout or a
24803
24925
  * rejection — and whose block is therefore `null` because we could not
24804
24926
  * READ it, not because there is nothing there.
@@ -27251,44 +27373,6 @@ DeviceType.Camera, method(object({
27251
27373
  auth: "protected"
27252
27374
  });
27253
27375
  /**
27254
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
27255
- * valutatore puro del suo FRONTE.
27256
- *
27257
- * Viveva dentro il builtin virtual-doorbell
27258
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
27259
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
27260
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
27261
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
27262
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
27263
- * SPOSTA qui e il doorbell la ri-esporta.
27264
- *
27265
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
27266
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
27267
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
27268
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
27269
- */
27270
- /**
27271
- * Known binary / switch source caps → the boolean slice field whose
27272
- * false→true rise counts as ACTIVE. Every entry is "fire on active".
27273
- * Sensors whose "active" reading is not a plain boolean (presence's string
27274
- * state, connectivity's connected flag) are deliberately excluded — a
27275
- * reconnect is not a doorbell press, and it is not a recording either.
27276
- */
27277
- var SOURCE_CAP_ACTIVE_FIELD = {
27278
- contact: "entryOpen",
27279
- binary: "on",
27280
- switch: "on",
27281
- motion: "detected",
27282
- flood: "flooded",
27283
- gas: "detected",
27284
- smoke: "detected",
27285
- "carbon-monoxide": "detected",
27286
- vibration: "detected",
27287
- tamper: "tampered"
27288
- };
27289
- /** Cap names whose presence in a device's bindings qualify it as a source. */
27290
- var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
27291
- /**
27292
27376
  * Doorbell button cap. Two kinds of providers coexist behind this cap
27293
27377
  * name (same pattern as `snapshot`):
27294
27378
  *
@@ -27361,7 +27445,13 @@ var doorbellCapability = {
27361
27445
  * push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
27362
27446
  * release/duration — it's a pulse.
27363
27447
  */
27364
- onPressed: { data: DoorbellPressEventSchema } },
27448
+ onPressed: {
27449
+ data: DoorbellPressEventSchema,
27450
+ fromState: {
27451
+ field: "lastPressedAt",
27452
+ on: "advance"
27453
+ }
27454
+ } },
27365
27455
  status: {
27366
27456
  schema: DoorbellStatusSchema,
27367
27457
  kind: "push"
@@ -32258,7 +32348,9 @@ var MotionOnMotionChangedDataSchema = object({
32258
32348
  detected: boolean(),
32259
32349
  timestamp: number(),
32260
32350
  source: MotionSourceEnum,
32261
- regions: array(MotionRegionSchema).readonly().optional()
32351
+ regions: array(MotionRegionSchema).readonly().optional(),
32352
+ /** Who started it (D672). Absent = the camera itself. Only `onboard` carries `sensor`. */
32353
+ triggeredBy: MotionTriggeredBySchema.optional()
32262
32354
  });
32263
32355
  var motionCapability = {
32264
32356
  name: "motion",
@@ -35248,12 +35340,21 @@ var RecordingStorageUsageSchema = object({
35248
35340
  */
35249
35341
  devicesRead: _enum(["archive", "unavailable"]).optional(),
35250
35342
  /**
35343
+ * Is the `archive` answer WHOLE (D674): the hour ledger loaded without
35344
+ * hitting its row cap, and at least one location resolved? Only then may a
35345
+ * camera ABSENT from `devices[]` be read as "no footage" — and only then is a
35346
+ * row's `oldestMs` the archive's oldest rather than the oldest of a fraction.
35347
+ * OPTIONAL: a recorder predating D674 omits it, and a consumer must read the
35348
+ * absence as `false`. A retention floor is a delete; it never guesses.
35349
+ */
35350
+ complete: boolean().optional(),
35351
+ /**
35251
35352
  * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
35252
- * of the payload without it: the hub serves this method through a BAKED
35253
- * router, so a hub whose framework train predates a change to this array
35254
- * strips it out of an answer a newer recorder happily produced. A UI that
35255
- * dereferences it unconditionally crashes the whole Storage page over a
35256
- * missing disk bar.
35353
+ * of the payload without it: a recorder that could not build it omits it. A
35354
+ * UI that dereferences it unconditionally crashes the whole Storage page over
35355
+ * a missing disk bar. (The hub's cap routers pass output through unvalidated
35356
+ * since 2026-07-11 — `cap-router-builder.ts` — so a newer recorder's fields
35357
+ * are not stripped by an older hub.)
35257
35358
  */
35258
35359
  locations: array(RecordingLocationUsageSchema).optional()
35259
35360
  });
@@ -38772,7 +38873,12 @@ Object.freeze({
38772
38873
  number: {
38773
38874
  kind: "fixed",
38774
38875
  result: ["number", "null"]
38775
- }
38876
+ },
38877
+ rose: {
38878
+ kind: "fixed",
38879
+ result: ["number", "null"]
38880
+ },
38881
+ count: NUMBER
38776
38882
  });
38777
38883
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
38778
38884
  /**
@@ -39813,6 +39919,7 @@ var BaseDeviceProvider = class extends BaseAddon {
39813
39919
  }];
39814
39920
  }
39815
39921
  async onShutdown() {
39922
+ this._restoreShuttingDown = true;
39816
39923
  this.cancelRestoreRetries();
39817
39924
  const devices = await this.ctx.kernel.devices?.getAll() ?? [];
39818
39925
  for (const device of devices) try {
@@ -39930,6 +40037,7 @@ var BaseDeviceProvider = class extends BaseAddon {
39930
40037
  };
39931
40038
  }
39932
40039
  async restoreDevices(savedDevices) {
40040
+ this._restoreShuttingDown = false;
39933
40041
  const report = await this.onRestoreDevices(savedDevices);
39934
40042
  if (savedDevices.length === 0) return;
39935
40043
  if (report && report.failedCount > 0) {
@@ -39944,8 +40052,13 @@ var BaseDeviceProvider = class extends BaseAddon {
39944
40052
  /** Retry lane width. See `device-restore-retry.ts` for why retries
39945
40053
  * never re-stampede full-width while the initial pass does (D167). */
39946
40054
  restoreRetryConcurrency = 4;
39947
- _restoreRetryScheduler = null;
40055
+ /** Every bounded retry this process started — the default pass's, and each
40056
+ * `restoreWithBoundedRetry` of a provider's own rows. Cancelled together. */
40057
+ _restoreRetrySchedulers = [];
39948
40058
  _restoreRetryCompletion = null;
40059
+ /** Set by `onShutdown` BEFORE the retries are cancelled: a restore pass still
40060
+ * in flight must not start a scheduler after the cancel ran. */
40061
+ _restoreShuttingDown = false;
39949
40062
  _permanentRestoreFailures = /* @__PURE__ */ new Map();
39950
40063
  /** Settles when the background retry rounds finish (or `null` when
39951
40064
  * nothing failed). Exposed for tests and subclass diagnostics —
@@ -40014,8 +40127,8 @@ var BaseDeviceProvider = class extends BaseAddon {
40014
40127
  });
40015
40128
  }
40016
40129
  cancelRestoreRetries() {
40017
- this._restoreRetryScheduler?.cancel();
40018
- this._restoreRetryScheduler = null;
40130
+ for (const scheduler of this._restoreRetrySchedulers) scheduler.cancel();
40131
+ this._restoreRetrySchedulers = [];
40019
40132
  }
40020
40133
  recordPermanentRestoreFailure(failure) {
40021
40134
  this._permanentRestoreFailures.set(failure.deviceId, failure);
@@ -40032,6 +40145,13 @@ var BaseDeviceProvider = class extends BaseAddon {
40032
40145
  });
40033
40146
  }
40034
40147
  scheduleRestoreRetries(failures, attempt) {
40148
+ if (this._restoreShuttingDown) {
40149
+ this.ctx.logger.info("Restore retries not scheduled — the provider is shutting down", { meta: {
40150
+ rows: failures.length,
40151
+ deviceIds: failures.map((f) => f.saved.id)
40152
+ } });
40153
+ return;
40154
+ }
40035
40155
  const scheduler = new DeviceRestoreRetryScheduler({
40036
40156
  logger: this.ctx.logger,
40037
40157
  delaysMs: this.restoreRetryDelaysMs,
@@ -40039,10 +40159,54 @@ var BaseDeviceProvider = class extends BaseAddon {
40039
40159
  attempt,
40040
40160
  onPermanentFailure: (failure) => this.recordPermanentRestoreFailure(failure)
40041
40161
  });
40042
- this._restoreRetryScheduler = scheduler;
40043
- this._restoreRetryCompletion = scheduler.run(failures).then(() => void 0).catch((err) => {
40162
+ this._restoreRetrySchedulers = [...this._restoreRetrySchedulers, scheduler];
40163
+ const run = scheduler.run(failures).then(() => void 0).catch((err) => {
40044
40164
  this.ctx.logger.error("Restore retry scheduler crashed", { meta: { error: err instanceof Error ? err.message : String(err) } });
40045
40165
  });
40166
+ const prior = this._restoreRetryCompletion;
40167
+ this._restoreRetryCompletion = prior === null ? run : Promise.all([prior, run]).then(() => void 0);
40168
+ }
40169
+ /**
40170
+ * Restore rows the default pass does not handle (a provider's own restore
40171
+ * loop) under the SAME bounded retry (D347): one attempt each, in order; a
40172
+ * row that throws is logged with `tags.deviceId` and retried in the
40173
+ * background rounds, then marked permanently failed like any other.
40174
+ *
40175
+ * A restore is a device build, and a build whose runtime-state load did not
40176
+ * answer is REFUSED rather than started on cold defaults (D677) — so a
40177
+ * transient hub read must cost a retry, never the device until the next
40178
+ * runner restart. An ANSWERED `{}` is the device's true state and never
40179
+ * refuses.
40180
+ */
40181
+ async restoreWithBoundedRetry(rows, attempt) {
40182
+ const failures = [];
40183
+ for (const saved of rows) {
40184
+ if (this.isUnrestorableShell(saved)) {
40185
+ this.quarantineShell(saved);
40186
+ continue;
40187
+ }
40188
+ try {
40189
+ await attempt(saved);
40190
+ } catch (err) {
40191
+ const error = err instanceof Error ? err.message : String(err);
40192
+ this.ctx.logger.warn("Failed to restore device — bounded retry scheduled", {
40193
+ tags: {
40194
+ deviceId: saved.id,
40195
+ stableId: saved.stableId
40196
+ },
40197
+ meta: {
40198
+ type: saved.type,
40199
+ attempt: 1,
40200
+ error
40201
+ }
40202
+ });
40203
+ failures.push({
40204
+ saved,
40205
+ error
40206
+ });
40207
+ }
40208
+ }
40209
+ if (failures.length > 0) this.scheduleRestoreRetries(failures, attempt);
40046
40210
  }
40047
40211
  /**
40048
40212
  * Tear down and reconstruct ONE device from its persisted rows — the
@@ -43352,6 +43516,12 @@ Object.freeze({
43352
43516
  addonId: null,
43353
43517
  access: "view"
43354
43518
  },
43519
+ "deviceManager.getWrapperActivation": {
43520
+ capName: "device-manager",
43521
+ capScope: "system",
43522
+ addonId: null,
43523
+ access: "view"
43524
+ },
43355
43525
  "deviceManager.listAll": {
43356
43526
  capName: "device-manager",
43357
43527
  capScope: "system",
@@ -45626,12 +45796,6 @@ Object.freeze({
45626
45796
  addonId: null,
45627
45797
  access: "view"
45628
45798
  },
45629
- "pipelineAnalytics.getSummary": {
45630
- capName: "pipeline-analytics",
45631
- capScope: "device",
45632
- addonId: null,
45633
- access: "view"
45634
- },
45635
45799
  "pipelineAnalytics.getTrack": {
45636
45800
  capName: "pipeline-analytics",
45637
45801
  capScope: "device",
@@ -45692,12 +45856,6 @@ Object.freeze({
45692
45856
  addonId: null,
45693
45857
  access: "view"
45694
45858
  },
45695
- "pipelineAnalytics.listRecentSummaries": {
45696
- capName: "pipeline-analytics",
45697
- capScope: "device",
45698
- addonId: null,
45699
- access: "view"
45700
- },
45701
45859
  "pipelineAnalytics.listRecentTracks": {
45702
45860
  capName: "pipeline-analytics",
45703
45861
  capScope: "device",
@@ -45728,12 +45886,6 @@ Object.freeze({
45728
45886
  addonId: null,
45729
45887
  access: "view"
45730
45888
  },
45731
- "pipelineAnalytics.listSummaries": {
45732
- capName: "pipeline-analytics",
45733
- capScope: "device",
45734
- addonId: null,
45735
- access: "view"
45736
- },
45737
45889
  "pipelineAnalytics.listTrackMedia": {
45738
45890
  capName: "pipeline-analytics",
45739
45891
  capScope: "device",
@@ -49161,6 +49313,11 @@ Object.freeze({
49161
49313
  form: "single",
49162
49314
  optional: false
49163
49315
  }],
49316
+ "deviceManager.getWrapperActivation": [{
49317
+ name: "deviceId",
49318
+ form: "single",
49319
+ optional: false
49320
+ }],
49164
49321
  "deviceManager.listAll": [{
49165
49322
  name: "deviceIds",
49166
49323
  form: "array",
@@ -49898,11 +50055,6 @@ Object.freeze({
49898
50055
  form: "single",
49899
50056
  optional: true
49900
50057
  }],
49901
- "pipelineAnalytics.listRecentSummaries": [{
49902
- name: "deviceIds",
49903
- form: "array",
49904
- optional: false
49905
- }],
49906
50058
  "pipelineAnalytics.listRecentTracks": [{
49907
50059
  name: "deviceIds",
49908
50060
  form: "array",
@@ -49913,11 +50065,6 @@ Object.freeze({
49913
50065
  form: "array",
49914
50066
  optional: true
49915
50067
  }],
49916
- "pipelineAnalytics.listSummaries": [{
49917
- name: "deviceIds",
49918
- form: "array",
49919
- optional: false
49920
- }],
49921
50068
  "pipelineAnalytics.listTrackMedia": [{
49922
50069
  name: "deviceId",
49923
50070
  form: "single",